Seeing your latest PR and calling AspSpan I wondered how MDBValue was designed.
Here is what Opus is suggesting:
MDBValue Performance Optimizations
This document outlines potential performance optimizations for the MDBValue struct in LightningDB using modern .NET marshalling and memory helpers.
Current Implementation
The existing MDBValue struct is a managed version of the native MDB_val type, designed to be blittable for P/Invoke marshalling.
Proposed Optimizations
1. Use MemoryMarshal for AsSpan()
The current implementation is efficient, but MemoryMarshal.CreateReadOnlySpan provides a more explicit approach:
using System. Runtime.InteropServices;
public ReadOnlySpan<byte> AsSpan() => MemoryMarshal.CreateReadOnlySpan(ref *data, (int)size);
2. Add a Writable Span Accessor
For scenarios requiring write access to the buffer:
public Span<byte> AsWritableSpan() => new Span<byte>(data, (int)size);
3. Add SkipLocalsInit Attribute
Skip zero-initialization for performance-critical structs:
using System.Runtime.CompilerServices;
[SkipLocalsInit]
public unsafe struct MDBValue
4. Add Typed Accessors with MemoryMarshal
For common use cases where the data type is known:
public T Read<T>() where T : unmanaged => MemoryMarshal. Read<T>(AsSpan());
public ReadOnlySpan<T> AsSpan<T>() where T : unmanaged => MemoryMarshal.Cast<byte, T>(AsSpan());
5. Use Unsafe.CopyBlockUnaligned for Bulk Copies
For frequent data copying scenarios:
using System.Runtime.CompilerServices;
public void CopyTo(Span<byte> destination)
{
Unsafe.CopyBlockUnaligned(ref MemoryMarshal.GetReference(destination), ref *data, (uint)size);
}
Complete Optimized Implementation
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LightningDB;
[SkipLocalsInit]
public unsafe struct MDBValue
{
internal MDBValue(int bufferSize, byte* pinnedOrStackAllocBuffer)
{
size = bufferSize;
data = pinnedOrStackAllocBuffer;
}
//DO NOT REORDER
internal nint size;
//DO NOT REORDER
internal byte* data;
[MethodImpl(MethodImplOptions. AggressiveInlining)]
public ReadOnlySpan<byte> AsSpan() => new(data, (int)size);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> AsWritableSpan() => new(data, (int)size);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Read<T>() where T : unmanaged => MemoryMarshal.Read<T>(AsSpan());
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> Cast<T>() where T : unmanaged => MemoryMarshal.Cast<byte, T>(AsSpan());
public byte[] CopyToNewArray() => AsSpan().ToArray();
public void CopyTo(Span<byte> destination)
{
Unsafe.CopyBlockUnaligned(
ref MemoryMarshal.GetReference(destination),
ref *data,
(uint)size);
}
}
Key Benefits
| Optimization |
Benefit |
AggressiveInlining |
Eliminates method call overhead by hinting the JIT to inline small methods |
SkipLocalsInit |
Avoids zero-initialization overhead (safe when fields are always assigned in constructor) |
| Generic typed accessors |
Avoids manual casting and leverages MemoryMarshal for zero-copy reinterpretation |
Unsafe.CopyBlockUnaligned |
Can be faster than Span.CopyTo for known-size copies |
Considerations
- The struct layout must remain unchanged to maintain blittability for P/Invoke
SkipLocalsInit is safe here because all fields are assigned in the constructor
- Generic methods with
unmanaged constraint ensure type safety while enabling high-performance scenarios
Seeing your latest PR and calling AspSpan I wondered how MDBValue was designed.
Here is what Opus is suggesting:
MDBValue Performance Optimizations
This document outlines potential performance optimizations for the
MDBValuestruct in LightningDB using modern .NET marshalling and memory helpers.Current Implementation
The existing
MDBValuestruct is a managed version of the nativeMDB_valtype, designed to be blittable for P/Invoke marshalling.Proposed Optimizations
1. Use
MemoryMarshalforAsSpan()The current implementation is efficient, but
MemoryMarshal.CreateReadOnlySpanprovides a more explicit approach:2. Add a Writable Span Accessor
For scenarios requiring write access to the buffer:
3. Add
SkipLocalsInitAttributeSkip zero-initialization for performance-critical structs:
4. Add Typed Accessors with
MemoryMarshalFor common use cases where the data type is known:
5. Use
Unsafe.CopyBlockUnalignedfor Bulk CopiesFor frequent data copying scenarios:
Complete Optimized Implementation
Key Benefits
AggressiveInliningSkipLocalsInitMemoryMarshalfor zero-copy reinterpretationUnsafe.CopyBlockUnalignedSpan.CopyTofor known-size copiesConsiderations
SkipLocalsInitis safe here because all fields are assigned in the constructorunmanagedconstraint ensure type safety while enabling high-performance scenarios