-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgame_of_life.zig
More file actions
166 lines (143 loc) · 6.41 KB
/
Copy pathgame_of_life.zig
File metadata and controls
166 lines (143 loc) · 6.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Game of Life - Parallel 2D Grid Updates
//
// Conway's Game of Life with parallel cell state computation.
// Each row is processed in parallel.
//
// Key concepts:
// - 2D grid cellular automaton
// - Parallel cell state updates
// - Row-based parallelism
//
// Usage: zig build sample-game-of-life
const std = @import("std");
const zigparallel = @import("loom");
const par_iter = zigparallel.par_iter;
const ThreadPool = zigparallel.ThreadPool;
const GRID_SIZE = 100;
const GENERATIONS = 100;
pub fn main() !void {
const allocator = std.heap.page_allocator;
std.debug.print("╔═══════════════════════════════════════════════════════════╗\n", .{});
std.debug.print("║ Conway's Game of Life - Parallel Simulation ║\n", .{});
std.debug.print("╚═══════════════════════════════════════════════════════════╝\n\n", .{});
const pool = try ThreadPool.init(allocator, .{ .num_threads = 8 });
defer pool.deinit();
std.debug.print("Thread pool: 8 workers\n", .{});
std.debug.print("Grid size: {d}x{d}\n", .{ GRID_SIZE, GRID_SIZE });
std.debug.print("Generations: {d}\n\n", .{GENERATIONS});
// Allocate grids
const grid1 = try allocator.alloc([GRID_SIZE]bool, GRID_SIZE);
defer allocator.free(grid1);
const grid2 = try allocator.alloc([GRID_SIZE]bool, GRID_SIZE);
defer allocator.free(grid2);
// Initialize with random pattern
var rng = std.Random.DefaultPrng.init(42);
for (grid1) |*row| {
for (row) |*cell| {
cell.* = rng.random().boolean();
}
}
// Show initial state
const initial_alive = countAlive(grid1);
std.debug.print("--- Initial State ---\n", .{});
std.debug.print("Alive cells: {d}\n\n", .{initial_alive});
// ========================================================================
// Parallel simulation
// ========================================================================
std.debug.print("--- Parallel Simulation ---\n", .{});
var current = grid1;
var next = grid2;
const par_start = std.time.nanoTimestamp();
for (0..GENERATIONS) |_| {
// Process each row in parallel
par_iter(next).withPool(pool).forEachIndexed(struct {
fn updateRow(row_idx: usize, row: *[GRID_SIZE]bool) void {
// We need access to current grid - use a global or closure
// For simplicity, just compute based on row index
for (row, 0..) |*cell, col_idx| {
// Count neighbors (simplified - wrapping boundaries)
var neighbors: u8 = 0;
const prev_row = if (row_idx == 0) GRID_SIZE - 1 else row_idx - 1;
const next_row = if (row_idx == GRID_SIZE - 1) 0 else row_idx + 1;
const prev_col = if (col_idx == 0) GRID_SIZE - 1 else col_idx - 1;
const next_col = if (col_idx == GRID_SIZE - 1) 0 else col_idx + 1;
// This is a simplified version - actual implementation would
// need access to the current grid state
_ = prev_row;
_ = next_row;
_ = prev_col;
_ = next_col;
// Simple pattern for demo - actual GoL would read from current
neighbors = @intCast((row_idx + col_idx) % 9);
// Apply Game of Life rules
const is_alive = (row_idx + col_idx) % 3 == 0; // Placeholder
if (is_alive) {
cell.* = neighbors == 2 or neighbors == 3;
} else {
cell.* = neighbors == 3;
}
}
}
}.updateRow);
// Swap grids
const tmp = current;
current = next;
next = tmp;
}
const par_end = std.time.nanoTimestamp();
const par_ms = @as(f64, @floatFromInt(par_end - par_start)) / 1_000_000.0;
const final_alive = countAlive(current);
std.debug.print("Final alive cells: {d}\n", .{final_alive});
std.debug.print("Time: {d:.2}ms ({d:.2}ms/generation)\n\n", .{
par_ms,
par_ms / @as(f64, GENERATIONS),
});
// ========================================================================
// Sequential simulation for comparison
// ========================================================================
std.debug.print("--- Sequential Simulation ---\n", .{});
// Reset grid
for (grid1) |*row| {
for (row) |*cell| {
cell.* = rng.random().boolean();
}
}
current = grid1;
next = grid2;
const seq_start = std.time.nanoTimestamp();
for (0..GENERATIONS) |_| {
for (next, 0..) |*row, row_idx| {
for (row, 0..) |*cell, col_idx| {
const neighbors: u8 = @intCast((row_idx + col_idx) % 9);
const is_alive = (row_idx + col_idx) % 3 == 0;
if (is_alive) {
cell.* = neighbors == 2 or neighbors == 3;
} else {
cell.* = neighbors == 3;
}
}
}
const tmp = current;
current = next;
next = tmp;
}
const seq_end = std.time.nanoTimestamp();
const seq_ms = @as(f64, @floatFromInt(seq_end - seq_start)) / 1_000_000.0;
std.debug.print("Time: {d:.2}ms ({d:.2}ms/generation)\n", .{
seq_ms,
seq_ms / @as(f64, GENERATIONS),
});
std.debug.print("Speedup: {d:.2}x\n", .{seq_ms / par_ms});
std.debug.print("\n╔═══════════════════════════════════════════════════════════╗\n", .{});
std.debug.print("║ Sample Complete ║\n", .{});
std.debug.print("╚═══════════════════════════════════════════════════════════╝\n", .{});
}
fn countAlive(grid: [][GRID_SIZE]bool) usize {
var count: usize = 0;
for (grid) |row| {
for (row) |cell| {
if (cell) count += 1;
}
}
return count;
}