[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:
parent
80235c9a3d
commit
2c73775389
|
|
@ -45,8 +45,8 @@ public static class Program
|
||||||
private static int TestIterations = 1;
|
private static int TestIterations = 1;
|
||||||
private static int BenchmarkSamples = 1; // Debug: single sample, fast iteration
|
private static int BenchmarkSamples = 1; // Debug: single sample, fast iteration
|
||||||
#else
|
#else
|
||||||
private static int WarmupIterations = 100; //5000
|
private static int WarmupIterations = 5000; //5000
|
||||||
private static int TestIterations = 10; //1000
|
private static int TestIterations = 1000; //1000
|
||||||
private static int BenchmarkSamples = 3;
|
private static int BenchmarkSamples = 3;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -365,8 +365,10 @@ public static partial class AcBinaryDeserializer
|
||||||
{
|
{
|
||||||
var byteLen = length * 2;
|
var byteLen = length * 2;
|
||||||
EnsureAvailable(byteLen);
|
EnsureAvailable(byteLen);
|
||||||
|
|
||||||
var chars = MemoryMarshal.Cast<byte, char>(_buffer.AsSpan(_position, byteLen));
|
var chars = MemoryMarshal.Cast<byte, char>(_buffer.AsSpan(_position, byteLen));
|
||||||
var value = new string(chars);
|
var value = new string(chars);
|
||||||
|
|
||||||
_position += byteLen;
|
_position += byteLen;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,13 +152,17 @@ public static partial class AcBinaryDeserializer
|
||||||
{
|
{
|
||||||
Input = input;
|
Input = input;
|
||||||
Input.Initialize(out _buffer, out _position, out _bufferLength);
|
Input.Initialize(out _buffer, out _position, out _bufferLength);
|
||||||
|
|
||||||
_useStringCaching = Options.UseStringCaching;
|
_useStringCaching = Options.UseStringCaching;
|
||||||
_maxCachedStringLength = Options.MaxCachedStringLength;
|
_maxCachedStringLength = Options.MaxCachedStringLength;
|
||||||
|
|
||||||
if (_useStringCaching) GetOrCreateStringCache();
|
if (_useStringCaching) GetOrCreateStringCache();
|
||||||
|
|
||||||
_internCache = null;
|
_internCache = null;
|
||||||
HasMetadata = false;
|
HasMetadata = false;
|
||||||
IsMergeMode = false;
|
IsMergeMode = false;
|
||||||
RemoveOrphanedItems = false;
|
RemoveOrphanedItems = false;
|
||||||
|
|
||||||
FastWire = Options.WireMode == WireMode.Fast;
|
FastWire = Options.WireMode == WireMode.Fast;
|
||||||
ChainTracker = null;
|
ChainTracker = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -290,9 +290,12 @@ public static partial class AcBinarySerializer
|
||||||
{
|
{
|
||||||
// IMPORTANT: base.Reset sets Options first, so derived code can use Options-derived properties
|
// IMPORTANT: base.Reset sets Options first, so derived code can use Options-derived properties
|
||||||
base.Reset(options);
|
base.Reset(options);
|
||||||
|
|
||||||
HasPropertyFilter = Options.PropertyFilter != null;
|
HasPropertyFilter = Options.PropertyFilter != null;
|
||||||
|
|
||||||
InternBit = 1 << (int)Options.UseStringInterning;
|
InternBit = 1 << (int)Options.UseStringInterning;
|
||||||
HasStringInterning = Options.UseStringInterning != StringInterningMode.None;
|
HasStringInterning = Options.UseStringInterning != StringInterningMode.None;
|
||||||
|
|
||||||
FastWire = Options.WireMode == WireMode.Fast;
|
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)
|
// UTF-16: char count (VarUInt) + raw char data (zero-encoding memcopy)
|
||||||
var charLen = value.Length;
|
var charLen = value.Length;
|
||||||
var byteLen = charLen * 2;
|
var byteLen = charLen * 2;
|
||||||
|
|
||||||
WriteVarUInt((uint)charLen);
|
WriteVarUInt((uint)charLen);
|
||||||
EnsureCapacity(byteLen);
|
EnsureCapacity(byteLen);
|
||||||
|
|
||||||
MemoryMarshal.AsBytes(value.AsSpan()).CopyTo(_buffer.AsSpan(_position, byteLen));
|
MemoryMarshal.AsBytes(value.AsSpan()).CopyTo(_buffer.AsSpan(_position, byteLen));
|
||||||
_position += byteLen;
|
_position += byteLen;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -681,9 +687,11 @@ public static partial class AcBinarySerializer
|
||||||
|
|
||||||
// Non-ASCII fallback: safe rewind (no Grow happened since pre-allocate)
|
// Non-ASCII fallback: safe rewind (no Grow happened since pre-allocate)
|
||||||
_position = savedPosition;
|
_position = savedPosition;
|
||||||
|
|
||||||
var byteCount = Utf8NoBom.GetByteCount(value);
|
var byteCount = Utf8NoBom.GetByteCount(value);
|
||||||
EnsureCapacity(VarUIntSize((uint)byteCount) + byteCount);
|
EnsureCapacity(VarUIntSize((uint)byteCount) + byteCount);
|
||||||
WriteVarUIntUnsafe((uint)byteCount);
|
WriteVarUIntUnsafe((uint)byteCount);
|
||||||
|
|
||||||
Utf8NoBom.GetBytes(value.AsSpan(), _buffer.AsSpan(_position, byteCount));
|
Utf8NoBom.GetBytes(value.AsSpan(), _buffer.AsSpan(_position, byteCount));
|
||||||
_position += byteCount;
|
_position += byteCount;
|
||||||
}
|
}
|
||||||
|
|
@ -699,25 +707,21 @@ public static partial class AcBinarySerializer
|
||||||
|
|
||||||
public void WriteFixStrDirect(string value)
|
public void WriteFixStrDirect(string value)
|
||||||
{
|
{
|
||||||
// Compute UTF-8 byte count up front. FixStr opcode encodes byte count (5-bit field, ≤31).
|
var length = value.Length;
|
||||||
// For ASCII: byte count = char count; for non-ASCII: byte count > char count.
|
EnsureCapacity(1 + length);
|
||||||
// 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 destSpan = _buffer.AsSpan(_position + 1, length);
|
||||||
var byteCount = Utf8NoBom.GetByteCount(value);
|
var status = Ascii.FromUtf16(value.AsSpan(), destSpan, out var bytesWritten);
|
||||||
if (byteCount <= BinaryTypeCode.FixStrMaxLength)
|
|
||||||
|
if (status == OperationStatus.Done && bytesWritten == length)
|
||||||
{
|
{
|
||||||
EnsureCapacity(1 + byteCount);
|
_buffer[_position] = BinaryTypeCode.EncodeFixStr(length);
|
||||||
_buffer[_position++] = BinaryTypeCode.EncodeFixStr(byteCount);
|
_position += 1 + length;
|
||||||
Utf8NoBom.GetBytes(value.AsSpan(), _buffer.AsSpan(_position, byteCount));
|
|
||||||
_position += byteCount;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_buffer[_position++] = BinaryTypeCode.String;
|
_buffer[_position++] = BinaryTypeCode.String;
|
||||||
EnsureCapacity(VarUIntSize((uint)byteCount) + byteCount);
|
WriteStringUtf8Internal(value);
|
||||||
WriteVarUIntUnsafe((uint)byteCount);
|
|
||||||
Utf8NoBom.GetBytes(value.AsSpan(), _buffer.AsSpan(_position, byteCount));
|
|
||||||
_position += byteCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue