在c#中使用SSE 4.2 crc32算法?可能吗?

在c#中使用SSE 4.2 crc32算法?可能吗?,c#,cryptography,sse,crc32,C#,Cryptography,Sse,Crc32,我必须在很多文件上计算crc32,而且还要计算大文件(几GB)。我尝试了几种在网络上找到的算法,比如or,它可以工作,但速度很慢(超过1分钟)。 我发现了各种crc32算法,发现sse 4.2有硬件加速的crc32方法 我没有找到使用SSE crc32代码的c#代码示例 1-可能吗 2-如何检测当前cpu是否启用了SSE4.2?(切换crc32方法) (如果可能,请编写示例代码)我相信Mono允许通过Mono.Simd名称空间访问CPU指令: 有关问题: Mono代码是开源的。看起来您不能仅

我必须在很多文件上计算crc32,而且还要计算大文件(几GB)。我尝试了几种在网络上找到的算法,比如or,它可以工作,但速度很慢(超过1分钟)。 我发现了各种crc32算法,发现sse 4.2有硬件加速的crc32方法

我没有找到使用SSE crc32代码的c#代码示例

1-可能吗

2-如何检测当前cpu是否启用了SSE4.2?(切换crc32方法)


(如果可能,请编写示例代码)

我相信Mono允许通过Mono.Simd名称空间访问CPU指令:

有关问题:

Mono代码是开源的。看起来您不能仅仅将其添加到.NET项目中以获得好处,因为它似乎需要Mono运行时:


这就是说,它可以工作,但它将是软件模拟的。

现在,我们已经被.NET Core 3.0中可用的
System.Runtime.Intrinsics.X86
命名空间所破坏。以下是使用SSE 4.2的CRC32-C算法的完整实现:

using System;
using System.Runtime.Intrinsics.X86;
using System.Security.Cryptography;

/// <summary>
/// The hardware implementation of the CRC32-C polynomial 
/// implemented on Intel CPUs supporting SSE4.2.
/// </summary>
public class Crc32HardwareAlgorithm : HashAlgorithm
{
    /// <summary>
    /// the current CRC value, bit-flipped
    /// </summary>
    private uint _crc;

    /// <summary>
    /// We can further optimize the algorithm when X64 is available.
    /// </summary>
    private bool _x64Available;

    /// <summary>
    /// Default constructor
    /// </summary>
    public Crc32HardwareAlgorithm()
    {
        if (!Sse42.IsSupported)
        {
            throw new NotSupportedException("SSE4.2 is not supported");
        }

        _x64Available = Sse42.X64.IsSupported;

        // The size, in bits, of the computed hash code.
        this.HashSizeValue = 32;
        this.Reset();
    }

    /// <summary>When overridden in a derived class, routes data written to the object into the hash algorithm for computing the hash.</summary>
    /// <param name="array">The input to compute the hash code for.</param>
    /// <param name="ibStart">The offset into the byte array from which to begin using data.</param>
    /// <param name="cbSize">The number of bytes in the byte array to use as data.</param>
    protected override void HashCore(byte[] array, int ibStart, int cbSize)
    {
        if (_x64Available)
        {
            while (cbSize >= 8)
            {
                _crc = (uint)Sse42.X64.Crc32(_crc, BitConverter.ToUInt64(array, ibStart));
                ibStart += 8;
                cbSize -= 8;
            }
        }

        while (cbSize > 0)
        {
            _crc = Sse42.Crc32(_crc, array[ibStart]);
            ibStart++;
            cbSize--;
        }
    }

    /// <summary>When overridden in a derived class, finalizes the hash computation after the last data is processed by the cryptographic stream object.</summary>
    /// <returns>The computed hash code.</returns>
    protected override byte[] HashFinal()
    {
        uint outputCrcValue = ~_crc;

        return BitConverter.GetBytes(outputCrcValue);
    }

    /// <summary>Initializes an implementation of the <see cref="T:System.Security.Cryptography.HashAlgorithm"></see> class.</summary>
    public override void Initialize()
    {
        this.Reset();
    }

    private void Reset()
    {
        _crc = uint.MaxValue;
    }
}
使用系统;
使用System.Runtime.Intrinsics.X86;
使用System.Security.Cryptography;
/// 
///CRC32-C多项式的硬件实现
///在支持SSE4.2的英特尔CPU上实现。
/// 
公共类CRC32硬件算法:哈希算法
{
/// 
///当前CRC值,位翻转
/// 
私家uint_crc;
/// 
///当X64可用时,我们可以进一步优化算法。
/// 
可提供私人bool_x64;
/// 
///默认构造函数
/// 
公共CRC32硬件算法()
{
如果(!Sse42.IsSupported)
{
抛出新的NotSupportedException(“不支持SSE4.2”);
}
_X64可用=Sse42.X64.IsSupported;
//计算出的哈希代码的大小(以位为单位)。
this.HashSizeValue=32;
这是Reset();
}
///在派生类中重写时,将写入对象的数据路由到哈希算法中以计算哈希。
///要为其计算哈希代码的输入。
///字节数组中开始使用数据的偏移量。
///字节数组中用作数据的字节数。
受保护的重写void HashCore(字节[]数组,int-ibStart,int-cbSize)
{
如果(_x64可用)
{
而(cbSize>=8)
{
_crc=(uint)Sse42.X64.Crc32(crc,BitConverter.ToUInt64(array,ibStart));
ibStart+=8;
cbSize-=8;
}
}
而(cbSize>0)
{
_crc=Sse42.Crc32(_crc,数组[ibStart]);
ibStart++;
cbSize——;
}
}
///当在派生类中重写时,在加密流对象处理最后一个数据后,完成哈希计算。
///计算出的哈希代码。
受保护的重写字节[]HashFinal()
{
uint输出crc值=~\u crc;
返回BitConverter.GetBytes(outputCrcValue);
}
///初始化类的实现。
公共覆盖无效初始化()
{
这是Reset();
}
私有无效重置()
{
_crc=uint.MaxValue;
}
}

您分析过您的代码吗?我严重怀疑您的问题是由于CRC算法太慢,更可能是读取数GB数据的I/O开销。@James确实,或者您可以利用
不安全的
代码区域和指针优化来获得一些好处-尽管这只是一个猜测:-)计算几个GB文件的CRC32需要读取整个文件。。。并按顺序进行处理。尝试测量简单读取整个文件(并添加字节或32位块,以避免系统优化读取)所需的时间,将其与CRC32计算进行比较。加密标记和CRC32非常可怕,我真的希望这是一个错误(在这种情况下,请删除标记),否则,您将面临比缓慢计算多得多的问题。@Bruno您是对的,但crc32算法非常适合.net System.Security.Cryptography模式。这就是我添加标签的原因。不管怎样,如果它惹恼了别人,我可以把它拿走。我用它来识别文件。它一次只读取数组1字节吗?提供了1、2、4或8字节的源大小。在C++中,<代码>这将比每次使用一个字节快8倍;无论输入大小如何,延迟都固定为3个周期。(32位模式一次最多只能处理4个字节)你说得对。我刚刚用X64版本更新了答案。现在稍微快一点,只是“稍微”?你在测试微小的输入吗?或者你的基准测试是由热身时间决定的?或者由于某种原因,它不能有效地编译?对于中等或更大的缓冲区(比如1KB应该足以隐藏启动/清理开销),速度应该快8倍。@PeterCordes我刚刚意识到一些有趣的事情。使用1GB的数据,在我的I7 CPU上,使用多项式表,我得到1080ms的哈希值,2660ms的X32 SSE和353ms的X64 SSE。结果是一致的。你可能会认为硬件版本总是更快:)我很确定在asm中,CRC32指令每次8字节是最快的方式,当然是在Intel CPU上。如果你的C#代码不是这样的话,那么它的编译效率就不高。在循环中使用本地tmp var而不是
\u crc
类成员来确保它可以优化到寄存器中,这会有帮助吗?你确定你使用了释放模式并且做了足够的热身跑吗?