diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-04-26 23:21:56 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-04-26 23:21:56 +0200 |
| commit | e2f01d5df22704bfe62396a0f9a260f86edbde0e (patch) | |
| tree | 86eba9adc57cbf1cab53b48bc56df73477a2dc7a /src | |
| parent | 922db9c5fc50b82182fb5a0b4e3c8bb18fc2e0ab (diff) | |
generate: add multi-threading
Diffstat (limited to 'src')
| -rw-r--r-- | src/argument.zig | 21 | ||||
| -rw-r--r-- | src/main.zig | 55 | ||||
| -rw-r--r-- | src/scheduler.zig | 60 |
3 files changed, 113 insertions, 23 deletions
diff --git a/src/argument.zig b/src/argument.zig index 37d9045..b553352 100644 --- a/src/argument.zig +++ b/src/argument.zig @@ -22,6 +22,8 @@ fn help(err: ?anyerror) noreturn { \\ \\ -n, --non-empty Only output texts which are non-empty. \\ + \\ -m, --min-length n Minimum length of sentential string. + \\ \\ recognize [grammar] [options] \\ Options: \\ -i, --input entry Specify input source, if the path @@ -74,12 +76,13 @@ pub const Entry = struct { name: []const u8, file: std.fs.File, - pub fn open(path: []const u8, flags: std.fs.File.OpenFlags) Self { + pub fn open(path: []const u8, writer: bool) Self { var cwd = std.fs.cwd(); return Self { .name = path, - .file = cwd.openFile(path, flags) catch |e| help(e) + .file = (if (writer) cwd.createFile(path, .{}) + else cwd.openFile(path, .{})) catch |e| help(e) }; } @@ -115,7 +118,7 @@ pub const RecognizeArgs = struct { pub const GenerateArgs = struct { count: usize, - empty: bool, + min_length: usize, output: Entry, grammar: Grammar, }; @@ -145,7 +148,7 @@ pub const Args = union(Mode) { while (args.next()) |arg| { if (check_flags(arg, &[_][]const u8 { "-i", "--input" })) { - input = Entry.open(check(args.next()), .{}); + input = Entry.open(check(args.next()), false); } else help(error.InvalidArgument); } @@ -160,22 +163,24 @@ pub const Args = union(Mode) { .generate => { var count: usize = 1; var output: ?Entry = null; - var empty: bool = true; + var min_length: usize = 0; while (args.next()) |arg| { if (check_flags(arg, &[_][]const u8 { "-o", "--output" })) { - output = Entry.open(check(args.next()), .{ .mode = .write_only }); + output = Entry.open(check(args.next()), true); } else if (check_flags(arg, &[_][]const u8 { "-c", "--count" })) { count = parse_int(check(args.next())); } else if (check_flags(arg, &[_][]const u8 { "-n", "--non-empty" })) { - empty = false; + min_length = 1; + } else if (check_flags(arg, &[_][]const u8 { "-m", "--min-length" })) { + min_length = parse_int(check(args.next())); } else help(error.InvalidArgument); } return Self { .generate = .{ .count = count, - .empty = empty, + .min_length = min_length, .output = output orelse Entry.stdout(), .grammar = grammar, }, diff --git a/src/main.zig b/src/main.zig index c1d63c0..ac52f8a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,6 +4,7 @@ pub const gss = @import("gss.zig"); pub const recognizer = @import("recognizer.zig"); pub const argument = @import("argument.zig"); pub const Generator = @import("generator.zig").Generator; +pub const Scheduler = @import("scheduler.zig").Scheduler; const Args = argument.Args; const RecognizeArgs = argument.RecognizeArgs; @@ -83,26 +84,50 @@ fn recognize(args: *RecognizeArgs, allocator: std.mem.Allocator) !void { try bufwriter.flush(); } -fn generate(args: *GenerateArgs, allocator: std.mem.Allocator) !void { - var writer = args.output.file.writer(); - var count: usize = 0; +const RandomGenerator = Generator(struct { + const Self = @This(); - var generator = Generator(struct { - const Self = @This(); + pub fn next(_: *Self, n: usize) usize { + return std.crypto.random.uintLessThan(usize, n); + } +}); - pub fn next(_: *Self, n: usize) usize { - return std.crypto.random.uintLessThan(usize, n); +pub fn generate_word( + args: *GenerateArgs, + generator: *RandomGenerator, + allocator: std.mem.Allocator +) []const u8 { + while (true) { + const text = generator.sentential_from_grammar( + &args.grammar, + 10000, + allocator + ) catch continue; + if (text.len >= args.min_length) { + return text; } - }){}; - while (count < args.count) { - const text = try generator.sentential_from_grammar(&args.grammar, 1000, allocator); - defer allocator.free(text); + allocator.free(text); + } +} - if (args.empty or text.len > 0) { - try writer.print("{s}\n", .{text}); - count += 1; - } +fn generate(args: *GenerateArgs, allocator: std.mem.Allocator) !void { + var writer = args.output.file.writer(); + + var scheduler: Scheduler(generate_word) = undefined; + try scheduler.init(allocator); + var generator = RandomGenerator {}; + + for (0..args.count) |_| { + try scheduler.push_task(.{args, &generator, allocator}); + } + + const results = try scheduler.deinit(); + defer allocator.free(results); + + for (results) |result| { + try writer.print("{s}\n", .{result}); + allocator.free(result); } } diff --git a/src/scheduler.zig b/src/scheduler.zig new file mode 100644 index 0000000..b2ab0db --- /dev/null +++ b/src/scheduler.zig @@ -0,0 +1,60 @@ +const std = @import("std"); + +pub fn Scheduler(function: anytype) type { + const FuncType = @TypeOf(function); + const func_info = @typeInfo(FuncType).Fn; + const R = func_info.return_type orelse std.builtin.Type.Void; + const ArgType = std.meta.ArgsTuple(FuncType); + + const wrapper = struct { + pub fn call(args: ArgType, result: *R) void { + result.* = @call(.auto, function, args); + } + }.call; + + return struct { + const Self = @This(); + + allocator: std.mem.Allocator, + pool: std.Thread.Pool, + wait_group: std.Thread.WaitGroup, + results: std.ArrayList(*R), + + pub fn init(self: *Self, allocator: std.mem.Allocator) !void { + self.allocator = allocator; + self.results = std.ArrayList(*R).init(allocator); + self.wait_group.reset(); + + try self.pool.init(.{ + .allocator = allocator, + }); + } + + pub fn push_task(self: *Self, args: ArgType) !void { + const result = try self.allocator.create(R); + try self.results.append(result); + self.pool.spawnWg( + &self.wait_group, + wrapper, + .{ args, result } + ); + } + + pub fn deinit(self: *Self) ![]R { + self.pool.waitAndWork(&self.wait_group); + self.pool.deinit(); + + const results = try self.allocator.alloc(R, self.results.items.len); + + for (self.results.items, 0..) |result, index| { + results[index] = result.*; + self.allocator.destroy(result); + } + + self.results.deinit(); + self.* = undefined; + + return results; + } + }; +} |