我正在尝试进行一些测量,以了解如果"加密/解密过程";添加了\。此外,我正在比较不同的方法,如使用FileStream
或返回MemoryStream
(在某些情况下我需要(。
看起来大文件保存在内存中(Gen2&LOH(。我如何才能完全清除堆(我想在FileStream方法中看到相同的Gen2结果(?
我使用的是using
关键字。但是看起来没有希望了!我还减少了默认的缓冲区大小,您可以看到下面的代码。但我在Gen2 中仍然有数字
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.572 (2004/?/20H1)
Intel Core i9-10920X CPU 3.50GHz, 1 CPU, 24 logical and 12 physical cores
[Host] : .NET Framework 4.8 (4.8.4250.0), X86 LegacyJIT
DefaultJob : .NET Framework 4.8 (4.8.4250.0), X86 LegacyJIT
文件流结果
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|-------------------- |----------:|----------:|----------:|----------:|---------:|------:|------------:|
| TXT300BYTES_Decrypt | 2.500 ms | 0.0444 ms | 0.0593 ms | 19.5313 | - | - | 105.11 KB |
| PDF500KB_Decrypt | 12.909 ms | 0.2561 ms | 0.4348 ms | 187.5000 | 15.6250 | - | 1019.59 KB |
| PDF1MB_Decrypt | 14.125 ms | 0.2790 ms | 0.4001 ms | 406.2500 | 15.6250 | - | 2149.96 KB |
| TIFF1MB_Decrypt | 10.087 ms | 0.1949 ms | 0.1728 ms | 437.5000 | 31.2500 | - | 2329.37 KB |
| TIFF5MB_Decrypt | 22.779 ms | 0.4316 ms | 0.4239 ms | 2000.0000 | 187.5000 | - | 10434.34 KB |
| TIFF10MB_Decrypt | 38.467 ms | 0.7382 ms | 0.8205 ms | 3857.1429 | 285.7143 | - | 20144.01 KB |
内存流结果
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|-------------------- |----------:|----------:|----------:|----------:|----------:|---------:|------------:|
| TXT300BYTES_Decrypt | 1.673 ms | 0.0098 ms | 0.0092 ms | 27.3438 | 1.9531 | - | 147.69 KB |
| PDF500KB_Decrypt | 9.956 ms | 0.1407 ms | 0.1248 ms | 328.1250 | 328.1250 | 328.1250 | 2316.08 KB |
| PDF1MB_Decrypt | 11.998 ms | 0.0622 ms | 0.0486 ms | 921.8750 | 546.8750 | 531.2500 | 4737.8 KB |
| TIFF1MB_Decrypt | 9.252 ms | 0.0973 ms | 0.0910 ms | 953.1250 | 671.8750 | 500.0000 | 4902.34 KB |
| TIFF5MB_Decrypt | 24.220 ms | 0.1105 ms | 0.0980 ms | 2531.2500 | 718.7500 | 468.7500 | 20697.43 KB |
| TIFF10MB_Decrypt | 41.463 ms | 0.5678 ms | 0.5033 ms | 4833.3333 | 1500.0000 | 916.6667 | 40696.31 KB |
public static class Constants
{
public const int BufferSize = 40960; // Default is 81920
}
文件解密方法
public class DescryptionService
{
public async Task<string> DecryptFileAsync(string sourcePath)
{
var tempFilePath = SecurityFileHelper.CreateTempFile();
using var sourceStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var keyBytes = Convert.FromBase64String(_key);
using var destinationStream = new FileStream(tempFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
using var provider = new AesCryptoServiceProvider();
var IV = new byte[provider.IV.Length];
await sourceStream.ReadAsync(IV, 0, IV.Length);
using var cryptoTransform = provider.CreateDecryptor(keyBytes, IV);
using var cryptoStream = new CryptoStream(sourceStream, cryptoTransform, CryptoStreamMode.Read);
await cryptoStream.CopyToAsync(destinationStream, Constants.BufferSize);
return tempFilePath;
}
}
内存解密方法
public class DescryptionService
{
public async Task<Stream> DecryptStreamAsync(Stream sourceStream)
{
var memoryStream = new MemoryStream();
if (sourceStream.Position != 0) sourceStream.Position = 0;
var tempFilePath = SecurityFileHelper.CreateTempFile();
try
{
var keyBytes = Convert.FromBase64String(_key);
using var destinationStream = new FileStream(tempFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
using var provider = new AesCryptoServiceProvider();
var IV = new byte[provider.IV.Length];
await sourceStream.ReadAsync(IV, 0, IV.Length);
using var cryptoTransform = provider.CreateDecryptor(keyBytes, IV);
using var cryptoStream = new CryptoStream(sourceStream, cryptoTransform, CryptoStreamMode.Read);
await cryptoStream.CopyToAsync(destinationStream, Constants.BufferSize);
destinationStream.Position = 0;
await destinationStream.CopyToAsync(memoryStream, Constants.BufferSize);
await memoryStream.FlushAsync();
memoryStream.Position = 0;
}
finally
{
if (File.Exists(tempFilePath))
File.Delete(tempFilePath);
}
return memoryStream;
}
}
// Calling it like this
using var encryptedStream = File.OpenRead("some file path");
var svc = new DecryptionService();
using var decryptedStream = await svc.DecryptStreamAsync(encryptedStream);
顺便说一下,我还添加了以下行:
decryptedStream.Position = 0;
decryptedStream.SetLength(0);
decryptedStream.Capacity = 0; // <<< this one will null bytes in memory stream
还有这些结果
| Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated |
|-------------------- |----------:|----------:|----------:|----------:|----------:|----------:|---------:|------------:|
| TXT300BYTES_Decrypt | 1.659 ms | 0.0322 ms | 0.0301 ms | 1.662 ms | 27.3438 | 1.9531 | - | 148.03 KB |
| PDF500KB_Decrypt | 11.085 ms | 0.2829 ms | 0.8297 ms | 10.769 ms | 328.1250 | 328.1250 | 328.1250 | 2312.33 KB |
| PDF1MB_Decrypt | 12.479 ms | 0.2029 ms | 0.3859 ms | 12.402 ms | 906.2500 | 562.5000 | 531.2500 | 4734.61 KB |
| TIFF1MB_Decrypt | 9.352 ms | 0.0971 ms | 0.0861 ms | 9.359 ms | 953.1250 | 593.7500 | 500.0000 | 4908 KB |
| TIFF5MB_Decrypt | 24.760 ms | 0.4752 ms | 0.4213 ms | 24.607 ms | 2593.7500 | 843.7500 | 531.2500 | 20715.76 KB |
| TIFF10MB_Decrypt | 41.976 ms | 0.6657 ms | 0.5901 ms | 42.011 ms | 4833.3333 | 1500.0000 | 916.6667 | 40744.43 KB |
我错过了什么?!:(
我正在比较不同的方法,比如使用FileStream或返回MemoryStream
看起来大文件保存在内存中(Gen2&LOH(。我如何才能完全清除堆(我想在FileStream方法中看到相同的Gen2结果(?
我不确定我是否理解您所说的清除堆的意思,以及您为什么希望看到FileStream
的Gen 2集合。
您正在使用的工具(BenchmarkDotNet(在每次基准迭代后强制执行两次全内存清理。它确保每一次基准测试迭代都以";清洁堆";。为了确保GC的自调优特性(或任何其他事情,如内存泄漏(不会影响其他基准测试,每个基准测试都在一个独立的过程中执行。此外,按1k操作(基准调用(缩放的集合数量。这允许对GC度量进行逐个比较。
您正在比较两种不同的方法,很可能(这是一个需要用内存探查器验证的假设(其中一种方法分配了大对象,因此您得到了Gen 2
集合。另一个没有。这是给定解决方案的一个性能特征,在实现业务逻辑时应该考虑它。例如:如果您的服务应该是低延迟的,并且您不能允许由Gen 2集合引起的长时间GC暂停,那么您应该选择不分配大对象的方法。
如果你想摆脱Gen 2集合,你可以尝试使用来集中内存
- 数组的ArrayPool
- RecycableMemoryStream用于内存流