課題17: SIMD基礎

マンダトリー要件 (80点)

Part 1: 基本的なベクトル演算 (20点)

ファイル名: part1_vector_ops.zig

const std = @import("std");

// TODO: 以下の関数を実装してください

// 1. 2つのベクトルの要素ごとの加算
pub fn vectorAdd(a: @Vector(4, f32), b: @Vector(4, f32)) @Vector(4, f32) {
    // ここに実装
}

// 2. ベクトルのスカラー倍
pub fn vectorScale(v: @Vector(4, f32), scalar: f32) @Vector(4, f32) {
    // ここに実装
}

// 3. ベクトルの内積
pub fn dotProduct(a: @Vector(4, f32), b: @Vector(4, f32)) f32 {
    // ここに実装
}

// 4. ベクトルのノルム(長さ)
pub fn vectorNorm(v: @Vector(4, f32)) f32 {
    // ここに実装
    // sqrt(x^2 + y^2 + z^2 + w^2)
}

// 5. ベクトルの正規化
pub fn vectorNormalize(v: @Vector(4, f32)) @Vector(4, f32) {
    // ここに実装
}

pub fn main() void {
    const a: @Vector(4, f32) = .{ 1.0, 2.0, 3.0, 4.0 };
    const b: @Vector(4, f32) = .{ 5.0, 6.0, 7.0, 8.0 };

    const sum = vectorAdd(a, b);
    std.debug.print("a + b = {any}\n", .{sum});

    const scaled = vectorScale(a, 2.0);
    std.debug.print("a * 2 = {any}\n", .{scaled});

    const dot = dotProduct(a, b);
    std.debug.print("a · b = {d:.2}\n", .{dot});

    const norm = vectorNorm(a);
    std.debug.print("||a|| = {d:.2}\n", .{norm});

    const normalized = vectorNormalize(a);
    std.debug.print("normalize(a) = {any}\n", .{normalized});
}

Part 2: 配列演算のSIMD化 (20点)

ファイル名: part2_array_ops.zig

const std = @import("std");

// TODO: 以下の関数をSIMDで実装してください

// 1. 配列の要素ごとの加算(SIMD版)
pub fn arrayAddSIMD(a: []const f32, b: []const f32, result: []f32) void {
    // ここに実装
    // 4要素ずつ処理、残りはスカラー処理
}

// 2. 配列のスカラー倍(SIMD版)
pub fn arrayScaleSIMD(a: []const f32, scalar: f32, result: []f32) void {
    // ここに実装
}

// 3. 配列の内積(SIMD版)
pub fn arrayDotProductSIMD(a: []const f32, b: []const f32) f32 {
    // ここに実装
}

// 4. 配列の合計(SIMD版)
pub fn arraySumSIMD(a: []const f32) f32 {
    // ここに実装
}

// 5. 配列の最大値(SIMD版)
pub fn arrayMaxSIMD(a: []const f32) f32 {
    // ここに実装
}

pub fn main() !void {
    const allocator = std.heap.page_allocator;

    const size = 1000;
    const a = try allocator.alloc(f32, size);
    defer allocator.free(a);

    const b = try allocator.alloc(f32, size);
    defer allocator.free(b);

    const result = try allocator.alloc(f32, size);
    defer allocator.free(result);

    // 初期化
    for (a, 0..) |*v, i| v.* = @floatFromInt(i);
    for (b, 0..) |*v, i| v.* = @floatFromInt(i * 2);

    arrayAddSIMD(a, b, result);
    std.debug.print("First 5 elements: {any}\n", .{result[0..5]});

    const dot = arrayDotProductSIMD(a, b);
    std.debug.print("Dot product: {d:.2}\n", .{dot});

    const sum = arraySumSIMD(a);
    std.debug.print("Sum: {d:.2}\n", .{sum});
}

Part 3: 画像処理 (20点)

ファイル名: part3_image_processing.zig

const std = @import("std");

pub const Image = struct {
    width: usize,
    height: usize,
    pixels: []u8, // RGBA

    // TODO: 以下のメソッドをSIMDで実装してください

    // 1. 明るさ調整
    pub fn adjustBrightness(self: *Image, factor: f32) void {
        // ここに実装
    }

    // 2. グレースケール変換
    pub fn toGrayscale(self: *Image) void {
        // ここに実装
        // R * 0.299 + G * 0.587 + B * 0.114
    }

    // 3. 反転
    pub fn invert(self: *Image) void {
        // ここに実装
        // 各ピクセル = 255 - 元の値
    }

    // 4. しきい値処理
    pub fn threshold(self: *Image, value: u8) void {
        // ここに実装
        // value より大きければ255、そうでなければ0
    }
};

pub fn main() !void {
    const allocator = std.heap.page_allocator;

    const width = 1920;
    const height = 1080;
    const pixels = try allocator.alloc(u8, width * height * 4);
    defer allocator.free(pixels);

    // 初期化
    for (pixels, 0..) |*p, i| {
        p.* = @intCast(i % 256);
    }

    var image = Image{
        .width = width,
        .height = height,
        .pixels = pixels,
    };

    const start = std.time.microTimestamp();
    image.adjustBrightness(1.5);
    const end = std.time.microTimestamp();

    std.debug.print("Brightness adjustment: {} μs\n", .{end - start});
}

Part 4: 行列演算 (20点)

ファイル名: part4_matrix.zig

const std = @import("std");

// TODO: 以下の関数をSIMDで実装してください

// 1. 行列とベクトルの乗算
pub fn matrixVectorMultiplySIMD(
    matrix: []const f32,
    vector: []const f32,
    result: []f32,
    rows: usize,
    cols: usize,
) void {
    // ここに実装
}

// 2. 行列の転置
pub fn matrixTransposeSIMD(
    matrix: []const f32,
    result: []f32,
    rows: usize,
    cols: usize,
) void {
    // ここに実装
}

// 3. 行列の加算
pub fn matrixAddSIMD(
    a: []const f32,
    b: []const f32,
    result: []f32,
    rows: usize,
    cols: usize,
) void {
    // ここに実装
}

// 4. 行列のスカラー倍
pub fn matrixScaleSIMD(
    matrix: []const f32,
    scalar: f32,
    result: []f32,
    rows: usize,
    cols: usize,
) void {
    // ここに実装
}

pub fn main() !void {
    const allocator = std.heap.page_allocator;

    const rows = 4;
    const cols = 4;

    const matrix = try allocator.alloc(f32, rows * cols);
    defer allocator.free(matrix);

    const vector = try allocator.alloc(f32, cols);
    defer allocator.free(vector);

    const result = try allocator.alloc(f32, rows);
    defer allocator.free(result);

    // 初期化
    for (matrix, 0..) |*v, i| v.* = @floatFromInt(i);
    for (vector, 0..) |*v, i| v.* = @floatFromInt(i);

    matrixVectorMultiplySIMD(matrix, vector, result, rows, cols);

    std.debug.print("Matrix-vector product:\n", .{});
    for (result) |v| {
        std.debug.print("{d:.2} ", .{v});
    }
    std.debug.print("\n", .{});
}

ボーナス課題 (20点)

Bonus 1: FFT(高速フーリエ変換) (10点)

ファイル名: bonus1_fft.zig

const std = @import("std");

pub const Complex = struct {
    real: f32,
    imag: f32,

    pub fn add(self: Complex, other: Complex) Complex {
        return .{
            .real = self.real + other.real,
            .imag = self.imag + other.imag,
        };
    }

    pub fn mul(self: Complex, other: Complex) Complex {
        return .{
            .real = self.real * other.real - self.imag * other.imag,
            .imag = self.real * other.imag + self.imag * other.real,
        };
    }
};

// TODO: SIMDを使用したFFTを実装
pub fn fftSIMD(
    allocator: std.mem.Allocator,
    input: []const Complex,
) ![]Complex {
    // ここに実装
    // Cooley-Tukey アルゴリズム
}

pub fn main() !void {
    const allocator = std.heap.page_allocator;

    const size = 1024;
    const input = try allocator.alloc(Complex, size);
    defer allocator.free(input);

    // 初期化(サイン波)
    for (input, 0..) |*c, i| {
        const t = @as(f32, @floatFromInt(i)) / @as(f32, @floatFromInt(size));
        c.* = .{
            .real = @sin(2.0 * std.math.pi * t * 10.0),
            .imag = 0.0,
        };
    }

    const start = std.time.microTimestamp();
    const output = try fftSIMD(allocator, input);
    defer allocator.free(output);
    const end = std.time.microTimestamp();

    std.debug.print("FFT completed in {} μs\n", .{end - start});
}

Bonus 2: レイトレーシング (10点)

ファイル名: bonus2_raytracing.zig

const std = @import("std");

pub const Vec3 = @Vector(3, f32);

pub const Ray = struct {
    origin: Vec3,
    direction: Vec3,
};

pub const Sphere = struct {
    center: Vec3,
    radius: f32,
};

// TODO: SIMDを使用したレイトレーシングを実装

// 1. レイと球の交差判定
pub fn raySphereIntersectSIMD(ray: Ray, sphere: Sphere) ?f32 {
    // ここに実装
}

// 2. 複数の球との交差判定
pub fn rayMultipleSpheresIntersectSIMD(
    ray: Ray,
    spheres: []const Sphere,
) ?struct { distance: f32, index: usize } {
    // ここに実装
}

// 3. 画像のレンダリング
pub fn renderSIMD(
    width: usize,
    height: usize,
    spheres: []const Sphere,
    pixels: []u8,
) void {
    // ここに実装
}

pub fn main() !void {
    const allocator = std.heap.page_allocator;

    const width = 800;
    const height = 600;
    const pixels = try allocator.alloc(u8, width * height * 3);
    defer allocator.free(pixels);

    const spheres = [_]Sphere{
        .{ .center = .{ 0, 0, -5 }, .radius = 1.0 },
        .{ .center = .{ 2, 0, -5 }, .radius = 0.5 },
        .{ .center = .{ -2, 0, -5 }, .radius = 0.5 },
    };

    const start = std.time.milliTimestamp();
    renderSIMD(width, height, &spheres, pixels);
    const end = std.time.milliTimestamp();

    std.debug.print("Rendered in {} ms\n", .{end - start});
}

評価基準

項目 配点
Part 1: 基本的なベクトル演算 20点
Part 2: 配列演算のSIMD化 20点
Part 3: 画像処理 20点
Part 4: 行列演算 20点
**マンダトリー合計** **80点**
Bonus 1: FFT 10点
Bonus 2: レイトレーシング 10点
**ボーナス合計** **20点**

提出方法

exercise17/
├── part1_vector_ops.zig
├── part2_array_ops.zig
├── part3_image_processing.zig
├── part4_matrix.zig
├── bonus1_fft.zig
└── bonus2_raytracing.zig

ヒント

  • @Vector型の使い方:
const v: @Vector(4, f32) = .{ 1, 2, 3, 4 };

  • @reduce:
const sum = @reduce(.Add, v);

  • @splat:
const ones: @Vector(4, f32) = @splat(1.0);

  • 配列とベクトルの変換:
const vec: @Vector(4, f32) = array[i..][0..4].*;

参考資料

  • Zig Vectors: https://ziglang.org/documentation/master/#Vectors
  • SIMD Tutorial: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/