【问题标题】:Using SSE 4.2 crc32 algorithm in c# ? Is it possible?在 c# 中使用 SSE 4.2 crc32 算法?可能吗?
【发布时间】:2019-11-23 13:05:56
【问题描述】:

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

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

1 - 有可能吗?

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

(如果可能,请提供代码示例)

【问题讨论】:

  • 你分析过你的代码吗?我严重怀疑您的问题是由于缓慢的 CRC 算法,更可能是读取几 GB 数据的 I/O 开销。
  • @James 确实,或者你可以利用 unsafe 代码区域和指针优化来获得一些好处——尽管这只是一个疯狂的猜想 :-)
  • 计算几个GB文件的CRC32需要读取整个文件...并按顺序处理。尝试测量读取整个文件(并添加字节或 32 位块,以避免您的系统优化读取)需要多长时间,并将其与您的 CRC32 计算进行比较。
  • CRC32 的密​​码学标签非常可怕,我真的希望这是一个错误(在这种情况下请删除标签),否则你的问题比计算缓慢更多。
  • @Bruno 你是对的,但是 crc32 算法非常适合 .net System.Security.Cryptography 模式。这就是我添加标签的原因。无论如何,如果它惹恼了其他人,我可以删除它。我用它来识别文件。

标签: c# cryptography sse crc32


【解决方案1】:

如今,我们已经被 .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;
    }
}

【讨论】:

  • 这是否一次只读取数组 1 个字节? x86 CRC32 instruction 可用于 1、2、4 或 8 字节源大小。在 C++ 中,_mm_crc32_u64。这将比一次使用一个字节快 8 倍;无论输入大小如何,延迟都固定为 3 个周期。 (32位模式一次最多只能做4个字节)
  • 你是对的。我刚刚用它的 X64 版本更新了答案。现在稍微快了一点。
  • 只是“稍微”?你在测试微小的输入吗?或者您的基准测试是否以热身时间为主?还是由于某种原因不能有效地编译?对于中等或更大的缓冲区(比如 1kiB 应该足以隐藏启动/清理开销),这应该快 8 倍。
  • @PeterCordes 我刚刚意识到一些有趣的事情。使用 1GB 的数据和在我的 I7 CPU 上,使用多项式表,我在 1080 毫秒内获得哈希,在 2660 毫秒内获得 X32 SSE,在 353 毫秒内获得 X64 SSE。并且结果是一致的。你会认为硬件版本总是更快:)
  • 我很确定在 asm 中,一次 8 个字节的 CRC32 指令是最快的方法,当然在 Intel CPU 上也是如此。如果您的 C# 代码不是这种情况,那么它的编译效率不高。在循环内使用本地 tmp var 而不是 _crc 类成员以确保它可以优化为寄存器是否有帮助?您确定您使用的是发布模式并进行了足够的热身运行吗?
【解决方案2】:

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

http://tirania.org/blog/archive/2008/Nov-03.html

Stackoverflow 相关问题:

Using SSE in c# is it possible?

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

Calling mono c# code from Microsoft .net?

也就是说,它会工作,但它会被软件模拟。

【讨论】:

    猜你喜欢
    • 2010-09-29
    • 2014-10-23
    • 2015-03-11
    • 2010-12-23
    • 2013-05-22
    • 2014-04-12
    • 2013-07-12
    • 2012-06-12
    相关资源
    最近更新 更多