我试图找出下面这些类中哪一个更快,因此我决定编写一个 BenchmarkDotNet 测试。
public class RequestSignerBenchmark
{
[Benchmark]
public void First()
{
var signer = RequestSigner2.FromSecret("hidden");
var timestamp = TimestampProvider.Default.CurrentTimestamp();
signer.Sign(timestamp, "GET", "products", "");
}
[Benchmark]
public void Second()
{
var signer = RequestSigner.FromSecret("hidden");
var timestamp = TimestampProvider.Default.CurrentTimestamp();
signer.Sign(timestamp, "GET", "products", "");
}
}
// Program.cs
BenchmarkRunner.Run<RequestSignerBenchmark>();
我不确定我所做的测试是否正确。从代码来看,First()
不是应该更快吗?如果错误,适当的测试会是什么样子?
| Method | Mean | Error | StdDev |
|------- |---------:|----------:|----------:|
| First | 3.234 us | 0.0646 us | 0.1769 us |
| Second | 2.659 us | 0.0516 us | 0.0614 us |
要评估的类
public sealed class RequestSigner
{
private readonly byte[] _base64Secret;
public RequestSigner(byte[] base64Secret)
{
_base64Secret = base64Secret;
}
public static RequestSigner FromSecret(string secret)
{
return new RequestSigner(Convert.FromBase64String(secret));
}
public string Sign(string timestamp, string method, string requestPath, string body)
{
var orig = Encoding.UTF8.GetBytes(timestamp + method.ToUpperInvariant() + requestPath + body);
using var hmac = new HMACSHA256(_base64Secret);
return Convert.ToBase64String(hmac.ComputeHash(orig));
}
}
public class RequestSigner2 : IDisposable
{
private readonly HMACSHA256 _hmac;
public RequestSigner2(byte[] base64Secret)
{
_hmac = new HMACSHA256(base64Secret);
}
public static RequestSigner2 FromSecret(string secret)
{
return new RequestSigner2(Convert.FromBase64String(secret));
}
public string Sign(string timestamp, string method, string requestPath, string body)
{
DoDisposeChecks();
if (string.IsNullOrWhiteSpace(method) || string.IsNullOrWhiteSpace(requestPath))
{
return string.Empty;
}
var orig = Encoding.UTF8.GetBytes(timestamp + method.ToUpperInvariant() + requestPath + body);
return Convert.ToBase64String(_hmac.ComputeHash(orig));
}
#region Disposable
private bool _isDisposed;
/// <summary>
/// Checks if this object has been disposed.
/// </summary>
/// <exception cref="ObjectDisposedException">Thrown if the object has been disposed.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void DoDisposeChecks()
{
if (_isDisposed)
{
throw new ObjectDisposedException(nameof(RequestSigner2));
}
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
/// <param name="disposing">A value indicating whether or not to dispose of managed resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}
if (disposing)
{
_hmac.Dispose();
}
_isDisposed = true;
}
#endregion
}
最佳答案
正如我在上面的评论中提到的,我相信你的断言 RequestSigner2
实现应该更快“可能”是不正确的,具体取决于您当前的测试结构。
您的 RequestSigner2
中有两个额外的 if 检查如果您不重复使用 HMACSHA256
,则会对您的性能产生影响(我们在这里讨论的是纳秒......)实例正确,但目前您还没有。
通过删除这两项检查,并包装 RequestSigner2
的初始化在 using 语句中,
[Benchmark]
public void First()
{
using var signer = RequestSigner2.FromSecret("aGlkZGVu");
var timestamp = DateTime.UtcNow.ToString();
signer.Sign(timestamp, "GET", "products", "");
}
我能够实现以下目标。
请注意,您的“优化”版本实际上多分配了 8 个字节。
我猜测您的假设是重用 HMACSHA256
实例比每次调用都初始化它要便宜。但有趣的是,这在您的测试中成本更高。
我相信您打算在实际应用程序代码中重用签名者,但这并未反射(reflect)在您的基准代码中。
您应该考虑初始化 RequestSigner2
一旦在 GlobalSetup 步骤中,然后在 GlobalCleanup 步骤中将其丢弃。
[MemoryDiagnoser]
public class RequestSignerBenchmark
{
private RequestSigner2 _requestSigner2;
[GlobalSetup]
public void Setup()
{
_requestSigner2 = RequestSigner2.FromSecret("aGlkZGVu");
}
[GlobalCleanup]
public void Cleanup()
{
_requestSigner2.Dispose();
}
[Benchmark]
public void First()
{
var timestamp = DateTime.UtcNow.ToString();
_requestSigner2.Sign(timestamp, "GET", "products", "");
}
[Benchmark]
public void Second()
{
var signer = RequestSigner.FromSecret("aGlkZGVu");
var timestamp = DateTime.UtcNow.ToString();
signer.Sign(timestamp, "GET", "products", "");
}
}
(请注意,由于没有时区实现,我不得不更改基准实现的一些部分)。
现在我们得到了您所期望的性能改进,即使重新添加了两个 if 检查!
关于c# - 尝试使用 BenchmarkDotNet 测试评估两个类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73026907/