校验和快速计算方法
2014-12-15 17:16
176 查看
先将代码贴上
RFC规定的checksum的计算方法是对每两个字节当做一个数进行计算,出现进位则加到低位上。
此处的代码优化有两个点:
1 交叉使用变量,以便节省装载延迟导致CPU等待
2 一次加法完成两对16字节数据相加,低16位进位则先加到高16位上,最终高16位也会加回低16位;为了防止高16位溢出,使用了uint64_t类型以便记录溢出
优化的第二条借鉴自Cavium的代码,不过可惜的是其代码由于使用非对齐加载以及未使用第一条优化,并且判断条件过多等,导致其性能严重低下。
优化后的代码性能基本上达到,cycle_num=len/2(不考虑cachemiss),也就是说一个1500的数据包大概只需要不到800个cycles就能够完成checksum计算(CPU1GHZ);
当然此代码也存在限制,那就是pstart指针至少需要4字节对齐,这就是为什么将其类型写成uint32_t*的原因。若不对齐,轻者严重影响效率;重者CPU出错。
一般来说,buff的首地址至少4字节对齐,计算checksum时,数据应该都已经在buff中装配好了,假设说buff中的开始数据是MAC头,接着是IP头,再跟着TCP头,那么只需要将
checksum=0
替换成
checksum=*(uint16_t*)pstart;
pstart=(uint32_t*)((uint16_t*)pstart+1);
len-=2;
即可直接将IP的payload首地址直接传进更正之后的函数,这是因为不管是EthernetII还是802.3定义的MAC头都满足4n+2字节,甚至VLAN字段数据长度也是4的倍数。
在含有PREFETCH指令的系统,值得将一次处理16个字节改成32个字节,以便加入PREFETCH指令而不会产生比较大的性能损失。
最初接触第二条优化方式,是在看<高效程序的奥秘>时;查看Linux内核时,发现其checksum计算方法也是使用此技巧。
uint16_tcal_checksum(uint32_t*pstart,uint16_tlen) { uint64_tchecksum; uint32_t*pend; uint32_tv0,v1,v2,v3,v4; checksum=0; pend=(uint32_t*)((char*)pstart+(len&(~0xF))); while(pstart<pend) { v0=*pstart; v1=*(pstart+1); v2=*(pstart+2); v3=*(pstart+3); checksum+=v0; checksum+=v1; checksum+=v2; checksum+=v3; pstart+=4; } len=len&0xF; pend=(uint32_t*)((char*)pstart+(len&(0xF))); while(pstart<pend) { v0=(uint32_t)(*(uint16_t*)pstart); v1=(uint32_t)(*((uint16_t*)pstart+1)); pstart+=1; checksum+=v0; checksum+=v1; } switch(len&0x3) { case3: v0=(uint32_t)(*(uint16_t*)pstart); v1=((uint32_t)(*((uint8_t*)pstart+2))<<8); checksum+=v0; checksum+=v1; break; case2: v0=(uint32_t)(*(uint16_t*)pstart); checksum+=v0; break; case1: v0=((uint32_t)(*((uint8_t*)pstart+2))<<8); checksum+=v0; default: break; } checksum=(checksum>>32)+(checksum&0xFFFFFFFF); checksum=(checksum>>32)+(checksum&0xFFFFFFFF); checksum=(checksum>>16)+(checksum&0xFFFF); checksum=(checksum>>16)+(checksum&0xFFFF); returnchecksum^0xFFFF; }
RFC规定的checksum的计算方法是对每两个字节当做一个数进行计算,出现进位则加到低位上。
此处的代码优化有两个点:
1 交叉使用变量,以便节省装载延迟导致CPU等待
2 一次加法完成两对16字节数据相加,低16位进位则先加到高16位上,最终高16位也会加回低16位;为了防止高16位溢出,使用了uint64_t类型以便记录溢出
优化的第二条借鉴自Cavium的代码,不过可惜的是其代码由于使用非对齐加载以及未使用第一条优化,并且判断条件过多等,导致其性能严重低下。
优化后的代码性能基本上达到,cycle_num=len/2(不考虑cachemiss),也就是说一个1500的数据包大概只需要不到800个cycles就能够完成checksum计算(CPU1GHZ);
当然此代码也存在限制,那就是pstart指针至少需要4字节对齐,这就是为什么将其类型写成uint32_t*的原因。若不对齐,轻者严重影响效率;重者CPU出错。
一般来说,buff的首地址至少4字节对齐,计算checksum时,数据应该都已经在buff中装配好了,假设说buff中的开始数据是MAC头,接着是IP头,再跟着TCP头,那么只需要将
checksum=0
替换成
checksum=*(uint16_t*)pstart;
pstart=(uint32_t*)((uint16_t*)pstart+1);
len-=2;
即可直接将IP的payload首地址直接传进更正之后的函数,这是因为不管是EthernetII还是802.3定义的MAC头都满足4n+2字节,甚至VLAN字段数据长度也是4的倍数。
在含有PREFETCH指令的系统,值得将一次处理16个字节改成32个字节,以便加入PREFETCH指令而不会产生比较大的性能损失。
最初接触第二条优化方式,是在看<高效程序的奥秘>时;查看Linux内核时,发现其checksum计算方法也是使用此技巧。
相关文章推荐
- 技巧:子网掩码的快速计算方法
- 快速计算子网掩码的方法以及工具(3个,附件中)
- Haar-like矩形特征的特征值的快速计算方法
- 用最简单,最快速的方法计算出下面这个圆形是否和正方形相交
- 极限优化:Haar特征的另一种的快速计算方法—boxfilter
- sqlserver大数据量计算行数的快速方法
- Haar-like矩形特征的特征值的快速计算方法
- 23.用最简单,最快速的方法计算出下面这个圆形是否和正方形相交
- Haar-like矩形特征的特征值的快速计算方法
- 算法习题23:用最简单, 最快速的方法计算出下面这个圆形是否和正方形相交
- 网络的快速计算方法
- Haar-like矩形特征的特征值的快速计算方法
- CRC循环冗余错误校验计算方法 (硬件实现)
- 极限优化:Haar特征的另一种的快速计算方法—boxfilter
- 超强快速计算方法
- 阶乘数的快速计算方法
- CRC循环冗余错误校验计算方法 (硬件实现)
- 一种写程序快速计算常系数线性齐次递推关系的指定项的方法
- 极限优化:Haar特征的另一种的快速计算方法—boxfilter
- 子网掩码的快速计算方法