-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
std.io.AnyWriter (and hence std.io.GenericWriter, since that uses AnyWriter internally) has a large amount of overhead in tight loops, as it hampers inlining.
The following code snippet demonstrates an extreme case of the issue, but it comes up a lot in practice too:
pub fn main() !void {
const devnull = try std.fs.openFileAbsolute("/dev/null", .{ .mode = .write_only });
var timer = try std.time.Timer.start();
{
// Write 1MiB to /dev/null, one byte at a time, using std.io.BufferedWriter
var bufw = std.io.bufferedWriter(devnull.writer());
const w = bufw.writer();
for (0..1 << 20) |i| {
try w.writeByte(@truncate(i));
}
try bufw.flush();
std.debug.print("BufferedWriter: {}\n", .{std.fmt.fmtDuration(timer.read())});
}
{
timer.reset();
// Write 1MiB to /dev/null, one byte at a time, using a custom buffering solution
var buf: [4096]u8 = undefined;
var idx: usize = 0;
const w = devnull.writer();
for (0..1 << 20) |i| {
buf[idx] = @truncate(i);
idx += 1;
if (idx >= buf.len) {
try w.writeAll(&buf);
idx = 0;
}
}
try w.writeAll(buf[0..idx]);
std.debug.print("Custom buffering: {}\n", .{std.fmt.fmtDuration(timer.read())});
}
}
const std = @import("std");EDIT (by @mlugg): the original numbers here were incorrect, since OP accidentally used -Doptimize with zig run. Accurate numbers from running this on my system are as follows:
[mlugg@nebula test]$ zig run repro.zig -OReleaseFast
BufferedWriter: 488.15us
Custom buffering: 272.103usUsing a custom buffering solution rather than std.io.BufferedWriter results in an approximately 2x speedup, simply by avoiding the overhead of std.io.AnyWriter.
This pretty much defeats the whole point of having (EDIT @mlugg: this sentence was written based on incorrect performance numbers, which showed a wrongly exaggerated result.)BufferedWriter in the first place, since AnyWriter destroys the good write performance it would otherwise provide.