C++ 从字符串获取IPv4地址的最快方法
我有以下代码,大约比inet_addr快7倍。我想知道是否有一种方法可以改进它,使它更快,或者是否有一种更快的替代方法 这段代码要求提供一个有效的以null结尾的IPv4地址,并且不带空格,在我的例子中总是这样,所以我针对这种情况进行了优化。通常,您会有更多的错误检查,但是如果有一种方法可以使下面的操作更快,或者有一种更快的替代方法,我将非常感激C++ 从字符串获取IPv4地址的最快方法,c++,c,string,optimization,ip-address,C++,C,String,Optimization,Ip Address,我有以下代码,大约比inet_addr快7倍。我想知道是否有一种方法可以改进它,使它更快,或者是否有一种更快的替代方法 这段代码要求提供一个有效的以null结尾的IPv4地址,并且不带空格,在我的例子中总是这样,所以我针对这种情况进行了优化。通常,您会有更多的错误检查,但是如果有一种方法可以使下面的操作更快,或者有一种更快的替代方法,我将非常感激 UINT32 GetIP(const char *p) { UINT32 dwIP=0,dwIP_Part=0; while(true
UINT32 GetIP(const char *p)
{
UINT32 dwIP=0,dwIP_Part=0;
while(true)
{
if(p[0] == 0)
{
dwIP = (dwIP << 8) | dwIP_Part;
break;
}
if(p[0]=='.')
{
dwIP = (dwIP << 8) | dwIP_Part;
dwIP_Part = 0;
p++;
}
dwIP_Part = (dwIP_Part*10)+(p[0]-'0');
p++;
}
return dwIP;
}
UINT32 GetIP(const char*p)
{
UINT32 dwIP=0,dwIP_部分=0;
while(true)
{
if(p[0]==0)
{
dwIP=(dwIP因为我们谈论的是最大化IP地址解析的吞吐量,所以我建议使用向量化解决方案
以下是x86特定的fast解决方案(需要SSE4.1,对于穷人,至少需要SSSE3):
\uuum128i shuffleTable[65536];//可以减少256倍,请参阅@iwillnotexistidnotexist
UINT32 MyGetIP(常量字符*str){
__m128i输入=_mm_lddqu_si128((常量m128i*)str);/“192.167.1.3”
输入=_mm_sub_epi8(输入,_mm_set1_epi8('0'));/1 9 2 254 1 6 7 254 1 254 3 208 245 0 8 40
__m128i cmp=输入;/…X…X.X.XX…(符号)
UINT32掩码=_mm_movemask_epi8(cmp);//6792-魔法索引
__m128i shuf=shuffleTable[mask];//10-1-1-18-1-1-1654-1120-1
__m128i arr=_mm_shuffle_epi8(输入,shuf);/3 0 0 0 0 0 0 0 0 0 0 7 6 1 0 0 2 9 1 0
__m128i系数=_mm_set_epi8(0,100,10,1,0,100,10,1,0,100,10,1,0,100,10,1,100,10,1);
__m128i prod=_mm_maddubs_epi16(系数,arr);/3 0 | 1 0 | 67 100 | 92 100
prod=_mm_hadd_epi16(prod,prod);/3 | 1 | 167 | 192 |?|?|?|?
__m128i imm=_mm_set_epi8(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,6,4,2,0);
prod=_mm_shuffle_epi8(prod,imm);//31167192 0 0 0 0 0 0 0
返回_mm_extract_epi32(prod,0);
//返回(UINT32(_-mm_-extract_-epi16(prod,1))作为备选方案:这与您的类似,但有一些错误检查:
#include <iostream>
#include <string>
#include <cstdint>
uint32_t getip(const std::string &sip)
{
uint32_t r=0, b, p=0, c=0;
const char *s;
s = sip.c_str();
while (*s)
{
r<<=8;
b=0;
while (*s&&((*s==' ')||(*s=='\t'))) s++;
while (*s)
{
if ((*s==' ')||(*s=='\t')) { while (*s&&((*s==' ')||(*s=='\t'))) s++; if (*s!='.') break; }
if (*s=='.') { p++; s++; break; }
if ((*s>='0')&&(*s<='9'))
{
b*=10;
b+=(*s-'0');
s++;
}
}
if ((b>255)||(*s=='.')) return 0;
r+=b;
c++;
}
return ((c==4)&&(p==3))?r:0;
}
void testip(const std::string &sip)
{
uint32_t nIP=0;
nIP = getip(sip);
std::cout << "\nsIP = " << sip << " --> " << std::hex << nIP << "\n";
}
int main()
{
testip("192.167.1.3");
testip("292.167.1.3");
testip("192.267.1.3");
testip("192.167.1000.3");
testip("192.167.1.300");
testip("192.167.1.");
testip("192.167.1");
testip("192.167..1");
testip("192.167.1.3.");
testip("192.1 67.1.3.");
testip("192 . 167 . 1 . 3");
testip(" 192 . 167 . 1 . 3 ");
return 0;
}
#包括
#包括
#包括
uint32_t getip(常量标准::字符串和sip)
{
uint32_t r=0,b,p=0,c=0;
常量字符*s;
s=sip.c_str();
而(*s)
{
我认为这更适合codereview.stackexchange.comWhy?我想知道从字符串中获取IP地址的最快方法是什么。您还应该注意,如果不将Endianness调整为网络字节顺序,UINT32可能不适合IP地址。@Harry codereview专门检查工作代码(您的代码工作正常),他们可以提出算法改进建议。这并不是说速度黑客不在这里讨论的主题。@Iwillnotexist Idonotexist我想如果我的代码确实是目前世界上最快的,那么可能值得去codereview看看它是否能更快?;)我在这里看到过更快的整数转换问题,我认为这与这些类型的问题有关。我自己在这个主题上找不到任何其他问题,如果没有其他选择,它可能会在将来帮助其他人。@PeterCordespmovskb
指令保证零填充整个寄存器(r32
或r64
)它被要求爆炸。不幸的是,\u mm\u movemask\u epi8()
和底层(在GCC上)\u内置的ia32\u pmovskb128()
intrinsic都返回int
,因此编译器迫切希望使用pmovmkb r32,xmm
表单,而不是本应获利的pmovmkb r64,xmm
表单。然后,编译器感觉需要对extend进行签名,因为返回值名义上是int
。我也注意到了这一点,并尝试了to抑制它,但意识到问题是根深蒂固的。@Harry您必须绝对使该表消失,或者您是否能够使其更小(例如,256个条目x 16个字节/条目?)如果是这样的话,可以修改stgatilov的代码,用一个完美的散列提取81个可能的有效数字掩码。我散列的一个秘密技巧是使用单个SSE4.2指令CRC,可以与\u mm\u crc32.*()
一起使用。对于这个用例,在掩码=\u mm\u crc32\u u16(0,mask>(32-n)中找到a
和n
;
这样您就有了2^n
个容器,并且在散列之后没有任何有效的掩码发生冲突。您可能应该能够得到n=8(256个条目)。@EvgenyKluev I成功地制作了一个256个容器的完美散列变体,它是stgatilov的优秀开端。(警告,使用了与GCC一致的属性)。它的速度只有原来的一半多,但LUT占用的空间却少了256倍。先生们,我刚刚通过在完美哈希中使用不同的魔法乘法常数成功地删除了crc
指令。因此,它甚至不需要SSE4.2,而且速度稍快。目前,在我的i7-4700MQ处理器上,时间资源:stgatilov@0.465
,我自己0.645
和哈里2.996
秒。空间资源:stgatilov@1MB,我自己@4KB,哈里@wherry@微不足道。因此,我的速度慢了40%,但占用了0.4%的内存,我的哈希表负载因子是81/256~31.6%,而不是81/65536~0.12%。@哈里:我没有测试它,但我预计如果我们完成的话用计算代替查找,这将使最快的算法速度降低2到3倍。但它可能比您的版本快得多,因为许多计算是并行完成的。Skylake体系结构即将推出,它允许对lzcnt
指令进行矢量化(使用AVX512指令集)因此,几乎所有的计算都可以并行进行;但它不太可能比这里提到的两种方法都好。与我最初的方法相比,你的速度是多少?@Harry:我没有做(没有数据)特定的批量速度测试;反正只与特定的批量速度相关
lddqu xmm1, XMMWORD PTR [rcx]
psubb xmm1, xmm6
pmovmskb ecx, xmm1
mov ecx, ecx //useless, see @PeterCordes and @IwillnotexistIdonotexist
add rcx, rcx //can be removed, see @EvgenyKluev
pshufb xmm1, XMMWORD PTR [r13+rcx*8]
movdqa xmm0, xmm8
pmaddubsw xmm0, xmm1
phaddw xmm0, xmm0
pshufb xmm0, xmm7
pextrd eax, xmm0, 0
#include <iostream>
#include <string>
#include <cstdint>
uint32_t getip(const std::string &sip)
{
uint32_t r=0, b, p=0, c=0;
const char *s;
s = sip.c_str();
while (*s)
{
r<<=8;
b=0;
while (*s&&((*s==' ')||(*s=='\t'))) s++;
while (*s)
{
if ((*s==' ')||(*s=='\t')) { while (*s&&((*s==' ')||(*s=='\t'))) s++; if (*s!='.') break; }
if (*s=='.') { p++; s++; break; }
if ((*s>='0')&&(*s<='9'))
{
b*=10;
b+=(*s-'0');
s++;
}
}
if ((b>255)||(*s=='.')) return 0;
r+=b;
c++;
}
return ((c==4)&&(p==3))?r:0;
}
void testip(const std::string &sip)
{
uint32_t nIP=0;
nIP = getip(sip);
std::cout << "\nsIP = " << sip << " --> " << std::hex << nIP << "\n";
}
int main()
{
testip("192.167.1.3");
testip("292.167.1.3");
testip("192.267.1.3");
testip("192.167.1000.3");
testip("192.167.1.300");
testip("192.167.1.");
testip("192.167.1");
testip("192.167..1");
testip("192.167.1.3.");
testip("192.1 67.1.3.");
testip("192 . 167 . 1 . 3");
testip(" 192 . 167 . 1 . 3 ");
return 0;
}