Are you using MD5 correctly in .net core?
The project environment of this article is .net 6.0 (.net 5.0 and above are supported)
I believe it is very easy to obtain the MD5 of a string in .net, but a casual search on the Internet reveals that there are many versions circulating, such as:
-
StringBuilder
version (should be considered the official version, used by the most people, I found that this is also used in ABP) -
BitConverter
version -
StringConcat
version (string concatenation, few people use it, probably everyone knows that the performance is not good)
But are they the best implementation? Let’s test it
StringBuilder version
public static string Md5_StringBuilder(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var hashByte in hashBytes)
{
sb.Append(hashByte.ToString("X2"));
}
return sb.ToString();
}
BitConverter version
public static string Md5_BitConverter(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
return BitConverter.ToString(hashBytes).Replace("-", "");
}
StringConcat version
public static string Md5_StringConcat(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
var output = string.Empty;
foreach (var hashByte in hashBytes)
{
output += hashByte.ToString("X2");
}
return output;
}
Performance comparison
First, let’s take a look at the data I got from the test (local configuration: 4 cores and 8 threads, the test results may be inconsistent)
Benchmark
Looking at the results, it is true that string splicing has the worst performance, but
StringBuilder
does not seem to be very efficient. What is Static? How can it perform so well? Compared with StringBuilder, single-thread performance Improved by 3 times, multi-linear increased by 5 times???
Yes, this is what I want to say, starting from .net 5.0, 2 very efficient methods are provided
Convert.ToHexString
MD5.HashData
Convert.ToHexString instance version
public static string MD5_HexConvert_Instance(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
return Convert.ToHexString(hashBytes);
}
MD5.HashData static version (strongly recommended)
public static string MD5_HexConvert_Static(string input)
{
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = MD5.HashData(inputBytes);
return Convert.ToHexString(hashBytes);
}
Summary
-
Strongly recommended Use
MD5.HashData
+Convert.ToHexString
. The code has the highest performance and is the most concise, with only 3 lines -
Don’t forget to release MD5. I saw that many people on the Internet do not use Dispose after using the example version
MD5.Create()
, which will lead to memory leaks!!!
Finally put my complete test code
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using System.Text;
using System.Security.Cryptography;
namespace ConsoleTest;
[SimpleJob(RuntimeMoniker.Net60)]
[MemoryDiagnoser(true)]
public class MD5_BenchMarks
{
[Params(10_0000)]
public int Size { get; set; }
[Benchmark]
[Arguments("123456")]
public string Md5_StringBuilder(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var hashByte in hashBytes)
{
sb.Append(hashByte.ToString("X2"));
}
return sb.ToString();
}
[Benchmark]
[Arguments("123456")]
public string Md5_StringConcat(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
var output = string.Empty;
foreach (var hashByte in hashBytes)
{
output += hashByte.ToString("X2");
}
return output;
}
[Benchmark]
[Arguments("123456")]
public string Md5_BitConverter(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
return BitConverter.ToString(hashBytes).Replace("-", "");
}
[Benchmark]
[Arguments("123456")]
public string MD5_HexConvert_Instance(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
return Convert.ToHexString(hashBytes);
}
[Benchmark]
[Arguments("123456")]
public string MD5_HexConvert_Static(string input)
{
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = MD5.HashData(inputBytes);
return Convert.ToHexString(hashBytes);
}
}
class Program
{
static void Main()
{
BenchmarkRunner.Run(typeof(MD5_BenchMarks));
Console.ReadKey();
}
}