在 Swift 里写出 3 纳秒的 IPv4 解析器
来源:blog.mahdibm.com — 2026-10-18
📋 概述
文章开篇先澄清:这不是 AI 生成的博客。背景是 Swift 至今没有统一、高质量的跨平台基础网络类型栈,实现散落在 Apple 专有的 Network 框架与开源的 swift-nio 中,作者因此在做 swift-endpoint,其中一部分将进入 Swift 新的 swift-networking-types,对标 Go 的 net/netip、Rust 的 std::net 与 Boost Asio 的一部分。既有高性能实现对 Swift 并不合用:很多依赖第 8 个空字节、要求 16 字节前置填充或契约,而 Swift 里空字节只在 C/C++ 互操作中存在;另一些依赖查找表,而基准测试常假设 L1 缓存已热;SIMD 方案又因 CPU 代际差异牺牲可移植性。他的算法有两个支点:一是并行性——虽然不知道每段的确切位置,但知道范围,第一个段必在前 4 个字符内,第四段从末尾往前也必在 4 个字符内,于是四次 UInt32 载入加一次与 0x30 掩码的 XOR 就把 ASCII 数字变成 0x00–0x09、把点变成 0x1E;二是 SWAR,把多个 UInt8 塞进一个 UInt64 一次算完。再进一步优化:找三个点只需三个窗口,第三段可以在已知第二、三个点位置后从内存里读一个更窄的窗口。
🔑 核心要点
- 性能目标写在标题里:在 M6 上约 3 纳秒、15 个 CPU 周期、75 条指令完成一次 IPv4 解析
- 两个技术支点:让 CPU 在同一周期跑多条独立指令,以及用 SWAR 把多个 UInt8 塞进一个 UInt64 一次算完
- 位置约束带来了并行:首段必在索引 0–2,末段从末尾倒数也必在 0–2,中间两段则在 2–6,于是四次 UInt32 载入即可覆盖全部数字
- XOR 0x30 是整套把戏的关键:ASCII 数字 0x30–0x39 被压成 0x00–0x09,而点号 0x2E 变成 0x1E,只有点号保留第 5 位,于是点与数字一眼可分
- 为了避开查找表与 16 字节填充契约,算法只依赖 7 字节最短长度这一事实,让性能不依赖缓存状态
- 作者对既有生态很克制:明确承认 Daniel Lemire 与 Wojciech Muła 等前人的位技巧与向量化研究是基础
💡 金句
I know what you're trying to figure out. No. This blog post is not AI generated.
👍 0
👎 0
← 返回 Lobsters 首页