課題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/