diff --git a/AyCode.Core.Serializers.Console/Program.cs b/AyCode.Core.Serializers.Console/Program.cs
index 91f308b..27acf85 100644
--- a/AyCode.Core.Serializers.Console/Program.cs
+++ b/AyCode.Core.Serializers.Console/Program.cs
@@ -43,13 +43,18 @@ public static class Program
private static readonly UTF8Encoding Utf8NoBom = new(encoderShouldEmitUTF8Identifier: false);
+ #if DEBUG
+ private static int WarmupIterations = 5;
+ private static int TestIterations = 10;
+ #else
private static int WarmupIterations = 2000;
private static int TestIterations = 1000;
+ #endif
public static void Main(string[] args)
{
// Set console encoding to UTF-8 for proper Unicode character display
- System.Console.OutputEncoding = System.Text.Encoding.UTF8;
+ System.Console.OutputEncoding = Encoding.UTF8;
var mode = args.Length > 0 ? args[0].ToLower() : "all";
@@ -779,8 +784,8 @@ public static class Program
}
System.Console.WriteLine($"└{"─".PadRight(6, '─')}─{"─".PadRight(27, '─')}┴{"─".PadRight(12, '─')}┴{"─".PadRight(14, '─')}┴{"─".PadRight(14, '─')}┴{"─".PadRight(13, '─')}┘");
- System.Console.WriteLine($"GrowBufferCount: {AyCode.Core.Serializers.Binaries.AcBinarySerializer.BinarySerializationContext.GrowBufferCount}");
- System.Console.WriteLine($"GrowBufferTotalBytes: {AyCode.Core.Serializers.Binaries.AcBinarySerializer.BinarySerializationContext.GrowBufferTotalBytes:N0} bytes");
+ System.Console.WriteLine($"GrowBufferCount: {AcBinarySerializer.GrowBufferCount}");
+ System.Console.WriteLine($"GrowBufferTotalBytes: {AcBinarySerializer.GrowBufferTotalBytes:N0} bytes");
}
// Summary: Best serializer for each category
@@ -979,8 +984,12 @@ public static class Program
sb.AppendLine($" {SerializerAcBinaryDefault} vs {SerializerMessagePack}: Size {sizePct:+0;-0}% │ Ser {serPct:+0;-0}% │ Des {desPct:+0;-0}% │ RT {rtPct:+0;-0}%");
}
+
+ sb.AppendLine($"GrowBufferCount: {AcBinarySerializer.GrowBufferCount}");
+ sb.AppendLine($"GrowBufferTotalBytes: {AcBinarySerializer.GrowBufferTotalBytes:N0} bytes");
}
+
// Summary comparison
sb.AppendLine();
sb.AppendLine($"=== {SerializerAcBinaryDefault} vs {SerializerMessagePack} (Overall) ===");
diff --git a/AyCode.Core/AyCode.Core.csproj b/AyCode.Core/AyCode.Core.csproj
index 4983021..8cbe66a 100644
--- a/AyCode.Core/AyCode.Core.csproj
+++ b/AyCode.Core/AyCode.Core.csproj
@@ -25,4 +25,8 @@
+
+
+
+
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
index c85c47f..bc364b2 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.BinarySerializationContext.cs
@@ -45,6 +45,20 @@ public static partial class AcBinarySerializer
}
}
+ public static int GrowBufferCount =>
+#if DEBUG
+ BinarySerializationContext.GrowBufferCount;
+#else
+ -1;
+#endif
+
+ public static long GrowBufferTotalBytes =>
+#if DEBUG
+ BinarySerializationContext.GrowBufferTotalBytes;
+#else
+ -1;
+#endif
+
///
/// Binary serialization context. Public for generated serializers.
///
@@ -60,6 +74,20 @@ public static partial class AcBinarySerializer
private int _position;
private int _initialBufferSize;
+#if DEBUG
+ ///
+ /// Counts how many times GrowBuffer was called during serialization.
+ /// Used for benchmarking buffer allocation efficiency.
+ ///
+ public static int GrowBufferCount { get; set; }
+
+ ///
+ /// Total bytes allocated by GrowBuffer during serialization.
+ /// Used for benchmarking buffer allocation efficiency.
+ ///
+ public static long GrowBufferTotalBytes { get; set; }
+#endif
+
// Use shared reference tracker from AcSerializerCommon
//private readonly AcSerializerCommon.SerializationReferenceTracker _refTracker = new();
@@ -102,7 +130,7 @@ public static partial class AcBinarySerializer
public byte MinStringInternLength => Options.MinStringInternLength;
public byte MaxStringInternLength => Options.MaxStringInternLength;
public BinaryPropertyFilter? PropertyFilter => Options.PropertyFilter;
-
+
///
/// Cached check for PropertyFilter != null. Set in Reset() to avoid property getter in hot loop.
///
@@ -120,7 +148,7 @@ public static partial class AcBinarySerializer
///
/// Factory for creating BinarySerializeTypeMetadata instances.
///
- protected override Func MetadataFactory
+ protected override Func MetadataFactory
=> static t => new BinarySerializeTypeMetadata(t, HasJsonIgnoreAttribute);
public override void Reset(AcBinarySerializerOptions options)
@@ -132,6 +160,9 @@ public static partial class AcBinarySerializer
_initialBufferSize = Math.Max(Options.InitialBufferCapacity, MinBufferSize);
HasPropertyFilter = Options.PropertyFilter != null;
+ // NOTE: GrowBufferCount és GrowBufferTotalBytes NEM nullázódik itt!
+ // Kumulatívan gyűjtjük a benchmark során.
+
if (_buffer.Length < _initialBufferSize)
{
ArrayPool.Shared.Return(_buffer);
@@ -161,6 +192,10 @@ public static partial class AcBinarySerializer
ArrayPool.Shared.Return(_propertyStateBuffer);
_propertyStateBuffer = null;
}
+
+ // NOTE: GrowBufferCount �s GrowBufferTotalBytes nem null�z�dik itt,
+ // hogy a m�r�sek v�g�n ki tudj�k �rni az �rt�keket.
+ // Csak a Reset() met�dusban null�z�dnak minden �j fut�s elej�n.
}
@@ -183,7 +218,7 @@ public static partial class AcBinarySerializer
ArrayPool.Shared.Return(_propertyStateBuffer);
_propertyStateBuffer = null;
}
-
+
}
#region String Interning
@@ -409,6 +444,11 @@ public static partial class AcBinarySerializer
_buffer.AsSpan(0, _position).CopyTo(newBuffer);
ArrayPool.Shared.Return(_buffer);
_buffer = newBuffer;
+
+ #if DEBUG
+ GrowBufferCount++;
+ GrowBufferTotalBytes += newSize;
+ #endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -516,7 +556,7 @@ public static partial class AcBinarySerializer
_buffer[position] = (byte)value;
return;
}
-
+
// Multi-byte case - need to shift buffer if new encoding is longer
// For simplicity, we'll rewrite from the position
// This is rare for property counts
@@ -535,10 +575,10 @@ public static partial class AcBinarySerializer
pos++;
}
currentSize++; // Include final byte without continuation bit
-
+
// Calculate new size needed
var newSize = GetVarUIntSize(value);
-
+
if (newSize == currentSize)
{
// Same size - just overwrite
@@ -556,7 +596,7 @@ public static partial class AcBinarySerializer
var delta = currentSize - newSize;
Array.Copy(_buffer, position + currentSize, _buffer, position + newSize, _position - position - currentSize);
_position -= delta;
-
+
var tempPos = position;
while (value >= 0x80)
{
@@ -572,7 +612,7 @@ public static partial class AcBinarySerializer
EnsureCapacity(delta);
Array.Copy(_buffer, position + currentSize, _buffer, position + newSize, _position - position - currentSize);
_position += delta;
-
+
var tempPos = position;
while (value >= 0x80)
{
@@ -687,7 +727,7 @@ public static partial class AcBinarySerializer
_position += value.Length;
return;
}
-
+
// Standard path for multi-byte UTF8
var byteCount = Utf8NoBom.GetByteCount(value);
WriteVarUInt((uint)byteCount);
@@ -821,7 +861,7 @@ public static partial class AcBinarySerializer
{
EnsureCapacity(source.Length);
var destination = _buffer.AsSpan(_position, source.Length);
-
+
if (Vector.IsHardwareAccelerated && source.Length >= Vector.Count * 2)
{
CopyWithSimd(source, destination);
@@ -830,7 +870,7 @@ public static partial class AcBinarySerializer
{
source.CopyTo(destination);
}
-
+
_position += source.Length;
}
@@ -843,7 +883,7 @@ public static partial class AcBinarySerializer
var vectorSize = Vector.Count;
var i = 0;
var length = source.Length;
-
+
// Process full vectors
var vectorCount = length / vectorSize;
for (var v = 0; v < vectorCount; v++)
@@ -852,7 +892,7 @@ public static partial class AcBinarySerializer
vec.CopyTo(destination.Slice(i, vectorSize));
i += vectorSize;
}
-
+
// Copy remaining bytes
if (i < length)
{
@@ -890,7 +930,7 @@ public static partial class AcBinarySerializer
// Guid is 16 bytes, perfect for SIMD
var byteLength = values.Length * 16;
EnsureCapacity(byteLength);
-
+
for (var i = 0; i < values.Length; i++)
{
values[i].TryWriteBytes(_buffer.AsSpan(_position, 16));
@@ -903,7 +943,7 @@ public static partial class AcBinarySerializer
#region Header and Metadata
private int _headerPosition;
-
+
// Footer-based string interning: no estimation or shifting needed
// Header: [version][flags][footerPosition (4 bytes, only if string interning)]
// Body: data with StringInterned indices
@@ -916,7 +956,7 @@ public static partial class AcBinarySerializer
public int EstimateHeaderPayloadSize()
{
var size = 0;
-
+
// Only property names are in header now
if (UseMetadata && _propertyNameList is { Count: > 0 })
{
@@ -928,7 +968,7 @@ public static partial class AcBinarySerializer
size += GetVarUIntSize((uint)byteCount) + byteCount;
}
}
-
+
return size;
}
@@ -1075,7 +1115,7 @@ public static partial class AcBinarySerializer
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public bool TrackForScanning(object obj) => _refTracker.TrackForScanning(obj);
-
+
///
/// IId-aware tracking for the scan phase.
/// First checks IId match (different instance, same Id), then falls back to ReferenceEquals.
@@ -1162,12 +1202,12 @@ public static partial class AcBinarySerializer
{
var length = value.Length;
EnsureCapacity(1 + length);
-
+
// Ascii.FromUtf16: SIMD-optimized ASCII conversion
// Returns actual bytes written - if less than input length, there was a non-ASCII char
var destSpan = _buffer.AsSpan(_position + 1, length);
var status = Ascii.FromUtf16(value.AsSpan(), destSpan, out var bytesWritten);
-
+
if (status == System.Buffers.OperationStatus.Done && bytesWritten == length)
{
// Success - write FixStr header
diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs
index c594a3a..87a9f36 100644
--- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs
+++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs
@@ -344,6 +344,10 @@ public static partial class AcBinarySerializer
private static BinarySerializationContext SerializeCore(object value, Type runtimeType, AcBinarySerializerOptions options)
{
+#if DEBUG
+ BinarySerializationContext.GrowBufferCount = 0;
+ BinarySerializationContext.GrowBufferTotalBytes = 0;
+#endif
var context = BinarySerializationContextPool.Get(options);
context.WriteHeaderPlaceholder();
diff --git a/AyCode.Core/Serializers/IIdCollectionMergeHelper.cs b/AyCode.Core/Serializers/IIdCollectionMergeHelper.cs
index d4bd3cc..1af42ae 100644
--- a/AyCode.Core/Serializers/IIdCollectionMergeHelper.cs
+++ b/AyCode.Core/Serializers/IIdCollectionMergeHelper.cs
@@ -1,94 +1,94 @@
-using System.Collections;
-using System.Runtime.CompilerServices;
-using AyCode.Core.Helpers;
-using static AyCode.Core.Helpers.JsonUtilities;
+//using System.Collections;
+//using System.Runtime.CompilerServices;
+//using AyCode.Core.Helpers;
+//using static AyCode.Core.Helpers.JsonUtilities;
-namespace AyCode.Core.Serializers;
+//namespace AyCode.Core.Serializers;
-///
-/// Helper class for merging IId collections during deserialization.
-/// Shared between JSON and Binary deserializers.
-///
-public static class IIdCollectionMergeHelper
-{
- ///
- /// Builds a lookup dictionary from an existing IId collection.
- /// Maps Id values to their corresponding items.
- ///
- /// The existing collection to index.
- /// Function to extract Id from an item.
- /// The type of the Id property.
- /// Dictionary mapping Id to item, or null if collection is empty.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Dictionary