我正在使用以下函数计算VS2008,.NET 3.5项目中文件的CRC32:
public UInt32 ComputeHash(System.IO.Stream stream)
{
unchecked
{
const int BUFFER_SIZE = 1024;
UInt32 crc32Result = 0xFFFFFFFF;
byte[] buffer = new byte[BUFFER_SIZE];
int count = stream.Read(buffer, 0, BUFFER_SIZE);
while (count > 0)
{
for (int i = 0; i < count; i++)
{
crc32Result = ((crc32Result) >> 8) ^ _crc32Table[(buffer[i]) ^ (crc32Result) & _LOOKUP_TABLE_MAX_INDEX];
}
count = stream.Read(buffer, 0, BUFFER_SIZE);
}
return ~crc32Result;
}
}
为了简洁起见,我遗漏了构建查找表(_crc32Table)的函数。该表是一个UInt32数组,是在实例化类时构建的,包含256个值(256也是_LOOKUP_TABLE_MAX_INDEX + 1的值)。
我已经运行了一些基准测试,将它与MD5CryptoServiceProvider和SHA1CryptoServiceProvider ComputeHash函数进行比较,它们的速度要快得多。 MD5功能的速度提高了两倍,SHA1哈希的速度提高了约35%。我被告知CRC32很快,但那不是我所看到的。
我的假设是否错误?这是预期的还是这个算法存在缺陷?
答案 0 :(得分:6)
您正在将代码与内置函数进行比较,并询问它们为何更快。您需要做的是找到内置函数的源代码。他们是如何工作的?看看有什么不同。
Betcha内置函数调用本机库并且不必在托管内存框架内运行就作弊。
答案 1 :(得分:3)
分析可能有助于确定IO调用(读取)与CRC计算所花费的时间。代码通常是IO绑定的。但是,作为在C#中实现了相当快的CRC功能的人,我可以给出一些指针,说明为什么它比MD5慢。
如果实现切片为N,则将所有查找表打包到一个大表中,以便可以通过相同的指针访问它们。您也可以使用相同的表进行4个切片和8个切片。另请注意,典型的N切片实现假设特定的机器字节序,因此您可能需要一个单独的版本来支持big-endian机器,您可以使用BitConverter.IsLittleEndian检查它。
答案 2 :(得分:0)
也许:您在计算CRC吞吐量时是否计算查找表的计算?通常,查找表计算一次并缓存。如果不缓存它,则每次计算CRC时都会计算它。此外,如果您只测量一个CRC,那么您可能已将表计算成本包括在CRC计算成本中。最好测量每个哈希的多次迭代。
附录:当我测量时,当使用/ debug +和/ optimize-编译应用程序时,我看到了一个2.6倍的因子,比较你的CRC32和MD5哈希值。使用/ debug-和/ optimize +,我看到了1.6倍的因子。当我更改编译标志时,绝对MD5性能没有变化。没有调试,CRC仍然较慢,但它更接近。
答案 3 :(得分:0)
我不太熟悉在执行此代码时自动执行的优化,但如果分析不适合您,则有几个选项。
我建议尝试使用不安全的代码,并使用指针算法进行缓冲区[i]和_crc32Table查找,以防它尚未优化。
我可以看到您遇到性能问题的另一个地方是Stream.Read调用。您是否尝试过不同的BUFFER_SIZE值?
如果没有自动优化,使用较大的字节缓冲区并可能执行某些手动loop unrolling也可以帮助您。