Skip to content

Commit

Permalink
- moved some Span extensions to netstandard2.0
Browse files Browse the repository at this point in the history
- added ToBase64 extensions with span
- added generic Compute**Hash overloads (avoiding GetCLRSize)
- added AsCastSpan extension
- added HashBench
  • Loading branch information
luithefirst committed May 22, 2024
1 parent aa3cfb7 commit 0537771
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 11 deletions.
84 changes: 75 additions & 9 deletions src/Aardvark.Base/Extensions/ArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> 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<byte> AsByteSpan<T>(this T[] data) where T : struct
{
var span = new Span<T>(data);
return MemoryMarshal.AsBytes(span);
return MemoryMarshal.AsBytes(data.AsSpan());
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<byte> AsByteSpan(this string data)
{
return MemoryMarshal.AsBytes(data.AsSpan());
}
#endif


[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<TTo> AsCastSpan<TFrom, TTo>(this TFrom[] arr)
where TFrom : struct
where TTo : struct
{
return MemoryMarshal.Cast<TFrom, TTo>(arr.AsSpan());
}

internal static unsafe T UseAsStream<T>(this Array data, Func<UnmanagedMemoryStream, T> action)
{
var gc = GCHandle.Alloc(data, GCHandleType.Pinned);
Expand Down Expand Up @@ -2418,8 +2428,6 @@ public static byte[] ComputeMD5Hash(this byte[] data)
}
#endif
}



/// <summary>
/// Computes the MD5 hash of the data array.
Expand All @@ -2442,6 +2450,19 @@ public static byte[] ComputeMD5Hash(this Array data)
#endif
}

#if NET6_0_OR_GREATER
/// <summary>
/// Computes the MD5 hash of the data array.
/// </summary>
/// <returns>128bit/16byte data hash</returns>
public static byte[] ComputeMD5Hash<T>(this T[] data) where T : struct
{
var hash = SHA1.HashData(data.AsByteSpan());
Array.Resize(ref hash, 16);
return hash;
}
#endif

/// <summary>
/// Computes the MD5 hash of the given string.
/// <returns>128bit/16byte data hash</returns>
Expand Down Expand Up @@ -2488,9 +2509,19 @@ public static byte[] ComputeSHA1Hash(this Array data)
return data.UseAsStream((stream) => sha.ComputeHash(stream));
}
#endif

}

#if NET6_0_OR_GREATER
/// <summary>
/// Computes the SHA1 hash of the data array.
/// </summary>
/// <returns>160bit/20byte data hash</returns>
public static byte[] ComputeSHA1Hash<T>(this T[] data) where T : struct
{
return SHA1.HashData(data.AsByteSpan());
}
#endif

/// <summary>
/// Computes the SHA1 hash of the given string.
/// <returns>160bit/20byte data hash</returns>
Expand Down Expand Up @@ -2537,6 +2568,17 @@ public static byte[] ComputeSHA256Hash(this Array data)
#endif
}

#if NET6_0_OR_GREATER
/// <summary>
/// Computes the SHA256 hash of the data array.
/// </summary>
/// <returns>256bit/32byte data hash</returns>
public static byte[] ComputeSHA256Hash<T>(this T[] data) where T : struct
{
return SHA256.HashData(data.AsByteSpan());
}
#endif

/// <summary>
/// Computes the SHA256 hash of the given string.
/// </summary>
Expand Down Expand Up @@ -2583,6 +2625,17 @@ public static byte[] ComputeSHA512Hash(this Array data)
#endif
}

#if NET6_0_OR_GREATER
/// <summary>
/// Computes the SHA512 hash of the data array.
/// </summary>
/// <returns>512bit/64byte data hash</returns>
public static byte[] ComputeSHA512Hash<T>(this T[] data) where T : struct
{
return SHA512.HashData(data.AsByteSpan());
}
#endif

/// <summary>
/// Computes the SHA512 hash of the given string.
/// </summary>
Expand Down Expand Up @@ -2623,6 +2676,19 @@ public static uint ComputeAdler32Checksum(this Array data)
return a.Checksum;
}

#if NET6_0_OR_GREATER
/// <summary>
/// Computes a checksum of the data array using the Adler-32 algorithm (<see cref="Adler32"/>).
/// </summary>
public static uint ComputeAdler32Checksum<T>(this T[] data) where T : struct
{
var a = new Adler32();
if (data != null)
a.Update(data.AsByteSpan());
return a.Checksum;
}
#endif

/// <summary>
/// Computes a checksum of the given string using the Adler-32 algorithm (<see cref="Adler32"/>).
/// </summary>
Expand All @@ -2638,6 +2704,6 @@ public static uint ComputeAdler32Checksum(this string s)
return a.Checksum;
}

#endregion
#endregion
}
}
20 changes: 19 additions & 1 deletion src/Aardvark.Base/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,24 @@ public static string ToBase64(this byte[] data)
return Convert.ToBase64String(data);
}

#if NET6_0_OR_GREATER
/// <summary>
/// Converts the byte span to string using base-64 encoding;
/// </summary>
public static string ToBase64(this Span<byte> span)
{
return Convert.ToBase64String(span);
}

/// <summary>
/// Converts the byte span to string using base-64 encoding;
/// </summary>
public static string ToBase64(this ReadOnlySpan<byte> span)
{
return Convert.ToBase64String(span);
}
#endif

/// <summary>
/// Builds a string representing the byte arrays as hexadecimal number.
/// </summary>
Expand Down Expand Up @@ -469,6 +487,6 @@ public static byte[] HexToBytes(this string str)
return buffer;
}

#endregion
#endregion
}
}
94 changes: 94 additions & 0 deletions src/Tests/Aardvark.Base.Benchmarks/Hashes.cs
Original file line number Diff line number Diff line change
@@ -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<T>(Array data, Func<UnmanagedMemoryStream, T> 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();
}
}
}
3 changes: 2 additions & 1 deletion src/Tests/Aardvark.Base.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<StreamWriterReader>(cfg);
BenchmarkRunner.Run<HashBench>(cfg);
//BenchmarkRunner.Run<StreamWriterReader>(cfg);

//BenchmarkRunner.Run<V4fLength>();
//BenchmarkRunner.Run<V3fLeng
Expand Down

0 comments on commit 0537771

Please sign in to comment.