读书人

比常规CRC32C算法快12倍的优化.解决方

发布时间: 2012-02-06 15:52:45 作者: rapoo

比常规CRC32C算法快12倍的优化.
CRC-32C (Castagnoli) 算法是 iSCSI 和SCTP 数据校验的算法,和常用CRC-32-IEEE 802.3算法所不同的是多项式常数CRC32C是0x1EDC6F41 ,CRC32是0x04C11DB7 ,也就是说由此生成的CRC表不同外算法是一模一样.



CRC32 常规算法如下:

Delphi(Pascal) code
function _CRC32CX86(Data: PByte; aLength: Integer): DWORD;   const    _CRC32CTable: array[0..255] of DWORD = (       $00000000, $F26B8303, $E13B70F7, $1350F3F4, //CRC32C Table       $C79A971F, $35F1141C, $26A1E7E8, $D4CA64EB,       $8AD958CF, $78B2DBCC, $6BE22838, $9989AB3B,       $4D43CFD0, $BF284CD3, $AC78BF27, $5E133C24,       $105EC76F, $E235446C, $F165B798, $030E349B,       $D7C45070, $25AFD373, $36FF2087, $C494A384,       $9A879FA0, $68EC1CA3, $7BBCEF57, $89D76C54,       $5D1D08BF, $AF768BBC, $BC267848, $4E4DFB4B,       $20BD8EDE, $D2D60DDD, $C186FE29, $33ED7D2A,       $E72719C1, $154C9AC2, $061C6936, $F477EA35,       $AA64D611, $580F5512, $4B5FA6E6, $B93425E5,       $6DFE410E, $9F95C20D, $8CC531F9, $7EAEB2FA,       $30E349B1, $C288CAB2, $D1D83946, $23B3BA45,       $F779DEAE, $05125DAD, $1642AE59, $E4292D5A,       $BA3A117E, $4851927D, $5B016189, $A96AE28A,       $7DA08661, $8FCB0562, $9C9BF696, $6EF07595,       $417B1DBC, $B3109EBF, $A0406D4B, $522BEE48,       $86E18AA3, $748A09A0, $67DAFA54, $95B17957,       $CBA24573, $39C9C670, $2A993584, $D8F2B687,       $0C38D26C, $FE53516F, $ED03A29B, $1F682198,       $5125DAD3, $A34E59D0, $B01EAA24, $42752927,       $96BF4DCC, $64D4CECF, $77843D3B, $85EFBE38,       $DBFC821C, $2997011F, $3AC7F2EB, $C8AC71E8,       $1C661503, $EE0D9600, $FD5D65F4, $0F36E6F7,       $61C69362, $93AD1061, $80FDE395, $72966096,       $A65C047D, $5437877E, $4767748A, $B50CF789,       $EB1FCBAD, $197448AE, $0A24BB5A, $F84F3859,       $2C855CB2, $DEEEDFB1, $CDBE2C45, $3FD5AF46,       $7198540D, $83F3D70E, $90A324FA, $62C8A7F9,       $B602C312, $44694011, $5739B3E5, $A55230E6,       $FB410CC2, $092A8FC1, $1A7A7C35, $E811FF36,       $3CDB9BDD, $CEB018DE, $DDE0EB2A, $2F8B6829,       $82F63B78, $709DB87B, $63CD4B8F, $91A6C88C,       $456CAC67, $B7072F64, $A457DC90, $563C5F93,       $082F63B7, $FA44E0B4, $E9141340, $1B7F9043,       $CFB5F4A8, $3DDE77AB, $2E8E845F, $DCE5075C,       $92A8FC17, $60C37F14, $73938CE0, $81F80FE3,       $55326B08, $A759E80B, $B4091BFF, $466298FC,       $1871A4D8, $EA1A27DB, $F94AD42F, $0B21572C,       $DFEB33C7, $2D80B0C4, $3ED04330, $CCBBC033,       $A24BB5A6, $502036A5, $4370C551, $B11B4652,       $65D122B9, $97BAA1BA, $84EA524E, $7681D14D,       $2892ED69, $DAF96E6A, $C9A99D9E, $3BC21E9D,       $EF087A76, $1D63F975, $0E330A81, $FC588982,       $B21572C9, $407EF1CA, $532E023E, $A145813D,       $758FE5D6, $87E466D5, $94B49521, $66DF1622,       $38CC2A06, $CAA7A905, $D9F75AF1, $2B9CD9F2,       $FF56BD19, $0D3D3E1A, $1E6DCDEE, $EC064EED,       $C38D26C4, $31E6A5C7, $22B65633, $D0DDD530,       $0417B1DB, $F67C32D8, $E52CC12C, $1747422F,       $49547E0B, $BB3FFD08, $A86F0EFC, $5A048DFF,       $8ECEE914, $7CA56A17, $6FF599E3, $9D9E1AE0,       $D3D3E1AB, $21B862A8, $32E8915C, $C083125F,       $144976B4, $E622F5B7, $F5720643, $07198540,       $590AB964, $AB613A67, $B831C993, $4A5A4A90,       $9E902E7B, $6CFBAD78, $7FAB5E8C, $8DC0DD8F,       $E330A81A, $115B2B19, $020BD8ED, $F0605BEE,       $24AA3F05, $D6C1BC06, $C5914FF2, $37FACCF1,       $69E9F0D5, $9B8273D6, $88D28022, $7AB90321,       $AE7367CA, $5C18E4C9, $4F48173D, $BD23943E,       $F36E6F75, $0105EC76, $12551F82, $E03E9C81,       $34F4F86A, $C69F7B69, $D5CF889D, $27A40B9E,       $79B737BA, $8BDCB4B9, $988C474D, $6AE7C44E,       $BE2DA0A5, $4C4623A6, $5F16D052, $AD7D5351);   var    i: Integer;   begin    Result := $FFFFFFFF;     for I := 0 to aLength - 1 do    begin      Result := (Result shr 8) xor _CRC32CTable[(Result and $FF) xor Data^];       Inc(Data);     end;     Result := not Result;   end;  



CRC32C使用SSE4.2硬件指令优化算法部分代码如下:

Delphi(Pascal) code

function _CRC32CSSE(Data: PByte; aLength: Integer): DWORD;            asm push esi push edx push ecx mov esi,eax mov eax,$FFFFFFFF test edx,edx jz @Exit  test esi,esi jz @Exit  mov ecx,edx shr ecx, 2      test ecx,ecx jz @Exit xor edx,edx@Alignment: crc32 eax,[edx*4+esi] inc edx cmp edx,ecx jb @Alignment@Exit: not eax pop ecx pop edx pop esiend; 


以上2个不同实现方式在Intel Core i7 720QM 1.60GHz CPU上测试成绩如下:

(数据采用随机算法生成,1M*100表示使用1M数据进行100次重复计算,数据量相当于100M)

--------------------------------------------------

| 数据量 | 常规算法时间 | 优化算法时间 | 快出百分比 |

--------------------------------------------------

| 1M *100 | X86 Time:390ms | SSE Time:32ms | 1218% |

--------------------------------------------------

| 4M *100 | X86 Time:1575ms | SSE Time:156ms | 1009% |

--------------------------------------------------

| 8M *100 | X86 Time:3136ms | SSE Time:280ms | 1120% |

--------------------------------------------------

| 32M *100 | X86 Time:12542ms | SSE Time:1092ms | 1148% |

--------------------------------------------------

通过对比可以清楚的看到使用SSE4.2中的新指令crc32可以比常规CRC32C算法要快出最少10倍的效率,Intel新增的指令确实对常规某些算法提供了高效的解决方案,使用好它们将对我们在以后的开发中得到质的提升。



[解决办法]
MARK...
[解决办法]
建议你也编写个DEMO程序,也传上来吧谢谢
[解决办法]
先Mark,再看
[解决办法]
mark 一下,偏听则暗兼听则明,听听其他高手是啥意见~~
[解决办法]
支持研究性能优化,不过建议还是在楼主“使用1M数据进行100次重复计算”的基础上,使用非重复的100M,甚至1G数据(随机生成即可)进行运算,这样更接近于真实环境。毕竟重复计算难免多少从某些缓冲机制中受益的。
[解决办法]
@Alignment:
crc32 eax,[edx*4+esi]
inc edx
cmp edx,ecx
jb @Alignment

这里面的 crc32 eax,[edx*4+esi] 这一句应该是硬件指令吧,

for I := 0 to aLength - 1 do
begin
Result := (Result shr 8) xor _CRC32CTable[(Result and $FF) xor Data^];
Inc(Data);
end;

分析一下这句,For 循环一个计数器 一个跳转指令,
一个shr 一个xor 一个Xor 两个寻址,一个ADD
本身也没有多少时钟周期,

硬件指令中同样需要循环和寻址。

应该跟具体算法关系不大,虽然结果看起来是快了十倍。
[解决办法]
楼主试下这个函数呢,希望差距小点点,我这CPU不支持
function _CRC32CX86(Data: Pointer; Size: Integer): DWORD;
asm
PUSH EBX;
MOV ECX , EDX;
MOV EDX , EAX;
MOV EAX , $FFFFFFFF;
@@Loop:
XOR EBX , EBX
MOV BL , AL;
XOR BL , BYTE PTR [EDX];
SHR EAX , 8;
XOR EAX , DWORD PTR _CRC32Table + EBX*4;
INC EDX;
Loop @@Loop
NOT EAX;
POP EBX;
end;

[解决办法]
lz的机器不错。。。
sse4.2要新机器 Nehalem 架构以后的才支持。

[解决办法]
用CRC32指令是快,但是结果与标准CRC32算法不一致,所以只能程序中自己用,不能用于校验其他程序-比如RAR-生成的校验码。

另外,这个_CRC32CSSE有BUG,只能验算4的倍数长度的数据,否则结果是错误的。先改正了再说性能吧。

------解决方案--------------------


嗯 ,确实比CRC16,CRC32代码快多了! 学习!
[解决办法]
地址搞死我了。

procedure TForm2.Button1Click(Sender: TObject);
var
Data: array [0 .. 1023] of Byte;
Data2: array of Byte;
Pd: PByte;

Fs: TFileStream;
begin
try
// demo1
Pd := @Data;
FillChar(Data, 1024, 1);
ShowMessage(IntToStr(_CRC32CX86(Pd, 1024)));

// demo2
Fs := TFileStream.Create('E:\iTAS\Source\Lib\Common\access.txt', FmOpenRead);
SetLength(Data2, 1024);
Pd := @Data2[0];
Fs.Position := 0;
Fs.Read(Pd^, Fs.Size); // 动态数组指针,可以得到数据
// Fs.Read(Data2, Fs.Size); // 动态数组,得不到数据
// Fs.Read(Data, Fs.Size); // 静态数组,可以得到数据
// 结论:
// 动态数组的首地址:@DynamicArray[0];
// 静态数组的首地址:@StaticArray[0]或@StaticArray.
ShowMessage(IntToHex(_CRC32CX86(Pd, Fs.Size), 8));
finally
Fs.Free;
end;
end;

[解决办法]
但是得到的crc32值跟其他工具计算出来的有不一致
[解决办法]
CPU内建的CRC table,就比从内存读省了不知道多少时间了,运算部分不会差太多吧

读书人网 >.NET

热点推荐