diff --git a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs index 74ec9ac240..14cffc041f 100644 --- a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs +++ b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs @@ -26,11 +26,16 @@ namespace Umbraco.Cms.Core.Packaging public static string GetEmbeddedPackageDataManifestHash(Type planType) { + // SEE: HashFromStreams in the benchmarks project for how fast this is. It will run + // on every startup for every embedded package.zip. The bigger the zip, the more time it takes. + // But it is still very fast ~303ms for a 100MB file. This will only be an issue if there are + // several very large package.zips. + using Stream stream = GetEmbeddedPackageStream(planType); using HashAlgorithm alg = SHA1.Create(); // create a string output for the hash - var stringBuilder = new StringBuilder(); + var stringBuilder = new StringBuilder(); var hashedByteArray = alg.ComputeHash(stream); foreach (var b in hashedByteArray) { diff --git a/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs index a27437c6f8..6f7cea07ca 100644 --- a/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs @@ -5,6 +5,7 @@ using Umbraco.Tests.Benchmarks.Config; namespace Umbraco.Tests.Benchmarks { + [QuickRunWithMemoryDiagnoserConfig] public class CombineGuidBenchmarks { diff --git a/src/Umbraco.Tests.Benchmarks/HashFromStreams.cs b/src/Umbraco.Tests.Benchmarks/HashFromStreams.cs new file mode 100644 index 0000000000..9d2d483c69 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/HashFromStreams.cs @@ -0,0 +1,132 @@ +using System.IO; +using System.Security.Cryptography; +using System.Text; +using BenchmarkDotNet.Attributes; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [MemoryDiagnoser] + public class HashFromStreams + { + // according to this post: https://stackoverflow.com/a/1051777/694494 + // the SHA1CryptoServiceProvider is faster, but that is not reflected in these benchmarks. + + /* + + BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1052 (20H2/October2020Update) + Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + .NET SDK=5.0.300-preview.21258.4 + [Host] : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT + DefaultJob : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT + + + ``` + | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | + |------------------------ |-------------:|------------:|------------:|------:|------:|------:|----------:| + | NormalSmall100KBFile | 279.6 μs | 4.97 μs | 4.65 μs | - | - | - | 1 KB | + | NormalMedium5MBFile | 15,051.0 μs | 242.68 μs | 215.13 μs | - | - | - | 1 KB | + | NormalLarge100MBFile | 303,356.2 μs | 2,520.40 μs | 2,234.27 μs | - | - | - | 1 KB | + | ManagedSmall100KBFile | 281.2 μs | 5.54 μs | 5.69 μs | - | - | - | 1 KB | + | ManagedMedium5MBFile | 14,895.4 μs | 95.67 μs | 74.69 μs | - | - | - | 1 KB | + | ManagedLarge100MBFile | 302,513.9 μs | 3,681.02 μs | 3,073.82 μs | - | - | - | 2 KB | + | UnmanagedSmall100KBFile | 279.1 μs | 2.91 μs | 2.43 μs | - | - | - | 1 KB | + | UnmanagedMedium5MBFile | 14,969.7 μs | 169.22 μs | 150.01 μs | - | - | - | 1 KB | + | UnmanagedLarge100MBFile | 306,127.9 μs | 3,203.10 μs | 2,839.46 μs | - | - | - | 2 KB | + + + */ + + private readonly SHA1CryptoServiceProvider _unmanaged; + private readonly SHA1Managed _managed; + private readonly SHA1 _normal; + private readonly Stream _smallFile; + private readonly Stream _medFile; + private readonly Stream _largeFile; + + public HashFromStreams() + { + _unmanaged = new SHA1CryptoServiceProvider(); + _managed = new SHA1Managed(); + _normal = SHA1.Create(); + _smallFile = File.OpenRead(@"C:\YOUR_PATH_GOES_HERE\small-file.bin"); + _medFile = File.OpenRead(@"C:\YOUR_PATH_GOES_HERE\med-file.bin"); + _largeFile = File.OpenRead(@"C:\YOUR_PATH_GOES_HERE\large-file.bin"); + } + + private string DoHash(HashAlgorithm alg, Stream stream) + { + var stringBuilder = new StringBuilder(); + var hashedByteArray = alg.ComputeHash(stream); + foreach (var b in hashedByteArray) + { + stringBuilder.Append(b.ToString("x2")); + } + return stringBuilder.ToString(); + } + + [Benchmark] + public string NormalSmall100KBFile() + { + _smallFile.Seek(0, SeekOrigin.Begin); + return DoHash(_normal, _smallFile); + } + + [Benchmark] + public string NormalMedium5MBFile() + { + _medFile.Seek(0, SeekOrigin.Begin); + return DoHash(_normal, _medFile); + } + + [Benchmark] + public string NormalLarge100MBFile() + { + _largeFile.Seek(0, SeekOrigin.Begin); + return DoHash(_normal, _largeFile); + } + + [Benchmark] + public string ManagedSmall100KBFile() + { + _smallFile.Seek(0, SeekOrigin.Begin); + return DoHash(_managed, _smallFile); + } + + [Benchmark] + public string ManagedMedium5MBFile() + { + _medFile.Seek(0, SeekOrigin.Begin); + return DoHash(_managed, _medFile); + } + + [Benchmark] + public string ManagedLarge100MBFile() + { + _largeFile.Seek(0, SeekOrigin.Begin); + return DoHash(_managed, _largeFile); + } + + [Benchmark] + public string UnmanagedSmall100KBFile() + { + _smallFile.Seek(0, SeekOrigin.Begin); + return DoHash(_unmanaged, _smallFile); + } + + [Benchmark] + public string UnmanagedMedium5MBFile() + { + _medFile.Seek(0, SeekOrigin.Begin); + return DoHash(_unmanaged, _medFile); + } + + [Benchmark] + public string UnmanagedLarge100MBFile() + { + _largeFile.Seek(0, SeekOrigin.Begin); + return DoHash(_unmanaged, _largeFile); + } + } +} +