听说Zig有一段时间了, 最近终于有机会尝试了一下, 这里记录一些初步的印象.
这次体验的方式是直接复刻一个Rust的Merkle tree实现到Zig, Rust版本的代码在这里.
之所以选择这个例子, 主要是因为其代码量始终, 逻辑清晰, 依赖少, 同时也能测试大数据的场景. 同时Rust版本的设计相对清晰, 在复刻时也采用了类似的设计.
好的方面
- 语法简洁, 学习曲线平缓, 很容易上手, 很接近C/Go的风格. 有defer, errdefer等语法糖, 比较便利.
- 构建系统很棒
- 编译速度很快, 远快于Rust
- 编译产物小巧, 完整复刻的版本编译后的ReleaseFast的产物在Mac下只有160k左右, Linux下800k左右, 都动态链接了C库.
- 性能非常好, 几乎没有做特别多性能优化, 近百万条数据的情况下, 性能就比Rust版本快了2.x倍, 内存占用略高(实在是不想手动优化内存占用了).
- 支持文件内单元测试, 非常方便, 内存泄漏也可以在单测中检测出来
- comptime 很好, 但这次没有用到.
不足的方面
- 生态略欠缺, 缺少一些常用的库, 但可以和C库互通, 这点在很大程度上能够弥补生态的不足.
- 版本不兼容变化较多, 极难找到相关文档, 通常需要去翻语言的源代码找到参考代码. 版本升级也导致有些库不能跟上最新的变化, 需要自己动手修改.
- LLM不友好, LLM的知识版本较旧,很难写可以用在最新版本编译器的代码.
- 对于需要动态分配内存的场景, 需要手动传入allocator, allocator无处不在, 有时候内存管理略显繁琐. 特写是手动写出有较多内存分配的代码会相对有挑战. 好在有单测,可以在测试时检测出内存泄漏的问题.
一些细节
- 默认内存分配器的性能有点拉, 正式环境还是要换成c分配器, 对于大量小对象的场景, 性能提升明显, 对于Merkle tree这种大量小对象的场景, 性能提升有20倍以上的提升.
总结
整体来讲这次复刻的体验挺好, 由于编译速度较快可以很好的进行迭代, 语法也比较容易上手, 但库的一些语法变化着实浪费了一些时间去找相关代码. Zig的性能表现非常亮眼, 感觉它更加适合系统编程, 有些偏底层.
代码编写风格更贴近命令式编程, 对于数据和内存的操作都相对底层, 但相应的会带来更多的控制权.
附
复刻的代码在这里. 欢迎提交PR ;-) .