[LOADED_DOCS: 2 files, no new loads]

Optimize FastWire string (de)serialization and benchmarks

- Increased release benchmark iterations for more robust testing.
- Improved FastWire string deserialization with zero-copy UTF-16.
- Set FastWire and string caching options during context init/reset.
- Optimized FastWire string serialization for direct UTF-16 copy.
- Enhanced non-ASCII string fallback to use Utf8NoBom encoding.
- Refactored WriteFixStr for efficient ASCII and fallback handling.
This commit is contained in:
Loretta 2026-05-04 07:32:29 +02:00
parent 80235c9a3d
commit 2c73775389
4 changed files with 26 additions and 16 deletions

View File

@ -45,8 +45,8 @@ public static class Program
private static int TestIterations = 1;
private static int BenchmarkSamples = 1; // Debug: single sample, fast iteration
#else
private static int WarmupIterations = 100; //5000
private static int TestIterations = 10; //1000
private static int WarmupIterations = 5000; //5000
private static int TestIterations = 1000; //1000
private static int BenchmarkSamples = 3;
#endif

View File

@ -365,8 +365,10 @@ public static partial class AcBinaryDeserializer
{
var byteLen = length * 2;
EnsureAvailable(byteLen);
var chars = MemoryMarshal.Cast<byte, char>(_buffer.AsSpan(_position, byteLen));
var value = new string(chars);
_position += byteLen;
return value;
}

View File

@ -152,13 +152,17 @@ public static partial class AcBinaryDeserializer
{
Input = input;
Input.Initialize(out _buffer, out _position, out _bufferLength);
_useStringCaching = Options.UseStringCaching;
_maxCachedStringLength = Options.MaxCachedStringLength;
if (_useStringCaching) GetOrCreateStringCache();
_internCache = null;
HasMetadata = false;
IsMergeMode = false;
RemoveOrphanedItems = false;
FastWire = Options.WireMode == WireMode.Fast;
ChainTracker = null;
}

View File

@ -290,9 +290,12 @@ public static partial class AcBinarySerializer
{
// IMPORTANT: base.Reset sets Options first, so derived code can use Options-derived properties
base.Reset(options);
HasPropertyFilter = Options.PropertyFilter != null;
InternBit = 1 << (int)Options.UseStringInterning;
HasStringInterning = Options.UseStringInterning != StringInterningMode.None;
FastWire = Options.WireMode == WireMode.Fast;
}
@ -658,10 +661,13 @@ public static partial class AcBinarySerializer
// UTF-16: char count (VarUInt) + raw char data (zero-encoding memcopy)
var charLen = value.Length;
var byteLen = charLen * 2;
WriteVarUInt((uint)charLen);
EnsureCapacity(byteLen);
MemoryMarshal.AsBytes(value.AsSpan()).CopyTo(_buffer.AsSpan(_position, byteLen));
_position += byteLen;
return;
}
@ -681,9 +687,11 @@ public static partial class AcBinarySerializer
// Non-ASCII fallback: safe rewind (no Grow happened since pre-allocate)
_position = savedPosition;
var byteCount = Utf8NoBom.GetByteCount(value);
EnsureCapacity(VarUIntSize((uint)byteCount) + byteCount);
WriteVarUIntUnsafe((uint)byteCount);
Utf8NoBom.GetBytes(value.AsSpan(), _buffer.AsSpan(_position, byteCount));
_position += byteCount;
}
@ -699,25 +707,21 @@ public static partial class AcBinarySerializer
public void WriteFixStrDirect(string value)
{
// Compute UTF-8 byte count up front. FixStr opcode encodes byte count (5-bit field, ≤31).
// For ASCII: byte count = char count; for non-ASCII: byte count > char count.
// Bonus over the prior ASCII-only path: short non-ASCII strings (e.g. 8-char Hungarian
// ≈ 12 bytes) now also fit in FixStr and save the String-marker + VarUInt overhead.
var byteCount = Utf8NoBom.GetByteCount(value);
if (byteCount <= BinaryTypeCode.FixStrMaxLength)
var length = value.Length;
EnsureCapacity(1 + length);
var destSpan = _buffer.AsSpan(_position + 1, length);
var status = Ascii.FromUtf16(value.AsSpan(), destSpan, out var bytesWritten);
if (status == OperationStatus.Done && bytesWritten == length)
{
EnsureCapacity(1 + byteCount);
_buffer[_position++] = BinaryTypeCode.EncodeFixStr(byteCount);
Utf8NoBom.GetBytes(value.AsSpan(), _buffer.AsSpan(_position, byteCount));
_position += byteCount;
_buffer[_position] = BinaryTypeCode.EncodeFixStr(length);
_position += 1 + length;
}
else
{
_buffer[_position++] = BinaryTypeCode.String;
EnsureCapacity(VarUIntSize((uint)byteCount) + byteCount);
WriteVarUIntUnsafe((uint)byteCount);
Utf8NoBom.GetBytes(value.AsSpan(), _buffer.AsSpan(_position, byteCount));
_position += byteCount;
WriteStringUtf8Internal(value);
}
}