快速解析IP地址:便携式方案与AI辅助优化

本文探讨了如何在不依赖SIMD等复杂技术的情况下,用C++高效、可移植地解析IPv4地址。通过手动实现、AI辅助优化以及标准库方案对比,并提供了多平台性能基准测试数据。

快速解析IP地址(便携式,无需SIMD魔法)

大多数程序员都熟悉IP地址。它们由四个介于0到255之间的数字组成,以点分隔:例如 192.168.0.1。从某种意义上说,这是一种表示32位整数的复杂方式。现代版本的IP地址是IPv6,通常用方括号包围,但根据我的经验,它不太常见。

使用花哨的技术,你可以用少至50条指令来解析IP地址。但这有点复杂,且不一定可移植。如果你想要高速度,但又不想做太多工作或使用专门的库呢?你可以尝试自己编写。但既然我是一个文明程序员,我直接让我最喜欢的AI为我写。

 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
32
33
34
// 解析从'p'开始的IPv4地址。
// p : 起始指针,pend: 字符串的末尾
std::expected<uint32_t, parse_error> parse_manual(const char *p, const char *pend) {
    uint32_t ip = 0;
    int octets = 0;
    while (p < pend && octets < 4) {
        uint32_t val = 0;
        const char *start = p;
        while (p < pend && *p >= '0' && *p <= '9') {
            val = val * 10 + (*p - '0');
            if (val > 255) {
                return std::unexpected(invalid_format);
            }
            p++;
        }
        if (p == start || (p - start > 1 && *start == '0')) {
            return std::unexpected(invalid_format);
        }
        ip = (ip << 8) | val;
        octets++;

        if (octets < 4) {
            if (p == pend || *p != '.') {
                return std::unexpected(invalid_format);
            }
            p++; // 跳过点
        }
    }
    if (octets == 4 && p == pend) {
        return ip;
    } else {
        return std::unexpected(invalid_format);
    }
}

我立刻明白这个函数没有达到可能的最快速度。然后我让AI利用每个数字由一到三位组成的事实来改进结果。我得到了以下合理的函数。

 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
32
33
34
35
36
37
std::expected<uint32_t, parse_error> parse_manual_unrolled(const char *p, const char *pend) {
    uint32_t ip = 0;
    int octets = 0;
    while (p < pend && octets < 4) {
        uint32_t val = 0;
        if (p < pend && *p >= '0' && *p <= '9') {
            val = (*p++ - '0');
            if (p < pend && *p >= '0' && *p <= '9') {
                if (val == 0) { 
                  return std::unexpected(invalid_format);
                }
                val = val * 10 + (*p++ - '0');
                if (p < pend && *p >= '0' && *p <= '9') {
                    val = val * 10 + (*p++ - '0');
                    if (val > 255) { 
                      return std::unexpected(invalid_format);
                    }
                }
            }
        } else {
            return std::unexpected(parse_error::invalid_format);
        }
        ip = (ip << 8) | val;
        octets++;
        if (octets < 4) {
            if (p == pend || *p != '.') {
              return std::unexpected(invalid_format);
            }
            p++; // 跳过点
        }
    }
    if (octets == 4 && p == pend) {
        return ip;
    } else {
        return std::unexpected(invalid_format);
    }
}

AI干得漂亮! 在C++中,我们有解析数字的标准函数(std::from_chars),它可以显著简化代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
std::expected<uint32_t, parse_error> parse_ip(const char *p, const char *pend) {
  const char *current = p;
  uint32_t ip = 0;
  for (int i = 0; i < 4; ++i) {
    uint8_t value;
    auto r = std::from_chars(current, pend, value);
    if (r.ec != std::errc()) {
      return std::unexpected(invalid_format);
    }
    current = r.ptr;
    ip = (ip << 8) | value;
    if (i < 3) {
      if (current == pend || *current++ != '.') {
        return std::unexpected(invalid_format);
      }
    }
  }
  return ip;
}

你也可以使用 fast_float 库作为 std::from_chars 的替代品。最新版本的 fast_float 得益于 Shikhar Soni(以及 Pavel Novikov 的修复)的贡献,实现了更快的8位整数解析。

我为这个问题编写了一个基准测试。让我们首先看看在 Apple M4 处理器(4.5 GHz)上使用 LLVM 17 的结果。

函数 指令数/IP 纳秒/IP
manual 185 6.2
manual (unrolled) 114 3.3
from_chars 381 14
fast_float 181 7.2

让我们用 GCC 12 和 Intel Ice Lake 处理器(3.2 GHz)试试。

函数 指令数/IP 纳秒/IP
manual 219 30
manual (unrolled) 154 24
from_chars 220 29
fast_float 211 18

最后,让我们用中国的龙芯 3A6000 处理器(2.5 GHz)和 LLVM 21 试试。

函数 指令数/IP 纳秒/IP
manual 187 29
manual (unrolled) 109 21
from_chars 191 39
fast_float 193 27

对 fast_float 库的优化工作取得了成效。这种差异在 x64 处理器上尤其明显。 在我这个小实验中,另一个有趣的点是,我能够以相对较少的个人努力就让AI生成了更快的代码。我确实需要“引导”AI。这是否意味着我可以退休了?还没到那一步。但我很高兴我能更快地获得良好的参考基线,这使我能够更好地将工作重点放在关键之处。

参考:fast_float C++ 库是一个快速数字解析库,是 GCC 和主要网络浏览器的一部分。 Daniel Lemire, “Parsing IP addresses quickly (portably, without SIMD magic),” in Daniel Lemire’s blog, December 27, 2025, https://lemire.me/blog/2025/12/27/parsing-ip-addresses-quickly-portably-without-simd-magic/.

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计