after merge

This commit is contained in:
2026-10-12 04:14:21 +08:00
15 changed files with 730 additions and 135 deletions
@@ -27,6 +27,8 @@ namespace Nitload.Common.HighPerformance.Memory
internal const uint MaxBufferSize = 0xFFFFFFFF - 64U;
[FieldOffset(16)] internal AllocationState State;
public static uint SizeOf { get; } = (uint)Unsafe.SizeOf<CacheLineMetadata>();
#region tlsf linked list
@@ -43,20 +45,27 @@ namespace Nitload.Common.HighPerformance.Memory
[FieldOffset(8)] public uint PositionMask;
[FieldOffset(12)] public uint SizeMask;
#endregion
[FieldOffset(16)] internal AllocationState State;
#endregion
}
#pragma warning disable IDE0032
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct Handle : IDisposable
public unsafe struct Handle : IDisposable
{
[FieldOffset(0)] private nuint _handle;
[FieldOffset(8)] private nuint _reserved;
internal Handle(
nuint handle,
nuint reserved
)
{
_handle = handle;
_reserved = reserved;
}
[Obsolete("You should not create a CacheLineHandle instance outside a L4", true)]
public Handle() { }
@@ -0,0 +1,98 @@
// -----------------------------------------------------------------------------
// file="L4.Map"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Nitload.Common.HighPerformance.Memory
{
public unsafe partial class L4
{
private const int _low_predict = 512;
private int _predict_count;
private int _swiss_reader_counter;
private int _swiss_swap_lock;
private int _swiss_writer_lock;
public partial Handle Map(
AddressSource source
)
{
ulong handle;
if (Volatile.Read(ref _predict_count) < _low_predict)
{
if (Interlocked.CompareExchange(ref _swiss_writer_lock, 1, 0) == 0)
{
try
{
Predict();
_ = Interlocked.Exchange(ref _swiss_swap_lock, 1);
while (Volatile.Read(ref _swiss_reader_counter) != 0) { }
_addressMap.SwapBuffers();
if (_addressMap.TryGet(source, out handle)) {
throw new InvalidOperationException($"Unexpected map request: [address source: 0x{source.Address:x16}; layout profile: {source.Description:x16}].");
}
}
finally
{
_ = Interlocked.Exchange(ref _swiss_swap_lock, 0);
_ = Interlocked.Exchange(ref _swiss_writer_lock, 0);
}
} else if (!TryGetAddress(source, out handle)) {
throw new InvalidOperationException($"Unexpected map request: [address source: 0x{source.Address:x16}; layout profile: {source.Description:x16}].");
}
} else if (!TryGetAddress(source, out handle)) {
throw new InvalidOperationException($"Unexpected map request: [address source: 0x{source.Address:x16}; layout profile: {source.Description:x16}].");
}
CacheLineMetadata* metadata = (CacheLineMetadata*)handle;
// process metadata, unfinished
uint offset = metadata->PositionMask;
nuint address = /*base ptr + */ offset;
_ = Interlocked.Decrement(ref _predict_count);
return new Handle(address, (nuint)handle);
}
private bool TryGetAddress(
AddressSource source,
out ulong handle
)
{
while (true)
{
while (Volatile.Read(ref _swiss_swap_lock) != 0) { }
_ = Interlocked.Increment(ref _swiss_reader_counter);
if (Volatile.Read(ref _swiss_swap_lock) == 0)
{
break;
}
_ = Interlocked.Decrement(ref _swiss_reader_counter);
}
try
{
return _addressMap.TryGet(source, out handle);
}
finally
{
_ = Interlocked.Decrement(ref _swiss_reader_counter);
}
}
}
}
@@ -0,0 +1,22 @@
// -----------------------------------------------------------------------------
// file="L4.Predict"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nitload.Common.HighPerformance.Memory
{
public unsafe partial class L4
{
internal partial void Predict() { }
}
}
@@ -8,7 +8,7 @@
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Runtime.Intrinsics.X86;
namespace Nitload.Common.HighPerformance.Memory
{
@@ -16,8 +16,6 @@ namespace Nitload.Common.HighPerformance.Memory
{
private const int BucketCapacity = 16;
private const uint CrcMagic = 0x7f1c9961;
private const int GroupCount = 1 << GroupCountLog;
private const int GroupCountLog = 10;
private const int IdealCount = GroupCount * BucketCapacity;//16384
@@ -36,17 +34,24 @@ namespace Nitload.Common.HighPerformance.Memory
}
[StructLayout(LayoutKind.Sequential)]
internal struct AddressSource
public struct AddressSource : IEquatable<AddressSource>
{
public ulong Address;
public ulong Description;
public override readonly bool Equals(
object obj
public readonly bool Equals(
AddressSource other
)
{
return obj is AddressSource source && this == source;
return this == other;
}
public override readonly bool Equals(
object? obj
)
{
return obj is AddressSource source && Equals(source);
}
public override readonly int GetHashCode()
@@ -88,127 +93,102 @@ namespace Nitload.Common.HighPerformance.Memory
}
internal sealed unsafe class SwissTable : IDisposable
internal unsafe struct SwissTable : IDisposable
{
private byte* _buffer_read;
private byte* _buffer_write;
private readonly ReaderWriterLockSlim _synchronization = new(LockRecursionPolicy.NoRecursion);
private SwissTable(byte* buffers)
private ulong _published_buffer;
private SwissTable(
byte* buffers
)
{
_buffer_read = buffers;
_published_buffer = (ulong)buffers;
_buffer_write = buffers + TableBufferSize;
InitializeBuffer(_buffer_read);
InitializeBuffer(buffers);
InitializeBuffer(_buffer_write);
}
internal static SwissTable Create(byte* buffers)
internal byte* ReadBufferForLockFreeBenchmark => (byte*)Volatile.Read(ref _published_buffer);
internal readonly byte* WriteBufferForLockFreeBenchmark => _buffer_write;
public void Dispose()
{
_ = Interlocked.Exchange(ref _published_buffer, 0);
_buffer_write = null;
}
/// <summary>
/// Publishes the write buffer. The caller must provide the phase barrier around this
/// operation.
/// </summary>
public void SwapBuffers()
{
ulong previousBuffer = Interlocked.Exchange(ref _published_buffer, (ulong)_buffer_write);
_buffer_write = (byte*)previousBuffer;
}
/// <summary>
/// Writes to the inactive buffer. The caller must serialize all writes.
/// </summary>
public readonly bool TryAddOrSet(
AddressSource key,
ulong value
)
{
if (Sse42.IsSupported) {
return SwissWriterX86.TryAddOrSet(_buffer_write, key, value);
}
throw new NotImplementedException("The Swiss backend is not implemented for this ISA.");
}
/// <summary>
/// Reads the published buffer. The caller must prevent swaps until all reads finish.
/// </summary>
public bool TryGet(
AddressSource key,
out ulong value
)
{
byte* publishedBuffer = (byte*)Volatile.Read(ref _published_buffer);
if (Sse42.IsSupported) {
return SwissReaderX86.TryGet(publishedBuffer, key, out value);
}
throw new NotImplementedException("The Swiss backend is not implemented for this ISA.");
}
/// <summary>
/// Removes from the inactive buffer. The caller must serialize all writes.
/// </summary>
public bool TryRemove(
AddressSource key,
out ulong value
)
{
if (Sse42.IsSupported) {
return SwissWriterX86.TryRemove(_buffer_write, key, out value);
}
throw new NotImplementedException("The Swiss backend is not implemented for this ISA.");
}
internal static SwissTable Create(
byte* buffers
)
{
ArgumentNullException.ThrowIfNull(buffers);
return new SwissTable(buffers);
}
/// <summary>
/// Reads the published buffer. Callers must prevent a swap while the read is in progress.
/// </summary>
public bool TryGet(AddressSource key, out ulong value)
private static void InitializeBuffer(
byte* buffer
)
{
_synchronization.EnterReadLock();
try
{
return SwissReaderX86.TryGet(_buffer_read, key, out value);
}
finally
{
_synchronization.ExitReadLock();
}
}
/// <summary>
/// Writes to the inactive buffer. Calls must be serialized by the caller.
/// </summary>
public bool TryAddOrSet(AddressSource key, ulong value)
{
_synchronization.EnterWriteLock();
try
{
return SwissWriterX86.TryAddOrSet(_buffer_write, key, value);
}
finally
{
_synchronization.ExitWriteLock();
}
}
/// <summary>
/// Writes to the inactive buffer. Calls must be serialized by the caller.
/// </summary>
public bool TryRemove(AddressSource key, out ulong value)
{
_synchronization.EnterWriteLock();
try
{
return SwissWriterX86.TryRemove(_buffer_write, key, out value);
}
finally
{
_synchronization.ExitWriteLock();
}
}
/// <summary>
/// Clears the inactive buffer. Call only after all readers of its previous generation have quiesced.
/// </summary>
public void PrepareWriteBuffer()
{
_synchronization.EnterWriteLock();
try
{
InitializeBuffer(_buffer_write);
}
finally
{
_synchronization.ExitWriteLock();
}
}
/// <summary>
/// Publishes the write buffer. The caller must provide the phase barrier around this operation.
/// </summary>
public void SwapBuffers()
{
_synchronization.EnterWriteLock();
try
{
byte* temporary = _buffer_read;
_buffer_read = _buffer_write;
_buffer_write = temporary;
}
finally
{
_synchronization.ExitWriteLock();
}
}
public void Dispose()
{
_synchronization.EnterWriteLock();
try
{
_buffer_read = null;
_buffer_write = null;
}
finally
{
_synchronization.ExitWriteLock();
}
_synchronization.Dispose();
}
private static void InitializeBuffer(byte* buffer)
{
Unsafe.InitBlock(buffer, (byte)SwissControl.Empty, (uint)IdealCount);
Unsafe.InitBlock(buffer, (byte)SwissControl.Empty, IdealCount);
}
}
@@ -6,6 +6,7 @@
// -----------------------------------------------------------------------------
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@@ -14,7 +15,25 @@ namespace Nitload.Common.HighPerformance.Memory
public partial class L4
{
private readonly unsafe struct SwissWriterX86
private const uint CrcMagic = 0x7f1c9961;
internal static class SwissHashX64
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Compute(
AddressSource key
)
{
ulong hash = CrcMagic;
hash = Sse42.X64.Crc32(hash, key.Address);
hash = Sse42.X64.Crc32(hash, key.Description);
return (uint)hash;
}
}
internal readonly unsafe struct SwissWriterX86
{
public static bool TryAddOrSet(
@@ -26,7 +45,7 @@ namespace Nitload.Common.HighPerformance.Memory
Vector128<byte>* control_bytes = (Vector128<byte>*)buffer;
SwissBucket* buckets = (SwissBucket*)(buffer + IdealCount);
uint hash = Sse42.Crc32(CrcMagic, (uint)key.GetHashCode());
uint hash = SwissHashX64.Compute(key);
uint begin = hash % GroupCount;
uint first_tomb_group = 0;
@@ -108,7 +127,7 @@ namespace Nitload.Common.HighPerformance.Memory
{
Vector128<byte>* control_bytes = (Vector128<byte>*)buffer;
SwissBucket* buckets = (SwissBucket*)(buffer + IdealCount);
uint hash = Sse42.Crc32(CrcMagic, (uint)key.GetHashCode());
uint hash = SwissHashX64.Compute(key);
uint begin = hash % GroupCount;
byte target_hash = (byte)((hash >> GroupCountLog) & ~(1 << 7));
@@ -149,7 +168,7 @@ namespace Nitload.Common.HighPerformance.Memory
}
private readonly unsafe struct SwissReaderX86
internal readonly unsafe struct SwissReaderX86
{
public static bool TryGet(
@@ -161,7 +180,7 @@ namespace Nitload.Common.HighPerformance.Memory
Vector128<byte>* control_bytes = (Vector128<byte>*)buffer;
SwissBucket* buckets = (SwissBucket*)(buffer + IdealCount);
uint hash = Sse42.Crc32(CrcMagic, (uint)key.GetHashCode());
uint hash = SwissHashX64.Compute(key);
uint begin = hash % GroupCount;
byte target_hash = (byte)((hash >> GroupCountLog) & ~(1 << 7));
@@ -186,7 +205,9 @@ namespace Nitload.Common.HighPerformance.Memory
Vector128<byte> target_empty_v128 = Vector128.Create((byte)SwissControl.Empty);
Vector128<byte> cmp_empty = Sse2.CompareEqual(control_bytes_v128, target_empty_v128);
if (Sse2.MoveMask(cmp_empty) != 0)
{
break;
}
begin = (begin + 1) & (GroupCount - 1);
}
@@ -54,6 +54,10 @@ namespace Nitload.Common.HighPerformance.Memory
GC.SuppressFinalize(this);
}
public partial Handle Map(
AddressSource source
);
internal void Collect()
{
ObjectDisposedException.ThrowIf(_disposed, this);
@@ -77,5 +81,7 @@ namespace Nitload.Common.HighPerformance.Memory
return _bufferAllocator.TryAlloc(size, out handle);
}
internal partial void Predict();
}
}