diff --git a/src/Aardvark.Base/Extensions/ArrayExtensions.cs b/src/Aardvark.Base/Extensions/ArrayExtensions.cs index cc1c8a85..5561a1a7 100644 --- a/src/Aardvark.Base/Extensions/ArrayExtensions.cs +++ b/src/Aardvark.Base/Extensions/ArrayExtensions.cs @@ -2339,25 +2339,35 @@ public static void CopyTo(this IntPtr input, IntPtr target, int size) } #if NET6_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsByteSpan(this Array data) { var elementSize = data.GetType().GetElementType().GetCLRSize(); var span = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetArrayDataReference(data), data.Length * elementSize); return span; } - +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsByteSpan(this T[] data) where T : struct { - var span = new Span(data); - return MemoryMarshal.AsBytes(span); + return MemoryMarshal.AsBytes(data.AsSpan()); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan AsByteSpan(this string data) { return MemoryMarshal.AsBytes(data.AsSpan()); } -#endif - + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsCastSpan(this TFrom[] arr) + where TFrom : struct + where TTo : struct + { + return MemoryMarshal.Cast(arr.AsSpan()); + } + internal static unsafe T UseAsStream(this Array data, Func action) { var gc = GCHandle.Alloc(data, GCHandleType.Pinned); @@ -2418,8 +2428,6 @@ public static byte[] ComputeMD5Hash(this byte[] data) } #endif } - - /// /// Computes the MD5 hash of the data array. @@ -2442,6 +2450,19 @@ public static byte[] ComputeMD5Hash(this Array data) #endif } +#if NET6_0_OR_GREATER + /// + /// Computes the MD5 hash of the data array. + /// + /// 128bit/16byte data hash + public static byte[] ComputeMD5Hash(this T[] data) where T : struct + { + var hash = SHA1.HashData(data.AsByteSpan()); + Array.Resize(ref hash, 16); + return hash; + } +#endif + /// /// Computes the MD5 hash of the given string. /// 128bit/16byte data hash @@ -2488,9 +2509,19 @@ public static byte[] ComputeSHA1Hash(this Array data) return data.UseAsStream((stream) => sha.ComputeHash(stream)); } #endif - } +#if NET6_0_OR_GREATER + /// + /// Computes the SHA1 hash of the data array. + /// + /// 160bit/20byte data hash + public static byte[] ComputeSHA1Hash(this T[] data) where T : struct + { + return SHA1.HashData(data.AsByteSpan()); + } +#endif + /// /// Computes the SHA1 hash of the given string. /// 160bit/20byte data hash @@ -2537,6 +2568,17 @@ public static byte[] ComputeSHA256Hash(this Array data) #endif } +#if NET6_0_OR_GREATER + /// + /// Computes the SHA256 hash of the data array. + /// + /// 256bit/32byte data hash + public static byte[] ComputeSHA256Hash(this T[] data) where T : struct + { + return SHA256.HashData(data.AsByteSpan()); + } +#endif + /// /// Computes the SHA256 hash of the given string. /// @@ -2583,6 +2625,17 @@ public static byte[] ComputeSHA512Hash(this Array data) #endif } +#if NET6_0_OR_GREATER + /// + /// Computes the SHA512 hash of the data array. + /// + /// 512bit/64byte data hash + public static byte[] ComputeSHA512Hash(this T[] data) where T : struct + { + return SHA512.HashData(data.AsByteSpan()); + } +#endif + /// /// Computes the SHA512 hash of the given string. /// @@ -2623,6 +2676,19 @@ public static uint ComputeAdler32Checksum(this Array data) return a.Checksum; } +#if NET6_0_OR_GREATER + /// + /// Computes a checksum of the data array using the Adler-32 algorithm (). + /// + public static uint ComputeAdler32Checksum(this T[] data) where T : struct + { + var a = new Adler32(); + if (data != null) + a.Update(data.AsByteSpan()); + return a.Checksum; + } +#endif + /// /// Computes a checksum of the given string using the Adler-32 algorithm (). /// @@ -2638,6 +2704,6 @@ public static uint ComputeAdler32Checksum(this string s) return a.Checksum; } - #endregion +#endregion } } \ No newline at end of file diff --git a/src/Aardvark.Base/Extensions/StringExtensions.cs b/src/Aardvark.Base/Extensions/StringExtensions.cs index 52d69b74..db21e2a9 100644 --- a/src/Aardvark.Base/Extensions/StringExtensions.cs +++ b/src/Aardvark.Base/Extensions/StringExtensions.cs @@ -424,6 +424,24 @@ public static string ToBase64(this byte[] data) return Convert.ToBase64String(data); } +#if NET6_0_OR_GREATER + /// + /// Converts the byte span to string using base-64 encoding; + /// + public static string ToBase64(this Span span) + { + return Convert.ToBase64String(span); + } + + /// + /// Converts the byte span to string using base-64 encoding; + /// + public static string ToBase64(this ReadOnlySpan span) + { + return Convert.ToBase64String(span); + } +#endif + /// /// Builds a string representing the byte arrays as hexadecimal number. /// @@ -469,6 +487,6 @@ public static byte[] HexToBytes(this string str) return buffer; } - #endregion +#endregion } } diff --git a/src/Tests/Aardvark.Base.Benchmarks/Hashes.cs b/src/Tests/Aardvark.Base.Benchmarks/Hashes.cs new file mode 100644 index 00000000..a0f64beb --- /dev/null +++ b/src/Tests/Aardvark.Base.Benchmarks/Hashes.cs @@ -0,0 +1,94 @@ +using BenchmarkDotNet.Attributes; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Aardvark.Base.Benchmarks +{ + + //BenchmarkDotNet v0.13.9+228a464e8be6c580ad9408e98f18813f6407fb5a, Windows 10 (10.0.19045.4412/22H2/2022Update) + //Intel Core i7-8700K CPU 3.70GHz(Coffee Lake), 1 CPU, 12 logical and 6 physical cores + //.NET SDK 6.0.422 + // [Host] : .NET 6.0.30 (6.0.3024.21525), X64 RyuJIT AVX2 + // DefaultJob : .NET 6.0.30 (6.0.3024.21525), X64 RyuJIT AVX2 + + + //| Method | Count | Mean | Error | StdDev | Median | Code Size | Gen0 | Allocated | + //|----------- |------ |-----------:|----------:|----------:|-----------:|----------:|-------:|----------:| + //| SHA1_Net48 | 16 | 788.3 ns | 10.64 ns | 9.43 ns | 788.6 ns | 262 B | 0.0629 | 400 B | + //| SHA1_Array | 16 | 440.3 ns | 6.48 ns | 5.41 ns | 437.2 ns | 107 B | 0.0076 | 48 B | + //| SHA1_T | 16 | 406.7 ns | 8.08 ns | 13.04 ns | 403.7 ns | 149 B | 0.0076 | 48 B | + //| SHA1_Net48 | 128 | 1,566.4 ns | 30.87 ns | 65.11 ns | 1,541.1 ns | 262 B | 0.0629 | 400 B | + //| SHA1_Array | 128 | 1,090.6 ns | 9.32 ns | 8.26 ns | 1,090.7 ns | 107 B | 0.0076 | 48 B | + //| SHA1_T | 128 | 1,063.4 ns | 17.39 ns | 17.85 ns | 1,060.4 ns | 149 B | 0.0076 | 48 B | + //| SHA1_Net48 | 1024 | 6,761.1 ns | 134.11 ns | 143.50 ns | 6,717.4 ns | 262 B | 0.0610 | 400 B | + //| SHA1_Array | 1024 | 6,169.2 ns | 80.47 ns | 75.27 ns | 6,140.3 ns | 107 B | 0.0076 | 48 B | + //| SHA1_T | 1024 | 6,128.8 ns | 68.52 ns | 57.22 ns | 6,116.5 ns | 149 B | 0.0076 | 48 B | + [MemoryDiagnoser, DisassemblyDiagnoser] + public class HashBench + { + internal static unsafe T UseAsStream(Array data, Func action) + { + var gc = GCHandle.Alloc(data, GCHandleType.Pinned); + var l = data.GetType().GetElementType().GetCLRSize() * data.Length; + try + { + using (var stream = + new UnmanagedMemoryStream(((byte*)gc.AddrOfPinnedObject())!, l, l, FileAccess.Read)) + { + return action(stream); + } + } + finally + { + gc.Free(); + } + } + + public static unsafe byte[] ComputeSHA1HashNet48(Array data) + { + using (var sha = SHA1.Create()) + { + return UseAsStream(data, (stream) => sha.ComputeHash(stream)); + } + } + + [Params(16, 128, 1024)] + public int Count; + + float[] data; + Array dataArr; + + [GlobalSetup] + public void Init() + { + data = new float[Count]; + for (int i = 0; i < data.Length; i++) + data[i] = Random.Shared.NextSingle(); + dataArr = data; + } + + [Benchmark] + public byte[] SHA1_Net48() + { + return ComputeSHA1HashNet48(data); + } + + [Benchmark] + public byte[] SHA1_Array() + { + return dataArr.ComputeSHA1Hash(); + } + + [Benchmark] + public byte[] SHA1_T() + { + return data.ComputeSHA1Hash(); + } + } +} diff --git a/src/Tests/Aardvark.Base.Benchmarks/Program.cs b/src/Tests/Aardvark.Base.Benchmarks/Program.cs index 70ed7aa1..77524b34 100644 --- a/src/Tests/Aardvark.Base.Benchmarks/Program.cs +++ b/src/Tests/Aardvark.Base.Benchmarks/Program.cs @@ -10,7 +10,8 @@ public static void Main(string[] args) var cfg = ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator); //BenchmarkSwitcher.FromAssembly(typeof(IntegerPowerFloat).Assembly).Run(args, cfg); - BenchmarkRunner.Run(cfg); + BenchmarkRunner.Run(cfg); + //BenchmarkRunner.Run(cfg); //BenchmarkRunner.Run(); //BenchmarkRunner.Run