8× faster binary search:从编译代码一路挖到机械同理心
这篇性能文章很适合给写 Rust 的人当一堂“CPU 现实主义”复习课。作者面对的不是玩具问题,而是 scikit-learn 直方图梯度提升里一个很实际的步骤:把大批浮点数分桶到 0–254 的整数范围。原始做法已经不差——算法是标准二分查找,代码跑在编译型语言里,还能并行——但作者继续往下挖,最后把实现从“够快”推进到 约 8× 加速,核心手段不是换算法,而是让代码别再和 CPU 的分支预测打架。
文中最有价值的地方,在于它把“为什么慢”拆得很具体。传统二分查找里,if boundaries[middle] < value 这种判断对 CPU 来说非常难猜,因为桶边界本来就是为了把输入尽量均匀摊开;结果就是大量 branch misprediction。作者给出的改法,是把可变次数的 while 循环改成固定轮数迭代,再用 Rust 的 std::hint::select_unpredictable() 把条件分支改写成更接近 branchless 的条件选择,让 CPU 能把更多指令并行吃下去。文章里给出的硬件计数结果也很直观:分支失预测几乎被打掉,IPC 明显升高,单核执行效率跟着上来。
更妙的是,这篇文章没有停在“branchless 就赢了”这种结论上,而是继续往 bounds check、编译优化和剩余无效工作上抠细节。对 Rust 开发者来说,它的启发不只是二分查找本身,而是一个很具体的提醒:当你已经有了还不错的算法以后,真正限制性能的,往往是 CPU 如何看待你的控制流,而不是源码表面上“做了几次比较”这么简单。
原文链接:https://pythonspeed.com/articles/branchless-binary-search/
x-nes v0.1 发布:cycle-accurate NES 模拟器核心用 Rust 跑到 no_std 和 FFI
x-nes 这个新项目很对底层和模拟器爱好者的胃口:它不是一个成品模拟器,而是一个 可嵌入的 NES 模拟器核心库,重点放在 Rust API、C FFI 和跨环境可集成性上。作者给出的目标相当明确:同一套核心既能跑在桌面,也能压到微控制器等更受限的环境里,因此项目一开始就把 no_std、FFI、静态/动态库产物 这些路线一起考虑进来了。
从目前披露的能力看,v0.1 的基础已经不算浅。它实现了 cycle-accurate 的 6502 CPU,覆盖 56 条指令和 13 种寻址模式;PPU 方面做到 scanline-accurate 并支持 NMI;APU 也已经有 pulse channel 和 sample buffer。卡带这边则支持 iNES 解析和 6 种 mapper。对很多“想做前端壳、调试器、教育项目或嵌入式移植”的开发者来说,这种库形态比单体模拟器更有二次开发价值。
作者也没有把它包装成“已经完美”的项目。当前准确率写得很坦白:AccuracyCoin 141 项里通过 86 项(61%),Blargg 测试则是 20/21 通过。已知问题集中在音频时序、PPU 边界行为、MMC3 支持缺失,以及 DMA / 中断 / 假读等更细碎的硬件细节。正因为这些短板讲得很清楚,反而让这个项目看起来更像一个值得持续跟踪的工程起点,而不是只靠截图和 GIF 吸引眼球的展示帖。
原文链接:https://old.reddit.com/r/rust/comments/1uyune4/xnes_v01_cycleaccurate_nes_emulator_library_in/
Roc 编译器 Rust→Zig 重写阶段性过线:功能对齐已完成,热重载与跨编译路径一并落地
Roc 团队这篇长文之所以在 Rust 社区引发讨论,不只是因为它在讲“从 Rust 重写到 Zig”,而是因为它给出了一个已经跑到 feature parity 的阶段性结果。作者回顾说,团队把大约 30 万行 Rust 编译器代码 重写到 Zig,整个过程持续了 487 天,如今新的编译器已经能把示例项目 Rocci Bird 跑起来,而且 roc build --opt=size 生成的 wasm 产物压到了 31KB,比旧编译器时代小了不止一点。
文章里真正值得 Rust 读者细看的,其实是“为什么他们宁愿重写”。作者并没有把原因简化成语言口味,而是明确提到原编译器里一些架构性问题,尤其是围绕 lambda set specialization / defunctionalization 的实现复杂度,已经到了继续局部修补不划算的程度。也就是说,这不是“Rust 不行”的情绪化叙述,而是一个编译器团队在大规模演进中,对历史结构债、目标能力和重写成本做出的工程判断。
与此同时,新编译器也不是只换了语言表皮。Roc 现在把 hot code loading、跨平台交叉编译,以及更激进的零分配字符串模式匹配能力一起往前推。文章里展示的方向很清楚:他们想把高性能编译语言常见的部署能力,和解释型语言常见的开发体验揉进同一条工具链里。对 Rust 社区来说,这篇文章最有讨论价值的点,不是“要不要学 Zig”,而是大型编译器项目在遇到架构拐点时,到底该怎么判断“渐进式重构”和“重写一次”各自的边界。
原文链接:https://rtfeldman.com/rust-to-zig
Async scoped task 的“三难困境”新解:用 MustCompleteFuture 把“不能忘掉 future”显式写进类型
这篇 self-post 提的问题相当 Rust:如果你想做类似 std::thread::scope 的 async scope task,最大的麻烦之一在于——一个 future 即使已经被 poll 过,只要还没完成,理论上依然可能被 mem::forget,而这会直接冲击那些借用外部数据的 scoped task 设计。作者把这件事概括成一个 scoped task 的“三难困境”,并尝试提出一种更类型系统导向的解决方案。
核心想法是引入一个 MustCompleteFuture trait,把“这个 future 最终必须被 drop 或 poll 到完成”这件事,从口头约束提升成类型层面的显式信息。这样一来,像 scope(...) 这类能力返回的就不再只是普通 future,而是带着“必须完成”语义的对象;调用者再通过 unsafe into_future() 或宏展开后的受控写法,把这一约束沿调用链往上传递。作者也很坦白地指出,这种设计依然会像 async 本身一样 viral,也就是要求整条调用栈配合标注,但它至少把原本分散在文档和人为约定里的安全前提,移动到了更可讨论、更可静态表达的层面。
这类帖子未必会直接变成标准库 API,但它很适合 Rust 社区持续讨论,因为它正好踩在语言设计、异步运行时和安全边界三者交界处。对写库的人来说,文章最大的价值不一定是照搬 MustCompleteFuture,而是它把“future 可以被忘掉”这个经常被忽视的事实,再次拖回了 scoped async 设计的正中央。
原文链接:https://old.reddit.com/r/rust/comments/1uzfv30/a_solution_to_the_scoped_task_trilemma/
评论区
写评论还没有评论