-
Notifications
You must be signed in to change notification settings - Fork 1
【Zig 日报】io_uring 与 Grand Central Dispatch 的 std.Io 实现已落地 #308
Copy link
Copy link
Open
Labels
日报daily reportdaily report
Description
作者:Andrew Kelley
随着 0.16.0 发布周期的尾声临近,Jacob 一直在努力工作,使 std.Io.Evented 紧跟所有最新的 API 变化:
- io_uring 实现
- Grand Central Dispatch (GCD) 实现
这两者都基于用户态栈切换(Userspace stack switching),有时也被称为“fiber(纤程)”、“stackful coroutines(有栈协程)”或“green threads(绿程)”。
现在可以通过使用 std.Io.Evented 构建应用程序来尝试这些功能。由于在实现可靠和鲁棒的运行之前还有重要的后续工作要做,它们目前应被视为实验性功能:
- 更好的错误处理
- 移除日志记录
- 诊断编译器在使用
IoMode.evented时出现的未预期的性能下降 - 部分函数仍未实现
- 需要更多的测试覆盖
- 需要内置函数来告知给定函数的最大栈大小,以便在关闭过量使用(overcommit)时使这些实现具有实用性。
考虑到这些注意事项,我们似乎确实正在抵达“应许之地”——在代码中可以毫不费力地切换 Zig 的 I/O 实现:
const std = @import("std");
pub fn main(init: std.process.Init.Minimal) !void {
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
const gpa = debug_allocator.allocator();
var threaded: std.Io.Threaded = .init(gpa, .{
.argv0 = .init(init.args),
.environ = init.environ,
});
defer threaded.deinit();
const io = threaded.io();
return app(io);
}
fn app(io: std.Io) !void {
try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
}下面是 ./hello_threaded 的 strace 输出,其显示了传统的 writev 系统调用:
$ strace ./hello_threaded
execve("./hello_threaded", ["./hello_threaded"], 0x7ffc1da88b20 /* 98 vars */) = 0
mmap(NULL, 262207, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f583f338000
arch_prctl(ARCH_SET_FS, 0x7f583f378018) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
prlimit64(0, RLIMIT_STACK, {rlim_cur=16384*1024, rlim_max=RLIM64_INFINITY}, NULL) = 0
sigaltstack({ss_sp=0x7f583f338000, ss_flags=0, ss_size=262144}, NULL) = 0
sched_getaffinity(0, 128, [0 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]) = 8
rt_sigaction(SIGIO, {sa_handler=0x1019d90, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x10328c0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=0x1019d90, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x10328c0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
writev(1, [{iov_base="Hello, World!\n", iov_len=14}], 1Hello, World!
) = 14
rt_sigaction(SIGIO, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x10328c0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x10328c0}, NULL, 8) = 0
exit_group(0) = ?
+++ exited with 0 +++
仅切换 I/O 实现:
const std = @import("std");
pub fn main(init: std.process.Init.Minimal) !void {
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
const gpa = debug_allocator.allocator();
var evented: std.Io.Evented = undefined;
try evented.init(gpa, .{
.argv0 = .init(init.args),
.environ = init.environ,
.backing_allocator_needs_mutex = false,
});
defer evented.deinit();
const io = evented.io();
return app(io);
}
fn app(io: std.Io) !void {
try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
}下面是 ./hello_evented 的 strace 输出,其显示了 io_uring_setup 和 io_uring_enter 等调用:
execve("./hello_evented", ["./hello_evented"], 0x7fff368894f0 /* 98 vars */) = 0
mmap(NULL, 262215, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f70a4c28000
arch_prctl(ARCH_SET_FS, 0x7f70a4c68020) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
prlimit64(0, RLIMIT_STACK, {rlim_cur=16384*1024, rlim_max=RLIM64_INFINITY}, NULL) = 0
sigaltstack({ss_sp=0x7f70a4c28008, ss_flags=0, ss_size=262144}, NULL) = 0
sched_getaffinity(0, 128, [0 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]) = 8
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f70a4c27000
mmap(0x7f70a4c28000, 548864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f70a4ba1000
io_uring_setup(64, {flags=IORING_SETUP_COOP_TASKRUN|IORING_SETUP_SINGLE_ISSUER, sq_thread_cpu=0, sq_thread_idle=1000, sq_entries=64, cq_entries=128, features=IORING_FEAT_SINGLE_MMAP|IORING_FEAT_NODROP|IORING_FEAT_SUBMIT_STABLE|IORING_FEAT_RW_CUR_POS|IORING_FEAT_CUR_PERSONALITY|IORING_FEAT_FAST_POLL|IORING_FEAT_POLL_32BITS|IORING_FEAT_SQPOLL_NONFIXED|IORING_FEAT_EXT_ARG|IORING_FEAT_NATIVE_WORKERS|IORING_FEAT_RSRC_TAGS|IORING_FEAT_CQE_SKIP|IORING_FEAT_LINKED_FILE|IORING_FEAT_REG_REG_RING|IORING_FEAT_RECVSEND_BUNDLE|IORING_FEAT_MIN_TIMEOUT|IORING_FEAT_RW_ATTR|IORING_FEAT_NO_IOWAIT, sq_off={head=0, tail=4, ring_mask=16, ring_entries=24, flags=36, dropped=32, array=2112, user_addr=0}, cq_off={head=8, tail=12, ring_mask=20, ring_entries=28, overflow=44, cqes=64, flags=40, user_addr=0}}) = 3
mmap(NULL, 2368, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, 3, 0) = 0x7f70a4ba0000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, 3, 0x10000000) = 0x7f70a4b9f000
io_uring_enter(3, 1, 1, IORING_ENTER_GETEVENTS, NULL, 8Hello, World!
) = 1
io_uring_enter(3, 1, 1, IORING_ENTER_GETEVENTS, NULL, 8) = 1
munmap(0x7f70a4b9f000, 4096) = 0
munmap(0x7f70a4ba0000, 2368) = 0
close(3) = 0
munmap(0x7f70a4ba1000, 548864) = 0
exit_group(0) = ?
+++ exited with 0 +++
这里的关键点在于,上述两个代码段中的 app 函数是完全相同的。
除了 Hello World 之外,Zig 编译器本身在使用 std.Io.Evented(无论是 io_uring 还是 GCD)时也能正常工作。但正如前文所述,这样做时存在尚未诊断出的性能下降问题。
祝各位 Hack 愉快!
Andrew
加入我们
Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文群体中的使用,有多种方式可以参与进来:
- 供稿,分享自己使用 Zig 的心得
- 改进 ZigCC 组织下的开源项目
- 加入微信群、Telegram 群组
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
日报daily reportdaily report