使用 Zig 实现 yes 命令

发布: 2022-11-17   上次更新: 2022-11-17   分类: 编程语言   标签: zig

文章目录

起因是看到一篇文章,作者介绍了如何用 Rust 优化 yes 命令,第一个 buffer 的版本还比较好懂,第二个复用 buffer 的就没有那么直接了。想了下用 Zig 实现会是怎么样?于是就有了下面的测试:

测试时会用到 pv 命令,需要单独安装

初始版

 1const std = @import("std");
 2
 3pub fn main() !void {
 4    const argv = std.os.argv;
 5
 6    const output: []const u8 = if (argv.len > 1)
 7        argv.ptr[1][0..std.mem.len(argv.ptr[1])]
 8    else
 9        "y";
10
11    const out = std.io.getStdOut();
12    var f = out.writer();
13    // var f = std.io.bufferedWriter(out.writer());
14    while ((try f.write(output)) > 0) {
15        _ = try f.write("\n");
16    }
17}

编译运行

1zig build-exe yes.zig -Drelease-safe
2./yes | pv -r > /dev/null

速度大概是 [2.77MiB/s]

笔者使用机器(macOS m1)上默认的 yes 命令速率: [5.31GiB/s] ,这差距不是一点两点,下面来一步步优化。

BufferedWriter 版本

 1const std = @import("std");
 2
 3pub fn main() !void {
 4    const argv = std.os.argv;
 5
 6    const output: []const u8 = if (argv.len > 1)
 7        argv.ptr[1][0..std.mem.len(argv.ptr[1])]
 8    else
 9        "y";
10
11    const out = std.io.getStdOut();
12    var f = std.io.bufferedWriter(out.writer());
13    while ((try f.write(output)) > 0) {
14        _ = try f.write("\n");
15    }
16}

速度大概是 116Mib/s

这个版本主要是利用了 bufferedWriter ,Zig 标准库默认是 4096,写入达到阈值一次性刷到底层 Writer 中,相当于变相减少了系统调用的次数。

复用 Buffer 版本

 1const std = @import("std");
 2
 3const BUFFER_CAP = 4 * 1024;
 4
 5fn fillBuffer(buf: []u8, output: []const u8) []const u8 {
 6    if (output.len + 1 > buf.len / 2) { // plus one newline
 7        return output;
 8    }
 9
10    std.mem.copy(u8, buf, output);
11    std.mem.copy(u8, buf[output.len..], "\n");
12    var buffer_size = output.len + 1;
13    while (buffer_size < buf.len / 2) {
14        std.mem.copy(u8, buf[buffer_size..], buf[0..buffer_size]);
15        buffer_size *= 2;
16    }
17
18    return buf[0..buffer_size];
19}
20
21pub fn main() !void {
22    const argv = std.os.argv;
23
24    const output: []const u8 = if (argv.len > 1)
25        argv.ptr[1][0..std.mem.len(argv.ptr[1])]
26    else
27        "y";
28
29    var buffer: [BUFFER_CAP]u8 = undefined;
30    const body = fillBuffer(&buffer, output);
31    const stdout = std.io.getStdOut();
32    var writer = stdout.writer();
33    while ((try writer.write(body)) > 0) {}
34}

速率大概是: [2.70GiB/s]

这个版本相比上个版本,除了减少系统调用外,通过复用同一个 buffer 减少了数据拷贝的代价。但还是赶不上系统版本的速度,可以通过增加 buffer 大小进一步提高吞吐。

当调整为 64*1024 时,速度大概是 [6.44GiB/s] ,再调大这个值速度也不会有显著提升,说明这已经达到磁盘写入上限了。

参考

评论

欢迎读者通过邮件与我交流,也可以在 MastodonTwitter 上关注我。