Reduce Unsafe usage in TryWriteSignificand/TryHash helpers#127921
Reduce Unsafe usage in TryWriteSignificand/TryHash helpers#127921EgorBo wants to merge 2 commits intodotnet:mainfrom
Conversation
Replace MemoryMarshal.GetReference + Unsafe.WriteUnaligned with
BinaryPrimitives.WriteXxxBigEndian/LittleEndian (or MemoryMarshal.Write
for NFloat where the underlying type is arch-conditional) in:
* Decimal.IFloatingPoint<decimal>.TryWriteSignificand{Big,Little}Endian
* NFloat.IFloatingPoint<NFloat>.TryWrite{Exponent,Significand}{Big,Little}Endian
* XxHash3.TryHash
* XxHash128.WriteBigEndian128 (used by TryHash / GetCurrentHash)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-numerics |
|
@jkotas @tannergooding so what would be the way to replace MemoryMarshal.Write here?
UPD: ah,
One of the motivational use cases for #127606 api proposal. |
|
Change the impl on bool IFloatingPoint<T>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten)
{
...
}to: internal bool TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten)
{
...
}
bool IFloatingPoint<T>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten)
{
return TryWriteExponentBigEndian(destination, out bytesWritten);
}and then change the bool IFloatingPoint<NFloat>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten)
{
return _value.TryWriteExponentBigEndian(destination, out int bytesWritten);
}Which avoids all the ifdef and other messiness. It's just one of the downsides of these interfaces being explicitly implemented. The alternative is to define a generic helper and call that, but that's extra generic bloat: static bool TryWriteExponentBigEndian<T>(T value, Span<byte> destination, out int bytesWritten)
where T : IFloatingPoint<T>
{
return value.TryWriteExponentBigEndian(destination, out int bytesWritten);
} |
Per review feedback, expose internal TryWrite{Exponent,Significand}{Big,Little}Endian
methods on Single/Double next to the explicit interface implementations,
and have NFloat's IFloatingPoint<NFloat>.TryWriteXxx forward directly to
_value.TryWriteXxx. This removes the TARGET_32BIT ifdefs, manual
ReverseEndianness, and the BitConverter.TryWriteBytes + ThrowHelper
boilerplate from NFloat.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Note This comment was generated with the help of GitHub Copilot. NFloat @EgorBot -linux_amd -osx_arm64 using System;
using System.Numerics;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);
public class Bench
{
private NFloat _value = (NFloat)3.14159265358979;
private byte[] _buffer = new byte[16];
[Benchmark]
public bool SignificandBE() => WriteSignificandBE(_value, _buffer);
[Benchmark]
public bool SignificandLE() => WriteSignificandLE(_value, _buffer);
[Benchmark]
public bool ExponentBE() => WriteExponentBE(_value, _buffer);
[Benchmark]
public bool ExponentLE() => WriteExponentLE(_value, _buffer);
private static bool WriteSignificandBE<T>(T v, Span<byte> buf) where T : IFloatingPoint<T>
=> v.TryWriteSignificandBigEndian(buf, out _);
private static bool WriteSignificandLE<T>(T v, Span<byte> buf) where T : IFloatingPoint<T>
=> v.TryWriteSignificandLittleEndian(buf, out _);
private static bool WriteExponentBE<T>(T v, Span<byte> buf) where T : IFloatingPoint<T>
=> v.TryWriteExponentBigEndian(buf, out _);
private static bool WriteExponentLE<T>(T v, Span<byte> buf) where T : IFloatingPoint<T>
=> v.TryWriteExponentLittleEndian(buf, out _);
} |
|
Note This comment was generated with the help of GitHub Copilot. XxHash128 incremental @EgorBot -linux_amd -osx_arm64 using System;
using System.IO.Hashing;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(Bench).Assembly).Run(args);
public class Bench
{
[Params(8, 32, 128, 1024)]
public int Length { get; set; }
private byte[] _data = default!;
private byte[] _dest = new byte[16];
private XxHash128 _hash = default!;
[GlobalSetup]
public void Setup()
{
_data = new byte[Length];
new Random(42).NextBytes(_data);
_hash = new XxHash128();
}
[Benchmark]
public int AppendAndGetCurrentHash()
{
_hash.Reset();
_hash.Append(_data);
return _hash.GetCurrentHash(_dest);
}
[Benchmark]
public int StaticTryHash()
{
XxHash128.TryHash(_data, _dest, out int written);
return written;
}
} |

No description provided.