将unsigned char数组转换为IP字符串的最快方法是什么?

时间:2015-12-17 12:08:07

标签: c++

我需要(或多或少)实时处理大量这些。我正在使用的方法不再削减它了。

std::string parse_ipv4_address( const std::vector<unsigned char> & data, int start )
{
    char ip_addr[16];
    snprintf( ip_addr, sizeof(ip_addr), "%d.%d.%d.%d", 
        data[start + 0], data[start + 1], data[start + 2], data[start + 3] );
    return std::string( ip_addr ); 
}

// used like this
std::vector<unsigned char> ip = { 0xc0, 0xa8, 0x20, 0x0c };
std::string ip_address = parse_ipv4_address( ip, 0 );

std::cout << ip_address << std::endl; // not actually printed in real code 
// produces 192.168.32.12

有更快的方法吗?怎么样?

请注意!性能是这里的问题,因此this issue不是重复的。

4 个答案:

答案 0 :(得分:5)

以下是影响绩效的潜在候选人:

  • snprintf需要解析格式字符串,并执行错误处理。要么花费时间,要么实现你不需要的功能。
  • 返回时构造std::string对象的代价很​​高。它将受控序列存储在freestore内存中(通常实现为堆内存),这在C ++(和C)中分配成本有点高。
  • 使用std::vector存储4字节值会不必要地占用资源。它也在freestore中分配内存。将其替换为char[4]或32位整数(uint32_t)。

由于您不需要printf - 函数族的多功能性,您可以完全放弃它,并使用查找表。查找表由256个条目组成,每个条目保存字节值0到255的可视化表示。为了优化它,让LUT包含尾随的.字符。 (需要特别注意,以解决字节序。我在这里假设小端。)

解决方案可能看起来像 1)

const uint32_t mapping[] = { 0x2E303030, // "000."
    0x2E313030, // "001."
    // ...
    0x2E343532, // "254."
    0x2E353532  // "255."
};

alignas(uint32_t) char ip_addr[16];
uint32_t* p = reinterpret_cast<uint32_t*>(&ip_addr[0]);
p[0] = mapping[data[0]];
p[1] = mapping[data[1]];
p[2] = mapping[data[2]];
p[3] = mapping[data[3]];

// Zero-terminate string (overwriting the superfluous trailing .-character)
ip_addr[15] = '\0';

// As a final step, waste all the hard earned savings by constructing a std::string.
// (as an ironic twist, let's pick the c'tor with the best performance)
return std::string(&ip_addr[0], &ip_addr[15]);

// A more serious approach would either return the array (ip_addr), or have the caller
// pass in a pre-allocated array for output.
return ip_addr;

<小时/> 1) 免责声明:从char*转换为uint32_t*技术上展示未定义的行为。 请勿使用,除非您的平台(编译器和操作系统)提供额外的保证,以便明确定义。

答案 1 :(得分:2)

三个 四个答案,价格为一个。

首先,确实,确定你正在优化正确的部分。 std::vectorstd::string创建都涉及内存分配,cout <<可能涉及文件访问,图形等!

第二:不要使用向量来表示IP地址的4字节。只需使用char ip[4],甚至是32位整数

第三:我猜你没有处理完全随机的IP地址。可能是几百或几千个不同的地址?在这种情况下,使用std::map<INT32, std::string>作为缓存,只需根据需要从缓存中提取所需的缓存,然后根据需要编写新的缓存。如果缓存太大,只需将其清空并重新开始......

<小时/> 第四:考虑用十六进制虚线四边形表示法编写IP地址。像inet_addr()之类的调用仍然可以接受这一点,它有几个优点:所有字段都是固定宽度,只有8个'字符'可以更新,二进制到十六进制转换通常比二进制到十进制更快。 https://en.wikipedia.org/wiki/IPv4#Address_representations

答案 2 :(得分:0)

您可以使用查找表,其中包含0到255之间的数字字符串。 如果速度非常重要,您还可以使用内联关键字或函数宏。 你也可以查看sse说明。

顺便说一下,通常代码越原始越快。我会使用unsigned char数组而不是vector,char数组而不是字符串,memcpy(甚至直接逐字节复制)而不是sprintf。

答案 3 :(得分:0)

您在这里...

    std::string IP_parse(unsigned char data[4])
    {
            std::string parsedString = "";
            snprintf((char*)parsedString.c_str(), sizeof(char[15]), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]);
            return parsedString;
    }