Track buffer growth stats in DEBUG; disable IId merge helper
Add DEBUG-only tracking of buffer growth in AcBinarySerializer for benchmarking, with stats output in console app. Expose stats via static properties and reset at serialization start. Add InternalsVisibleTo for console access. Comment out IIdCollectionMergeHelper.cs. Minor code cleanups included.
This commit is contained in:
parent
f778d4faa9
commit
2eca18ca3f
|
|
@ -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) ===");
|
||||
|
|
|
|||
|
|
@ -25,4 +25,8 @@
|
|||
<Folder Include="Expressions\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="AyCode.Core.Serializers.Console" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Binary serialization context. Public for generated serializers.
|
||||
/// </summary>
|
||||
|
|
@ -60,6 +74,20 @@ public static partial class AcBinarySerializer
|
|||
private int _position;
|
||||
private int _initialBufferSize;
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Counts how many times GrowBuffer was called during serialization.
|
||||
/// Used for benchmarking buffer allocation efficiency.
|
||||
/// </summary>
|
||||
public static int GrowBufferCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total bytes allocated by GrowBuffer during serialization.
|
||||
/// Used for benchmarking buffer allocation efficiency.
|
||||
/// </summary>
|
||||
public static long GrowBufferTotalBytes { get; set; }
|
||||
#endif
|
||||
|
||||
// Use shared reference tracker from AcSerializerCommon
|
||||
//private readonly AcSerializerCommon.SerializationReferenceTracker _refTracker = new();
|
||||
|
||||
|
|
@ -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<byte>.Shared.Return(_buffer);
|
||||
|
|
@ -161,6 +192,10 @@ public static partial class AcBinarySerializer
|
|||
ArrayPool<byte>.Shared.Return(_propertyStateBuffer);
|
||||
_propertyStateBuffer = null;
|
||||
}
|
||||
|
||||
// NOTE: GrowBufferCount <20>s GrowBufferTotalBytes nem null<6C>z<EFBFBD>dik itt,
|
||||
// hogy a m<>r<EFBFBD>sek v<>g<EFBFBD>n ki tudj<64>k <20>rni az <20>rt<72>keket.
|
||||
// Csak a Reset() met<65>dusban null<6C>z<EFBFBD>dnak minden <20>j fut<75>s elej<65>n.
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -409,6 +444,11 @@ public static partial class AcBinarySerializer
|
|||
_buffer.AsSpan(0, _position).CopyTo(newBuffer);
|
||||
ArrayPool<byte>.Shared.Return(_buffer);
|
||||
_buffer = newBuffer;
|
||||
|
||||
#if DEBUG
|
||||
GrowBufferCount++;
|
||||
GrowBufferTotalBytes += newSize;
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for merging IId collections during deserialization.
|
||||
/// Shared between JSON and Binary deserializers.
|
||||
/// </summary>
|
||||
public static class IIdCollectionMergeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a lookup dictionary from an existing IId collection.
|
||||
/// Maps Id values to their corresponding items.
|
||||
/// </summary>
|
||||
/// <param name="existingList">The existing collection to index.</param>
|
||||
/// <param name="idGetter">Function to extract Id from an item.</param>
|
||||
/// <param name="idType">The type of the Id property.</param>
|
||||
/// <returns>Dictionary mapping Id to item, or null if collection is empty.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Dictionary<object, object>? BuildIdLookup(
|
||||
IList existingList,
|
||||
Func<object, object?> idGetter,
|
||||
Type idType)
|
||||
{
|
||||
var count = existingList.Count;
|
||||
if (count == 0) return null;
|
||||
///// <summary>
|
||||
///// Helper class for merging IId collections during deserialization.
|
||||
///// Shared between JSON and Binary deserializers.
|
||||
///// </summary>
|
||||
//public static class IIdCollectionMergeHelper
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// Builds a lookup dictionary from an existing IId collection.
|
||||
// /// Maps Id values to their corresponding items.
|
||||
// /// </summary>
|
||||
// /// <param name="existingList">The existing collection to index.</param>
|
||||
// /// <param name="idGetter">Function to extract Id from an item.</param>
|
||||
// /// <param name="idType">The type of the Id property.</param>
|
||||
// /// <returns>Dictionary mapping Id to item, or null if collection is empty.</returns>
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// public static Dictionary<object, object>? BuildIdLookup(
|
||||
// IList existingList,
|
||||
// Func<object, object?> idGetter,
|
||||
// Type idType)
|
||||
// {
|
||||
// var count = existingList.Count;
|
||||
// if (count == 0) return null;
|
||||
|
||||
var dict = new Dictionary<object, object>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var item = existingList[i];
|
||||
if (item == null) continue;
|
||||
// var dict = new Dictionary<object, object>(count);
|
||||
// for (var i = 0; i < count; i++)
|
||||
// {
|
||||
// var item = existingList[i];
|
||||
// if (item == null) continue;
|
||||
|
||||
var id = idGetter(item);
|
||||
if (id != null && !IsDefaultValue(id, idType))
|
||||
dict[id] = item;
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
// var id = idGetter(item);
|
||||
// if (id != null && !IsDefaultValue(id, idType))
|
||||
// dict[id] = item;
|
||||
// }
|
||||
// return dict;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Removes orphaned items from the collection that are not present in the source IDs.
|
||||
/// </summary>
|
||||
/// <param name="existingList">The collection to clean up.</param>
|
||||
/// <param name="existingById">Lookup dictionary of existing items.</param>
|
||||
/// <param name="sourceIds">Set of IDs that were seen in source data.</param>
|
||||
public static void RemoveOrphanedItems(
|
||||
IList existingList,
|
||||
Dictionary<object, object> existingById,
|
||||
HashSet<object> sourceIds)
|
||||
{
|
||||
var itemsToRemove = new List<object>();
|
||||
foreach (var kvp in existingById)
|
||||
{
|
||||
if (!sourceIds.Contains(kvp.Key))
|
||||
{
|
||||
itemsToRemove.Add(kvp.Value);
|
||||
}
|
||||
}
|
||||
// /// <summary>
|
||||
// /// Removes orphaned items from the collection that are not present in the source IDs.
|
||||
// /// </summary>
|
||||
// /// <param name="existingList">The collection to clean up.</param>
|
||||
// /// <param name="existingById">Lookup dictionary of existing items.</param>
|
||||
// /// <param name="sourceIds">Set of IDs that were seen in source data.</param>
|
||||
// public static void RemoveOrphanedItems(
|
||||
// IList existingList,
|
||||
// Dictionary<object, object> existingById,
|
||||
// HashSet<object> sourceIds)
|
||||
// {
|
||||
// var itemsToRemove = new List<object>();
|
||||
// foreach (var kvp in existingById)
|
||||
// {
|
||||
// if (!sourceIds.Contains(kvp.Key))
|
||||
// {
|
||||
// itemsToRemove.Add(kvp.Value);
|
||||
// }
|
||||
// }
|
||||
|
||||
foreach (var item in itemsToRemove)
|
||||
{
|
||||
existingList.Remove(item);
|
||||
}
|
||||
}
|
||||
// foreach (var item in itemsToRemove)
|
||||
// {
|
||||
// existingList.Remove(item);
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Copies properties from source object to target object using metadata.
|
||||
/// </summary>
|
||||
/// <typeparam name="TPropertyInfo">Type of property info (varies by serializer).</typeparam>
|
||||
/// <param name="source">Source object to copy from.</param>
|
||||
/// <param name="target">Target object to copy to.</param>
|
||||
/// <param name="properties">Array of property accessors.</param>
|
||||
/// <param name="getter">Function to get property value.</param>
|
||||
/// <param name="setter">Action to set property value.</param>
|
||||
public static void CopyProperties<TPropertyInfo>(
|
||||
object source,
|
||||
object target,
|
||||
TPropertyInfo[] properties,
|
||||
Func<TPropertyInfo, object, object?> getter,
|
||||
Action<TPropertyInfo, object, object?> setter)
|
||||
{
|
||||
for (var i = 0; i < properties.Length; i++)
|
||||
{
|
||||
var prop = properties[i];
|
||||
var value = getter(prop, source);
|
||||
if (value != null)
|
||||
setter(prop, target, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// /// <summary>
|
||||
// /// Copies properties from source object to target object using metadata.
|
||||
// /// </summary>
|
||||
// /// <typeparam name="TPropertyInfo">Type of property info (varies by serializer).</typeparam>
|
||||
// /// <param name="source">Source object to copy from.</param>
|
||||
// /// <param name="target">Target object to copy to.</param>
|
||||
// /// <param name="properties">Array of property accessors.</param>
|
||||
// /// <param name="getter">Function to get property value.</param>
|
||||
// /// <param name="setter">Action to set property value.</param>
|
||||
// public static void CopyProperties<TPropertyInfo>(
|
||||
// object source,
|
||||
// object target,
|
||||
// TPropertyInfo[] properties,
|
||||
// Func<TPropertyInfo, object, object?> getter,
|
||||
// Action<TPropertyInfo, object, object?> setter)
|
||||
// {
|
||||
// for (var i = 0; i < properties.Length; i++)
|
||||
// {
|
||||
// var prop = properties[i];
|
||||
// var value = getter(prop, source);
|
||||
// if (value != null)
|
||||
// setter(prop, target, value);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
Loading…
Reference in New Issue