在计算大字符串的散列时如何避免分配大的Byte []

时间:2015-02-20 23:46:19

标签: c# stream large-object-heap

我的任务是在我的应用程序中尽可能消除所有(或尽可能多的)大对象堆的分配。最大的冒犯者之一是我们的代码,它计算大字符串的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));
    }
}

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

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

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

两者都没有真正做到这一点。

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

2 个答案:

答案 0 :(得分:2)

您可以使用字符串实现自己的流。

请注意,基本上您只需要实现ReadWrite,相应地使用文档(但只需在NotSupportedException上抛出Write,因为您不应该写这个流):

  

实现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

答案 1 :(得分:2)

如果您不关心编码,那么您可以做的一件事就是使用一些不安全的代码来阻止任何进一步的缓冲区分配。即获取字符串的原始字节,在其周围包装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)); 
            }
        }
    }
}