【问题标题】:How to avoid allocation of large Byte[] when computing hash of large strings计算大字符串的哈希时如何避免分配大字节[]
【发布时间】:2015-04-22 19:01:25
【问题描述】:

我的任务是在我的应用程序中消除对大对象堆的所有(或尽可能多的)分配。最大的违规者之一是我们计算大字符串的 MD5 哈希的代码。

public static string MD5Hash(this string s)
{
    using (MD5CryptoServiceProvider csp = new MD5CryptoServiceProvider())
    {
         byte[] bytesToHash = Encoding.UTF8.GetBytes(s);
         byte[] hashBytes = csp.ComputeHash(bytesToHash);
         return Convert.ToBase64String(hashBytes);
    }
 }

为了示例的缘故,字符串本身可能已经在 LOH 中。我们的目标是防止对堆进行更多分配。

此外,当前的实现假设 UTF8 编码(一个很大的假设),但真正的目标是从字符串生成一个 byte[]。

MD5CryptoServiceProvider 可以将 Stream 作为输入,因此我们可以创建一个方法:

public static string MD5Hash(this Stream stream)
{
    using (MD5CryptoServiceProvider csp = new MD5CryptoServiceProvider())
    {
         return Convert.ToBase64String(csp.ComputeHash(stream));
    }
}

这是有希望的,因为我们不需要 byte[] 来让 ComputeHash 工作。我们需要一个流对象,它会在 ComputeHash 请求字节时从字符串中读取字节。

This rather controvesial question 提供了一种从字符串创建字节数组的方法,而不管编码如何。但是,我们要避免创建大字节数组。

This question 提供了一种通过将字符串读入 MemoryStream 来从字符串创建流的方法,但在内部也只是分配了一个大字节 [] 数组。

两者都不是真正的伎俩。

那么如何避免分配大字节[]?是否有一个 Stream 类会在读取字节时从另一个流(或读取器)读取?

【问题讨论】:

  • 这个 MD5 计算的时间有多紧迫?如果对时间要求不是很高,您始终可以将字符串写入(临时)文件,创建文件流并将其提供给 MD5CryptoServiceProvider。

标签: c# stream large-object-heap


【解决方案1】:

如果您不关心编码,那么您可以做的一件事是使用一些不安全的代码来防止任何进一步的缓冲区分配。 IE。获取字符串的原始字节,将 UnmanagedMemoryStream 的实例包裹在其周围并将其提供给 MD5 加密计算。

所以是这样的:

public static string MD5Hash(this string s)
{
    using (MD5CryptoServiceProvider csp = new MD5CryptoServiceProvider())
    {
        unsafe
        {
            fixed (char* input = s)
            {
                using (var stream = new UnmanagedMemoryStream((byte*)input, sizeof(char) * s.Length))
                    return Convert.ToBase64String(csp.ComputeHash(stream)); 
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    您可以实现自己的由字符串支持的流。

    请注意,基本上您只需要根据文档实现ReadWrite(但只需在Write 上抛出NotSupportedException,因为您不应该写入此流):

    当您实现 Stream 的派生类时,您必须提供 Read 和 Write 方法的实现。异步方法 ReadAsync、WriteAsync 和 CopyToAsync 在其实现中使用同步方法 Read 和 Write。

    您可能还想实现ReadByte

    ReadByte 和 WriteByte 的默认实现创建一个新的单元素字节数组,然后调用您的 Read 和 Write 实现

    来源:https://msdn.microsoft.com/pt-br/library/system.io.stream%28v=vs.110%29.aspx

    【讨论】:

      猜你喜欢
      • 2013-01-16
      • 2012-04-15
      • 1970-01-01
      • 2012-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-14
      • 2013-02-16
      相关资源
      最近更新 更多