Skip to content

Commit 86a0e33

Browse files
committed
knucleotide: add parallelized zig version
1 parent 0c1d472 commit 86a0e33

File tree

1 file changed

+220
-0
lines changed

1 file changed

+220
-0
lines changed

bench/algorithm/knucleotide/1-m.zig

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
const std = @import("std");
2+
3+
const gpa = std.heap.c_allocator;
4+
const stdout = std.io.getStdOut().writer();
5+
6+
const Code = struct {
7+
data: u64,
8+
9+
pub inline fn encodeByte(c: u8) u8 {
10+
return (c >> 1) & 0b11;
11+
}
12+
13+
pub inline fn makeMask(frame: usize) u64 {
14+
return (@as(u64, 1) << @as(u6, @intCast(2 * frame))) - 1;
15+
}
16+
17+
pub inline fn push(self: *Code, c: u8, mask: u64) void {
18+
self.data = ((self.data << 2) | c) & mask;
19+
}
20+
21+
pub fn fromStr(s: []const u8) Code {
22+
const mask = Code.makeMask(s.len);
23+
var res = Code{ .data = 0 };
24+
for (s) |c| {
25+
res.push(Code.encodeByte(c), mask);
26+
}
27+
return res;
28+
}
29+
30+
pub fn toString(self: Code, frame: usize) ![]const u8 {
31+
var result = std.ArrayList(u8).init(gpa);
32+
var code = self.data;
33+
var i: usize = 0;
34+
while (i < frame) : (i += 1) {
35+
const c: u8 = switch (@as(u8, @truncate(code)) & 0b11) {
36+
Code.encodeByte('A') => 'A',
37+
Code.encodeByte('T') => 'T',
38+
Code.encodeByte('G') => 'G',
39+
Code.encodeByte('C') => 'C',
40+
else => unreachable,
41+
};
42+
try result.append(c);
43+
code >>= 2;
44+
}
45+
std.mem.reverse(u8, result.items);
46+
return result.toOwnedSlice();
47+
}
48+
};
49+
50+
pub fn readInput() ![]const u8 {
51+
const args = try std.process.argsAlloc(gpa);
52+
defer std.process.argsFree(gpa, args);
53+
const file_name = if (args.len > 1) args[1] else "25000_in";
54+
const file = try std.fs.cwd().openFile(file_name, .{});
55+
var buffered_reader = std.io.bufferedReader(file.reader());
56+
const reader = buffered_reader.reader();
57+
{ // skip past first lines starting with '>'
58+
var i: u8 = 0;
59+
while (i < 3) : (i += 1) {
60+
while (true) {
61+
const c = try reader.readByte();
62+
if (c == '>') break;
63+
}
64+
}
65+
while (true) {
66+
const c = try reader.readByte();
67+
if (c == '\n') break;
68+
}
69+
}
70+
71+
var buf = try reader.readAllAlloc(gpa, std.math.maxInt(u32));
72+
// In place, remove all newlines from buf and encode nucleotides
73+
// using only the last 2 bits in each byte.
74+
{
75+
var i: usize = 0;
76+
for (buf) |c| {
77+
if (c != '\n') {
78+
// Gives a -> 0x00, c -> 0x01, g -> 0x03, t -> 0x02
79+
buf[i] = (c >> 1) & 0x03;
80+
i += 1;
81+
}
82+
}
83+
buf.len = i;
84+
}
85+
return buf;
86+
}
87+
88+
const CodeContext = struct {
89+
pub fn eql(_: CodeContext, a: Code, b: Code) bool { return a.data == b.data; }
90+
pub fn hash(_: CodeContext, c: Code) u64 { return c.data ^ (c.data >> 7); }
91+
};
92+
93+
const Map = std.HashMapUnmanaged(Code, u32, CodeContext, 45);
94+
95+
const Iter = struct {
96+
i: usize = 0,
97+
input: []const u8,
98+
code: Code,
99+
mask: u64,
100+
101+
pub fn init(input: []const u8, frame: usize) Iter {
102+
const mask = Code.makeMask(frame);
103+
var code = Code{ .data = 0 };
104+
for (input[0 .. frame - 1]) |c| code.push(c, mask);
105+
return .{
106+
.input = input[frame - 1 ..],
107+
.code = code,
108+
.mask = mask,
109+
};
110+
}
111+
112+
pub fn next(self: *Iter) ?Code {
113+
if (self.i >= self.input.len) return null;
114+
defer self.i += 1;
115+
const c = self.input[self.i];
116+
Code.push(&self.code, c, self.mask);
117+
return self.code;
118+
}
119+
};
120+
121+
fn genMap(taskIndex: usize, from: usize, to: usize, seq: []const u8, frame: usize, maps: []Map) !void {
122+
const map = &maps[taskIndex];
123+
var iter = Iter.init(seq[from .. to + frame - 1], frame);
124+
while (iter.next()) |code| {
125+
const gop = try map.getOrPut(gpa, code);
126+
if (!gop.found_existing) gop.value_ptr.* = 0;
127+
gop.value_ptr.* += 1;
128+
}
129+
}
130+
131+
const CountCode = struct {
132+
count: u32,
133+
code: Code,
134+
135+
pub fn dsc(_: void, a: CountCode, b: CountCode) bool {
136+
const order = std.math.order(a.count, b.count);
137+
return order == .gt or (order == .eq and b.code.data > a.code.data);
138+
}
139+
};
140+
141+
fn printMap(frame: usize, maps: []const Map) !void {
142+
const code_limit = 16;
143+
var counts: [code_limit]u32 = @splat(0);
144+
var total: u64 = 0;
145+
for (maps) |map| {
146+
var iter = map.iterator();
147+
while (iter.next()) |it| {
148+
const code = it.key_ptr.*;
149+
const count = it.value_ptr.*;
150+
total += count;
151+
counts[code.data] += count;
152+
}
153+
}
154+
155+
var cc: std.BoundedArray(CountCode, code_limit) = .{};
156+
for (counts, 0..) |count, code_data| if (count > 0) {
157+
cc.appendAssumeCapacity(.{ .count = count, .code = .{ .data = @intCast(code_data) } });
158+
};
159+
std.mem.sort(CountCode, cc.slice(), {}, CountCode.dsc);
160+
161+
for (cc.slice()) |c| {
162+
try stdout.print("{!s} {d:.3}\n", .{
163+
c.code.toString(frame),
164+
@as(f32, @floatFromInt(c.count)) / @as(f32, @floatFromInt(total)) * 100.0,
165+
});
166+
}
167+
try stdout.print("\n", .{});
168+
}
169+
170+
fn printOcc(occ: []const u8, maps: []const Map) !void {
171+
const code = Code.fromStr(occ);
172+
var total: u32 = 0;
173+
for (maps) |m| {
174+
if (m.get(code)) |count| total += count;
175+
}
176+
try stdout.print("{}\t{s}\n", .{ total, occ });
177+
}
178+
179+
fn runInParallel(task_count: usize, len: usize, comptime f: anytype, args: anytype) !void {
180+
const tasks = try gpa.alloc(std.Thread, task_count - 1);
181+
defer gpa.free(tasks);
182+
const len_per_task = @divTrunc(len, task_count);
183+
for (tasks, 0..) |*task, i| {
184+
const first = len_per_task * i;
185+
const last = first + len_per_task;
186+
task.* = try std.Thread.spawn(.{}, f, .{ i, first, last } ++ args);
187+
}
188+
try @call(.auto, f, .{ tasks.len, tasks.len * len_per_task, len } ++ args);
189+
for (tasks) |*task| task.join();
190+
}
191+
192+
fn genMaps(seq: []const u8, frame: usize, maps: []Map) !void {
193+
for (maps) |*m| m.clearAndFree(gpa);
194+
try runInParallel(maps.len, seq.len - (frame - 1), genMap, .{seq, frame, maps});
195+
}
196+
197+
pub fn main() !void {
198+
const seq = try readInput();
199+
const task_count = try std.Thread.getCpuCount();
200+
const maps = try gpa.alloc(Map, task_count);
201+
defer gpa.free(maps);
202+
@memset(maps, .empty);
203+
204+
try genMaps(seq, 1, maps);
205+
try printMap(1, maps);
206+
try genMaps(seq, 2, maps);
207+
try printMap(2, maps);
208+
209+
const occs = [_][]const u8{
210+
"GGT",
211+
"GGTA",
212+
"GGTATT",
213+
"GGTATTTTAATT",
214+
"GGTATTTTAATTTATAGT",
215+
};
216+
for (occs) |occ| {
217+
try genMaps(seq, occ.len, maps);
218+
try printOcc(occ, maps);
219+
}
220+
}

0 commit comments

Comments
 (0)