Refactor benchmark infra: generic, multi-variant test data
Refactored the benchmark and test data infrastructure to use generic, type-safe, and multi-variant models. Introduced generic base classes for the test data hierarchy and factories, with closing-generic aliases for _All_True and _All_False families. Benchmarks now select the correct test data variant per serializer options, and all serializers are generic over the order type. Output and result reporting now include the CLR type name for clarity. Centralized string property handling and improved documentation throughout.
This commit is contained in:
parent
32f2de0db3
commit
027ff6bd49
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Console.Benchmarks;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using MemoryPack;
|
||||
|
|
@ -73,7 +73,7 @@ internal static class BenchmarkLoop
|
|||
try
|
||||
{
|
||||
var allResults = new List<BenchmarkResult>();
|
||||
var allTestDataSets = BenchmarkTestDataProvider.CreateTestDataSets();
|
||||
var allTestDataSets = BuildMultiVariantTestDataSets();
|
||||
var testDataSets = FilterByLayer(allTestDataSets, layer);
|
||||
|
||||
System.Console.WriteLine($"Layer: {layer} | OpMode: {opMode} | SerializerMode: {serializerMode} | Charset: {Configuration.GetCurrentCharsetName()} | Iterations: per-cell adaptive (~{Configuration.TargetSampleMs} ms target) | Warmup: {Configuration.WarmupIterations} per phase (Ser/Des isolated) | Samples: {Configuration.BenchmarkSamples} (median) + pilot discard");
|
||||
|
|
@ -196,6 +196,7 @@ internal static class BenchmarkLoop
|
|||
IoMode = serializer.IoMode,
|
||||
DispatchMode = serializer.DispatchMode,
|
||||
OptionsPreset = serializer.OptionsPreset,
|
||||
OrderTypeName = serializer.OrderTypeName,
|
||||
OptionsDescription = serializer.OptionsDescription,
|
||||
SerializedSize = serializer.SerializedSize,
|
||||
SetupSerializeAllocBytes = serializer.SetupSerializeAllocBytes,
|
||||
|
|
@ -293,8 +294,96 @@ internal static class BenchmarkLoop
|
|||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Phase 2 multi-variant test-data builder. Constructs each cell in both the _All_False and
|
||||
/// _All_True families, then cross-registers _All_True on the _All_False primaries so the
|
||||
/// CreateSerializers downstream can pick the matching variant per AcBinary options preset.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Memory cost: ~600 KB across 5 cells (Large dominates at ~340 KB for both variants). The two
|
||||
/// families are built independently — same data values + same numeric sequence (per-family
|
||||
/// _idCounter reset). MemPack/MsgPack benchmarks consume the _All_True variant canonically;
|
||||
/// AcBinary's variant is preset-dependent (see CreateSerializers).
|
||||
/// </remarks>
|
||||
private static List<TestDataSet> BuildMultiVariantTestDataSets()
|
||||
{
|
||||
var allFalse = BenchmarkTestDataProvider_All_False.CreateTestDataSets();
|
||||
var allTrue = BenchmarkTestDataProvider.CreateTestDataSets();
|
||||
|
||||
// Zip by ordinal — both providers emit the same 5 cells in the same order
|
||||
// (Small / Medium / Large / Repeated / Deep), confirmed by their identical
|
||||
// CreateTestDataSets call sequence on the generic base.
|
||||
for (var i = 0; i < allFalse.Count; i++)
|
||||
{
|
||||
var falseDs = (TestDataSet<TestOrder_All_False>)allFalse[i];
|
||||
var trueDs = (TestDataSet<TestOrder_All_True>)allTrue[i];
|
||||
falseDs.RegisterVariant(trueDs.Order);
|
||||
}
|
||||
return allFalse;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Phase 2 variant dispatch rule for AcBinary: a preset uses <c>TestOrder_All_False</c> iff every
|
||||
/// AcBinary "feature flag" is off (no string interning, no reference handling, no metadata, no
|
||||
/// property filter). Any "true"-flagged feature promotes the benchmark to <c>TestOrder_All_True</c>
|
||||
/// — the richer graph + opt-out attribute model exercises the feature's deduplication / dispatch
|
||||
/// path on real shared-reference content. WireMode, SGen mode, and Compression are encoding-axis
|
||||
/// options and intentionally NOT part of this decision (they don't change which graph shape is
|
||||
/// meaningful to feed).
|
||||
/// </summary>
|
||||
private static bool UsesAllFalseVariant(AcBinarySerializerOptions options) =>
|
||||
options.UseStringInterning == StringInterningMode.None &&
|
||||
options.ReferenceHandling == ReferenceHandlingMode.None &&
|
||||
!options.UseMetadata &&
|
||||
options.PropertyFilter == null;
|
||||
|
||||
// Per-class factory helpers — each returns ISerializerBenchmark closed over the variant T
|
||||
// selected by UsesAllFalseVariant(options). Compile-time T at the new T() call site preserves
|
||||
// SGen apples-to-apples (no runtime reflection, no type erasure across the JIT boundary).
|
||||
private static ISerializerBenchmark MakeAcBinary(TestDataSet td, AcBinarySerializerOptions opt, string preset) =>
|
||||
UsesAllFalseVariant(opt)
|
||||
? new AcBinaryBenchmark<TestOrder_All_False>(td.GetOrder<TestOrder_All_False>(), opt, preset)
|
||||
: new AcBinaryBenchmark<TestOrder_All_True>(td.GetOrder<TestOrder_All_True>(), opt, preset);
|
||||
|
||||
private static ISerializerBenchmark MakeAcBinaryBufferWriter(TestDataSet td, AcBinarySerializerOptions opt, string preset) =>
|
||||
UsesAllFalseVariant(opt)
|
||||
? new AcBinaryBufferWriterBenchmark<TestOrder_All_False>(td.GetOrder<TestOrder_All_False>(), opt, preset)
|
||||
: new AcBinaryBufferWriterBenchmark<TestOrder_All_True>(td.GetOrder<TestOrder_All_True>(), opt, preset);
|
||||
|
||||
private static ISerializerBenchmark MakeAcBinaryFreshBufferWriter(TestDataSet td, AcBinarySerializerOptions opt, string preset) =>
|
||||
UsesAllFalseVariant(opt)
|
||||
? new AcBinaryFreshBufferWriterBenchmark<TestOrder_All_False>(td.GetOrder<TestOrder_All_False>(), opt, preset)
|
||||
: new AcBinaryFreshBufferWriterBenchmark<TestOrder_All_True>(td.GetOrder<TestOrder_All_True>(), opt, preset);
|
||||
|
||||
private static ISerializerBenchmark MakeAcBinaryNamedPipe(TestDataSet td, AcBinarySerializerOptions opt, string preset) =>
|
||||
UsesAllFalseVariant(opt)
|
||||
? new AcBinaryNamedPipeBenchmark<TestOrder_All_False>(td.GetOrder<TestOrder_All_False>(), opt, preset)
|
||||
: new AcBinaryNamedPipeBenchmark<TestOrder_All_True>(td.GetOrder<TestOrder_All_True>(), opt, preset);
|
||||
|
||||
private static ISerializerBenchmark MakeAcBinaryNamedPipeRaw(TestDataSet td, AcBinarySerializerOptions opt, string preset) =>
|
||||
UsesAllFalseVariant(opt)
|
||||
? new AcBinaryNamedPipeRawByteArrayBenchmark<TestOrder_All_False>(td.GetOrder<TestOrder_All_False>(), opt, preset)
|
||||
: new AcBinaryNamedPipeRawByteArrayBenchmark<TestOrder_All_True>(td.GetOrder<TestOrder_All_True>(), opt, preset);
|
||||
|
||||
private static ISerializerBenchmark MakeAcBinaryInMemoryPipe(TestDataSet td, AcBinarySerializerOptions opt, string preset) =>
|
||||
UsesAllFalseVariant(opt)
|
||||
? new AcBinaryInMemoryPipeBenchmark<TestOrder_All_False>(td.GetOrder<TestOrder_All_False>(), opt, preset)
|
||||
: new AcBinaryInMemoryPipeBenchmark<TestOrder_All_True>(td.GetOrder<TestOrder_All_True>(), opt, preset);
|
||||
|
||||
private static ISerializerBenchmark MakeAcBinaryInMemoryRaw(TestDataSet td, AcBinarySerializerOptions opt, string preset) =>
|
||||
UsesAllFalseVariant(opt)
|
||||
? new AcBinaryInMemoryRawByteArrayBenchmark<TestOrder_All_False>(td.GetOrder<TestOrder_All_False>(), opt, preset)
|
||||
: new AcBinaryInMemoryRawByteArrayBenchmark<TestOrder_All_True>(td.GetOrder<TestOrder_All_True>(), opt, preset);
|
||||
|
||||
private static List<ISerializerBenchmark> CreateSerializers(TestDataSet testData, SerializerSelectionMode serializerMode)
|
||||
{
|
||||
// Phase 2 variant dispatch (refined): AcBinary picks variant per UsesAllFalseVariant(options).
|
||||
// MemPack / MsgPack canonically use _All_False (no AcBinary opt-in/opt-out axis — both
|
||||
// produce identical MemPack/MsgPack wire on either variant since their contract is family-
|
||||
// agnostic). `orderFalse` is the cell primary; `orderTrue` is fetched on-demand by the AcBinary
|
||||
// factory helpers when an options preset has a "true" flag.
|
||||
var orderFalse = testData.GetOrder<TestOrder_All_False>();
|
||||
|
||||
// FastestByte mode — focused 1:1 comparison on the "fastest Byte[]" path.
|
||||
// TWO benchmarks: AcBinary FastMode Byte[] (Compact UTF-8) + MemoryPack Byte[].
|
||||
// - Compact: smallest wire, UTF-8 encode/decode CPU cost vs MemPack head-to-head.
|
||||
|
|
@ -310,9 +399,11 @@ internal static class BenchmarkLoop
|
|||
|
||||
return new List<ISerializerBenchmark>
|
||||
{
|
||||
new AcBinaryBenchmark(testData.Order, fastestByteOptions, "FastMode"),
|
||||
//new AcBinaryBenchmark(testData.Order, fastWireOptions, "FastMode (FastWire)"),
|
||||
new MemoryPackBenchmark(testData.Order, "Default"),
|
||||
MakeAcBinary(testData, fastestByteOptions, "FastMode"),
|
||||
//MakeAcBinary(testData, fastWireOptions, "FastMode (FastWire)"),
|
||||
// MemPack canonically on _All_True (no AcBinary opt-in/opt-out axis applies; the MemoryPackable
|
||||
// contract serialises identical bytes either way, but _All_True is the established baseline).
|
||||
new MemoryPackBenchmark<TestOrder_All_False>(orderFalse, "Default"),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -337,13 +428,13 @@ internal static class BenchmarkLoop
|
|||
// Chunked-framed AsyncPipe: SerializeChunkedFramed + AsyncPipeReaderInput.DrainFromAsync.
|
||||
// Measures the FULL streaming-I/O stack — wire framing + drain task + sliding-window buffer +
|
||||
// MRES wait-on-byte-shortage — over a kernel NamedPipe.
|
||||
new AcBinaryNamedPipeBenchmark(testData.Order, binaryFastModePipeChunkOnly, "FastMode (PipeChunk)"),
|
||||
MakeAcBinaryNamedPipe(testData, binaryFastModePipeChunkOnly, "FastMode (PipeChunk)"),
|
||||
// Raw byte[] over NamedPipe (sync receive, no chunk-framing). Same kernel-pipe transport,
|
||||
// same inBufferSize, but: serialize → byte[] → Stream.Write → Stream.Read → Deserialize<T>(byte[]).
|
||||
// No drain task, no AsyncPipeReaderInput, no [201][UINT16][data]…[202] framing. Side-by-side with
|
||||
// the chunked-row above this isolates AsyncPipe-framework-overhead (Δ vs raw) from
|
||||
// kernel-transport-overhead (raw vs in-process Byte[]).
|
||||
new AcBinaryNamedPipeRawByteArrayBenchmark(testData.Order, binaryFastModePipeChunkOnly, "FastMode (PipeRaw)"),
|
||||
MakeAcBinaryNamedPipeRaw(testData, binaryFastModePipeChunkOnly, "FastMode (PipeRaw)"),
|
||||
// Chunked-framed AsyncPipe over an IN-MEMORY System.IO.Pipelines.Pipe (NO NamedPipe, NO kernel).
|
||||
// Same chunked-streaming code path (SerializeChunkedFramed → AsyncPipeReaderInput) but with the
|
||||
// kernel-pipe replaced by a managed-only Pipe. Eliminates per-chunk syscall overhead (~30 µs/chunk
|
||||
|
|
@ -352,11 +443,11 @@ internal static class BenchmarkLoop
|
|||
// in-memory Pipe row should be much closer to the raw-byte[] row, validating that NamedPipe loopback
|
||||
// is the worst-case benchmark scenario for chunked-streaming and not representative of real network
|
||||
// / file / cross-thread Pipe scenarios.
|
||||
new AcBinaryInMemoryPipeBenchmark(testData.Order, binaryFastModePipeChunkOnly, "FastMode (PipeChunk)"),
|
||||
MakeAcBinaryInMemoryPipe(testData, binaryFastModePipeChunkOnly, "FastMode (PipeChunk)"),
|
||||
// Raw byte[] over IN-MEMORY direct cross-thread handoff (no transport at all). Apples-to-apples
|
||||
// baseline for the in-memory chunked row above: same in-memory transport (zero kernel), but raw
|
||||
// byte[] vs chunked-streaming wire format. Completes the 2x2 matrix [chunked,raw] × [kernel,memory].
|
||||
new AcBinaryInMemoryRawByteArrayBenchmark(testData.Order, binaryFastModePipeChunkOnly, "FastMode (PipeRaw)"),
|
||||
MakeAcBinaryInMemoryRaw(testData, binaryFastModePipeChunkOnly, "FastMode (PipeRaw)"),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -405,39 +496,42 @@ internal static class BenchmarkLoop
|
|||
// AcBinary — Byte[] API (uncomment to compare option presets side-by-side)
|
||||
// ============================================================
|
||||
// Fastest Byte[] — SGen path (UseGeneratedCode=true, default).
|
||||
new AcBinaryBenchmark(testData.Order, binaryFastModeOption, "FastMode"),
|
||||
MakeAcBinary(testData, binaryFastModeOption, "FastMode"),
|
||||
// Fastest Byte[] — Runtime path (UseGeneratedCode=false). Same wire/options, no source-generated dispatch.
|
||||
// Always paired with the SGen variant so every layer can compare the SGen speed-up apples-to-apples.
|
||||
// NativeAOT-safe: AcSerializerCommon.Create*Getter/Setter falls back to reflection-based delegates
|
||||
// when RuntimeFeature.IsDynamicCodeSupported is false (slower but works under AOT publish).
|
||||
new AcBinaryBenchmark(testData.Order, binaryFastModeNoSgenOption, "FastMode"),
|
||||
MakeAcBinary(testData, binaryFastModeNoSgenOption, "FastMode"),
|
||||
// Default preset Byte[] — RefHandling=OnlyId (deduplicates IId-shared references on the wire) +
|
||||
// UseStringInterning=All (deduplicates repeated strings). Showcases the Default preset's wire-size
|
||||
// and CPU trade-off vs FastMode on the ~20% IId-ref / repeated-string test data.
|
||||
|
||||
new AcBinaryBenchmark(testData.Order, defaultOptions, "Default"),
|
||||
//new AcBinaryBenchmark(testData.Order, binaryDefaultNoSgenOption, "Default"),
|
||||
//new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.WithoutReferenceHandling, "NoRef"),
|
||||
//new AcBinaryBenchmark(testData.Order, binaryNoInternOption, "NoIntern"),
|
||||
// Default preset (ReferenceHandling=OnlyId + StringInterning) → _All_True graph.
|
||||
// Phase 2 variant-dispatch rule: any options preset with a "true"-flagged feature uses
|
||||
// the _All_True family (rich graph, opt-out AcBinarySerializable attribute matches).
|
||||
MakeAcBinary(testData, defaultOptions, "Default"),
|
||||
//MakeAcBinary(testData, binaryDefaultNoSgenOption, "Default"),
|
||||
//MakeAcBinary(testData, AcBinarySerializerOptions.WithoutReferenceHandling, "NoRef"),
|
||||
//MakeAcBinary(testData, binaryNoInternOption, "NoIntern"),
|
||||
|
||||
// AcBinary via IBufferWriter (reused ArrayBufferWriter — long-running service / batch scenario)
|
||||
new AcBinaryBufferWriterBenchmark(testData.Order, binaryFastModeOption, "FastMode"),
|
||||
MakeAcBinaryBufferWriter(testData, binaryFastModeOption, "FastMode"),
|
||||
|
||||
// AcBinary via IBufferWriter (FRESH ArrayBufferWriter per call — one-shot scenario).
|
||||
// 4 KB chunk size from binaryFastModeBufWrChunk — minimises the per-call ArrayBufferWriter
|
||||
// allocation. Optimum for this scenario.
|
||||
new AcBinaryFreshBufferWriterBenchmark(testData.Order, binaryFastModeBufWrChunk, "FastMode (4KB)"),
|
||||
MakeAcBinaryFreshBufferWriter(testData, binaryFastModeBufWrChunk, "FastMode (4KB)"),
|
||||
|
||||
// AcBinary chunked-streaming over an IN-MEMORY Pipe (no kernel transport). Side-by-side with the
|
||||
// Byte[] / IBufferWriter rows above this shows the chunked-streaming framework's pure CPU cost
|
||||
// (no NamedPipe loopback noise) vs the simpler in-process serialize-then-deserialize patterns.
|
||||
// The IO column shows "Pipe(in-mem)" — distinct from the NamedPipe AsyncPipe rows in [P] mode.
|
||||
new AcBinaryInMemoryPipeBenchmark(testData.Order, binaryFastModePipeChunkInMem, "FastMode (PipeChunk)"),
|
||||
MakeAcBinaryInMemoryPipe(testData, binaryFastModePipeChunkInMem, "FastMode (PipeChunk)"),
|
||||
|
||||
// Raw byte[] over IN-MEMORY direct cross-thread handoff (no transport, no kernel, no Pipe). Apples-to-
|
||||
// apples baseline for the in-memory chunked row above: same in-memory pattern, but raw byte[] vs
|
||||
// chunked-streaming wire format. The IO column shows "Bytes(in-mem)".
|
||||
new AcBinaryInMemoryRawByteArrayBenchmark(testData.Order, binaryFastModePipeChunkInMem, "FastMode (PipeRaw)"),
|
||||
MakeAcBinaryInMemoryRaw(testData, binaryFastModePipeChunkInMem, "FastMode (PipeRaw)"),
|
||||
|
||||
// AsyncPipe streaming over kernel NamedPipe (AcBinaryNamedPipeBenchmark) is intentionally OMITTED
|
||||
// here — run it via the dedicated AsyncPipe menu [P] / CLI mode for isolated kernel-transport
|
||||
|
|
@ -446,9 +540,10 @@ internal static class BenchmarkLoop
|
|||
// ============================================================
|
||||
// MemoryPack — three I/O modes for apples-to-apples comparison
|
||||
// ============================================================
|
||||
new MemoryPackBenchmark(testData.Order, "Default"),
|
||||
new MemoryPackBufferWriterBenchmark(testData.Order, "Default"),
|
||||
new MemoryPackFreshBufferWriterBenchmark(testData.Order, "Default"),
|
||||
// MemPack canonically on _All_True (see FastestByte-mode comment above for rationale).
|
||||
new MemoryPackBenchmark<TestOrder_All_False>(orderFalse, "Default"),
|
||||
new MemoryPackBufferWriterBenchmark<TestOrder_All_False>(orderFalse, "Default"),
|
||||
new MemoryPackFreshBufferWriterBenchmark<TestOrder_All_False>(orderFalse, "Default"),
|
||||
|
||||
// ============================================================
|
||||
// MessagePack — for legacy comparison
|
||||
|
|
@ -457,11 +552,11 @@ internal static class BenchmarkLoop
|
|||
// MessagePack v3's DynamicGenericResolver uses Activator.CreateInstance on trimmed
|
||||
// ListFormatter<T> et al. — fails under NativeAOT publish with "No parameterless constructor".
|
||||
// Excluded from the AOT build; available for regular JIT runs only.
|
||||
new MessagePackBenchmark(testData.Order, "ContractBased"),
|
||||
new MessagePackBenchmark<TestOrder_All_False>(orderFalse, "ContractBased"),
|
||||
#endif
|
||||
|
||||
// System.Text.Json (commented — JSON serializer for reference; not in active suite)
|
||||
//new SystemTextJsonBenchmark(testData.Order, "Default")
|
||||
//new SystemTextJsonBenchmark<TestOrder_All_False>(orderFalse, "Default")
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,15 @@ internal sealed class BenchmarkResult
|
|||
public BenchmarkDispatchMode DispatchMode { get; set; }
|
||||
public string OptionsPreset { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// CLR type name of the order graph serialised in this row (e.g. <c>"TestOrder_All_False"</c>,
|
||||
/// <c>"TestOrder_All_True"</c>). Captured from <see cref="ISerializerBenchmark.OrderTypeName"/> in
|
||||
/// <c>RunBenchmarksForTestData</c>; surfaced in the SERIALIZER OPTIONS section of every output
|
||||
/// (.log, .LLM, console) so the reader can correlate each preset with its TestOrder variant
|
||||
/// without inflating the per-row tables with an extra column.
|
||||
/// </summary>
|
||||
public string OrderTypeName { get; set; } = "";
|
||||
|
||||
/// <summary>True if Serialize() captures a full round-trip and Deserialize() is a no-op
|
||||
/// (single-use streaming transports like NamedPipe). Excluded from "Fastest Serialize" / "Fastest Deserialize"
|
||||
/// winners rankings; still ranked in "Fastest Round-trip". Display-side: Ser µs/op / SerAlloc / Des µs/op / DesAlloc
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
|
@ -8,22 +8,23 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// AcBinary benchmark, Byte[] I/O mode. The headline AcBinary row in every cell — compared
|
||||
/// against <see cref="MemoryPackBenchmark"/> as the SOTA baseline.
|
||||
/// </summary>
|
||||
internal sealed class AcBinaryBenchmark : ISerializerBenchmark
|
||||
internal sealed class AcBinaryBenchmark<T> : ISerializerBenchmark where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly AcBinarySerializerOptions _options;
|
||||
private readonly byte[] _serialized;
|
||||
|
||||
public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.ByteArray;
|
||||
public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes => 0;
|
||||
public long SetupDeserializeAllocBytes => 0;
|
||||
public string OptionsDescription => BenchmarkOptions.BuildAcBinary(_options);
|
||||
|
||||
public AcBinaryBenchmark(TestOrder_All_True order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
public AcBinaryBenchmark(T order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
_options = options;
|
||||
|
|
@ -35,12 +36,12 @@ internal sealed class AcBinaryBenchmark : ISerializerBenchmark
|
|||
public void Serialize() => AcBinarySerializer.Serialize(_order, _options);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Deserialize() => AcBinaryDeserializer.Deserialize<TestOrder_All_True>(_serialized, _options);
|
||||
public void Deserialize() => AcBinaryDeserializer.Deserialize<T>(_serialized, _options);
|
||||
|
||||
public bool VerifyRoundTrip()
|
||||
{
|
||||
var bytes = AcBinarySerializer.Serialize(_order, _options);
|
||||
var roundTripped = AcBinaryDeserializer.Deserialize<TestOrder_All_True>(bytes, _options);
|
||||
var roundTripped = AcBinaryDeserializer.Deserialize<T>(bytes, _options);
|
||||
return BenchmarkLoop.DeepEqualsViaJson(_order, roundTripped);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
|
@ -9,9 +9,9 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// Benchmarks AcBinary via the IBufferWriter overload with a pre-allocated, reused ArrayBufferWriter.
|
||||
/// Realistic IBufferWriter usage pattern: caller owns + reuses the writer (zero alloc per call after warmup).
|
||||
/// </summary>
|
||||
internal sealed class AcBinaryBufferWriterBenchmark : ISerializerBenchmark
|
||||
internal sealed class AcBinaryBufferWriterBenchmark<T> : ISerializerBenchmark where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly AcBinarySerializerOptions _options;
|
||||
private readonly byte[] _serialized;
|
||||
private readonly ArrayBufferWriter<byte> _bufferWriter;
|
||||
|
|
@ -19,13 +19,14 @@ internal sealed class AcBinaryBufferWriterBenchmark : ISerializerBenchmark
|
|||
public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrReuse;
|
||||
public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes { get; }
|
||||
public long SetupDeserializeAllocBytes => 0;
|
||||
public string OptionsDescription => BenchmarkOptions.BuildAcBinary(_options);
|
||||
|
||||
public AcBinaryBufferWriterBenchmark(TestOrder_All_True order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
public AcBinaryBufferWriterBenchmark(T order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
_options = options;
|
||||
|
|
@ -55,14 +56,14 @@ internal sealed class AcBinaryBufferWriterBenchmark : ISerializerBenchmark
|
|||
// (the production-realistic surface for SignalR / Pipe consumers) rather than secretly testing
|
||||
// byte[] Deser under the BufWr label.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Deserialize() => AcBinaryDeserializer.Deserialize<TestOrder_All_True>(new ReadOnlySequence<byte>(_serialized), _options);
|
||||
public void Deserialize() => AcBinaryDeserializer.Deserialize<T>(new ReadOnlySequence<byte>(_serialized), _options);
|
||||
|
||||
public bool VerifyRoundTrip()
|
||||
{
|
||||
_bufferWriter.ResetWrittenCount();
|
||||
AcBinarySerializer.Serialize(_order, _bufferWriter, _options);
|
||||
|
||||
var roundTripped = AcBinaryDeserializer.Deserialize<TestOrder_All_True>(new ReadOnlySequence<byte>(_bufferWriter.WrittenMemory), _options);
|
||||
var roundTripped = AcBinaryDeserializer.Deserialize<T>(new ReadOnlySequence<byte>(_bufferWriter.WrittenMemory), _options);
|
||||
return BenchmarkLoop.DeepEqualsViaJson(_order, roundTripped);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
|
@ -12,22 +12,23 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// otherwise AcBinary would request 64KB upfront via GetSpan(), forcing the fresh ABW to allocate 64KB
|
||||
/// regardless of payload size (heavy over-allocation for small payloads).
|
||||
/// </summary>
|
||||
internal sealed class AcBinaryFreshBufferWriterBenchmark : ISerializerBenchmark
|
||||
internal sealed class AcBinaryFreshBufferWriterBenchmark<T> : ISerializerBenchmark where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly AcBinarySerializerOptions _options;
|
||||
private readonly byte[] _serialized;
|
||||
|
||||
public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrNew;
|
||||
public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes => 0;
|
||||
public long SetupDeserializeAllocBytes => 0;
|
||||
public string OptionsDescription => BenchmarkOptions.BuildAcBinary(_options, $", BufferSize={_options.BufferWriterChunkSize}B");
|
||||
|
||||
public AcBinaryFreshBufferWriterBenchmark(TestOrder_All_True order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
public AcBinaryFreshBufferWriterBenchmark(T order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
// BufferWriterChunkSize comes from the caller (central source of truth in CreateSerializers
|
||||
|
|
@ -51,13 +52,13 @@ internal sealed class AcBinaryFreshBufferWriterBenchmark : ISerializerBenchmark
|
|||
// (the production-realistic surface for SignalR / Pipe consumers) rather than secretly testing
|
||||
// byte[] Deser under the BufWr label.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Deserialize() => AcBinaryDeserializer.Deserialize<TestOrder_All_True>(new ReadOnlySequence<byte>(_serialized), _options);
|
||||
public void Deserialize() => AcBinaryDeserializer.Deserialize<T>(new ReadOnlySequence<byte>(_serialized), _options);
|
||||
|
||||
public bool VerifyRoundTrip()
|
||||
{
|
||||
var abw = new ArrayBufferWriter<byte>();
|
||||
AcBinarySerializer.Serialize(_order, abw, _options);
|
||||
var roundTripped = AcBinaryDeserializer.Deserialize<TestOrder_All_True>(new ReadOnlySequence<byte>(abw.WrittenMemory), _options);
|
||||
var roundTripped = AcBinaryDeserializer.Deserialize<T>(new ReadOnlySequence<byte>(abw.WrittenMemory), _options);
|
||||
return BenchmarkLoop.DeepEqualsViaJson(_order, roundTripped);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Tests.Serialization; // DrainFromAsync extension (test-only, used by benchmark)
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using System.IO.Pipelines;
|
||||
|
|
@ -24,9 +24,9 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// custom message brokers). The numbers from this row reflect that scenario, NOT the kernel-pipe loopback
|
||||
/// of the NamedPipe benchmark.</para>
|
||||
/// </summary>
|
||||
internal sealed class AcBinaryInMemoryPipeBenchmark : ISerializerBenchmark, IDisposable
|
||||
internal sealed class AcBinaryInMemoryPipeBenchmark<T> : ISerializerBenchmark, IDisposable where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly AcBinarySerializerOptions _options;
|
||||
private readonly byte[] _serialized; // for SerializedSize reporting only
|
||||
|
||||
|
|
@ -50,6 +50,7 @@ internal sealed class AcBinaryInMemoryPipeBenchmark : ISerializerBenchmark, IDis
|
|||
public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.InMemoryPipe;
|
||||
public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes { get; }
|
||||
|
|
@ -57,7 +58,7 @@ internal sealed class AcBinaryInMemoryPipeBenchmark : ISerializerBenchmark, IDis
|
|||
public bool IsRoundTripOnly => true;
|
||||
public string OptionsDescription => BenchmarkOptions.BuildAcBinary(_options, $", BufferSize={_options.BufferWriterChunkSize}B, Transport=Pipe(in-memory,multiMessage,2-task)");
|
||||
|
||||
public AcBinaryInMemoryPipeBenchmark(TestOrder_All_True order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
public AcBinaryInMemoryPipeBenchmark(T order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
_options = options;
|
||||
|
|
@ -106,7 +107,7 @@ internal sealed class AcBinaryInMemoryPipeBenchmark : ISerializerBenchmark, IDis
|
|||
|
||||
try
|
||||
{
|
||||
var result = AcBinaryDeserializer.Deserialize<TestOrder_All_True>(_input, _options);
|
||||
var result = AcBinaryDeserializer.Deserialize<T>(_input, _options);
|
||||
if (_captureResult) _lastResult = result;
|
||||
}
|
||||
catch
|
||||
|
|
@ -158,7 +159,7 @@ internal sealed class AcBinaryInMemoryPipeBenchmark : ISerializerBenchmark, IDis
|
|||
try
|
||||
{
|
||||
Serialize();
|
||||
var result = _lastResult as TestOrder_All_True;
|
||||
var result = _lastResult as T;
|
||||
return result != null && BenchmarkLoop.DeepEqualsViaJson(_order, result);
|
||||
}
|
||||
finally
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
|
@ -21,9 +21,9 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// Side-by-side with <see cref="AcBinaryNamedPipeRawByteArrayBenchmark"/> this isolates the kernel-NamedPipe
|
||||
/// overhead on the raw-byte[] side.</para>
|
||||
/// </summary>
|
||||
internal sealed class AcBinaryInMemoryRawByteArrayBenchmark : ISerializerBenchmark, IDisposable
|
||||
internal sealed class AcBinaryInMemoryRawByteArrayBenchmark<T> : ISerializerBenchmark, IDisposable where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly AcBinarySerializerOptions _options;
|
||||
private readonly byte[] _serialized; // for SerializedSize reporting only
|
||||
|
||||
|
|
@ -41,6 +41,7 @@ internal sealed class AcBinaryInMemoryRawByteArrayBenchmark : ISerializerBenchma
|
|||
public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.InMemoryRaw;
|
||||
public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes { get; }
|
||||
|
|
@ -48,7 +49,7 @@ internal sealed class AcBinaryInMemoryRawByteArrayBenchmark : ISerializerBenchma
|
|||
public bool IsRoundTripOnly => true;
|
||||
public string OptionsDescription => BenchmarkOptions.BuildAcBinary(_options, $", BufferSize={_options.BufferWriterChunkSize}B, Transport=in-memory(raw,2-task)");
|
||||
|
||||
public AcBinaryInMemoryRawByteArrayBenchmark(TestOrder_All_True order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
public AcBinaryInMemoryRawByteArrayBenchmark(T order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
_options = options;
|
||||
|
|
@ -89,7 +90,7 @@ internal sealed class AcBinaryInMemoryRawByteArrayBenchmark : ISerializerBenchma
|
|||
var bytes = _pendingBytes;
|
||||
if (bytes != null)
|
||||
{
|
||||
var result = AcBinaryDeserializer.Deserialize<TestOrder_All_True>(bytes, _options);
|
||||
var result = AcBinaryDeserializer.Deserialize<T>(bytes, _options);
|
||||
if (_captureResult) _lastResult = result;
|
||||
}
|
||||
}
|
||||
|
|
@ -142,7 +143,7 @@ internal sealed class AcBinaryInMemoryRawByteArrayBenchmark : ISerializerBenchma
|
|||
try
|
||||
{
|
||||
Serialize();
|
||||
var result = _lastResult as TestOrder_All_True;
|
||||
var result = _lastResult as T;
|
||||
return result != null && BenchmarkLoop.DeepEqualsViaJson(_order, result);
|
||||
}
|
||||
finally
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Tests.Serialization; // DrainFromAsync extension (test-only, used by benchmark)
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using System.IO.Pipelines;
|
||||
|
|
@ -39,9 +39,9 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// <para><b>Approximation note</b>: single-process loopback NamedPipe. Real cross-process / cross-machine SignalR
|
||||
/// adds further transport latency (TCP, WebSocket framing) on top. The benchmark gives a lower bound.</para>
|
||||
/// </summary>
|
||||
internal sealed class AcBinaryNamedPipeBenchmark : ISerializerBenchmark, IDisposable
|
||||
internal sealed class AcBinaryNamedPipeBenchmark<T> : ISerializerBenchmark, IDisposable where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly AcBinarySerializerOptions _options;
|
||||
private readonly byte[] _serialized; // for SerializedSize reporting only
|
||||
|
||||
|
|
@ -65,6 +65,7 @@ internal sealed class AcBinaryNamedPipeBenchmark : ISerializerBenchmark, IDispos
|
|||
public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.NamedPipe;
|
||||
public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes { get; }
|
||||
|
|
@ -72,7 +73,7 @@ internal sealed class AcBinaryNamedPipeBenchmark : ISerializerBenchmark, IDispos
|
|||
public bool IsRoundTripOnly => true;
|
||||
public string OptionsDescription => BenchmarkOptions.BuildAcBinary(_options, $", BufferSize={_options.BufferWriterChunkSize}B, Transport=NamedPipe(long-lived,multiMessage,2-task)");
|
||||
|
||||
public AcBinaryNamedPipeBenchmark(TestOrder_All_True order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
public AcBinaryNamedPipeBenchmark(T order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
// BufferWriterChunkSize comes from the caller (central source of truth in CreateSerializers
|
||||
|
|
@ -152,7 +153,7 @@ internal sealed class AcBinaryNamedPipeBenchmark : ISerializerBenchmark, IDispos
|
|||
|
||||
try
|
||||
{
|
||||
var result = AcBinaryDeserializer.Deserialize<TestOrder_All_True>(_input, _options);
|
||||
var result = AcBinaryDeserializer.Deserialize<T>(_input, _options);
|
||||
if (_captureResult) _lastResult = result;
|
||||
}
|
||||
catch
|
||||
|
|
@ -203,7 +204,7 @@ internal sealed class AcBinaryNamedPipeBenchmark : ISerializerBenchmark, IDispos
|
|||
try
|
||||
{
|
||||
Serialize();
|
||||
var result = _lastResult as TestOrder_All_True;
|
||||
var result = _lastResult as T;
|
||||
return result != null && BenchmarkLoop.DeepEqualsViaJson(_order, result);
|
||||
}
|
||||
finally
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using System.IO.Pipes;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
|
@ -25,9 +25,9 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// <see cref="AcBinaryBenchmark"/>'s API contract); the receive-side scratch buffer is also allocated per-iter
|
||||
/// on the consumer-task (counted via <c>GC.GetTotalAllocatedBytes</c> in <c>BenchmarkLoop.MeasureAllocationTotal</c>).
|
||||
/// </summary>
|
||||
internal sealed class AcBinaryNamedPipeRawByteArrayBenchmark : ISerializerBenchmark, IDisposable
|
||||
internal sealed class AcBinaryNamedPipeRawByteArrayBenchmark<T> : ISerializerBenchmark, IDisposable where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly AcBinarySerializerOptions _options;
|
||||
private readonly byte[] _serialized; // for SerializedSize reporting + receive-side size known upfront
|
||||
|
||||
|
|
@ -52,6 +52,7 @@ internal sealed class AcBinaryNamedPipeRawByteArrayBenchmark : ISerializerBenchm
|
|||
public BenchmarkEngine Engine => BenchmarkEngine.AcBinary;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.NamedPipeRaw;
|
||||
public BenchmarkDispatchMode DispatchMode => _options.UseGeneratedCode ? BenchmarkDispatchMode.SGen : BenchmarkDispatchMode.Runtime;
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes { get; }
|
||||
|
|
@ -59,7 +60,7 @@ internal sealed class AcBinaryNamedPipeRawByteArrayBenchmark : ISerializerBenchm
|
|||
public bool IsRoundTripOnly => true;
|
||||
public string OptionsDescription => BenchmarkOptions.BuildAcBinary(_options, $", BufferSize={_options.BufferWriterChunkSize}B, Transport=NamedPipe(raw,2-task)");
|
||||
|
||||
public AcBinaryNamedPipeRawByteArrayBenchmark(TestOrder_All_True order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
public AcBinaryNamedPipeRawByteArrayBenchmark(T order, AcBinarySerializerOptions options, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
// BufferWriterChunkSize comes from the caller — same source-of-truth contract as
|
||||
|
|
@ -125,7 +126,7 @@ internal sealed class AcBinaryNamedPipeRawByteArrayBenchmark : ISerializerBenchm
|
|||
if (n == 0) break; // pipe closed / EOF — partial read swallowed
|
||||
totalRead += n;
|
||||
}
|
||||
var result = AcBinaryDeserializer.Deserialize<TestOrder_All_True>(bytes, _options);
|
||||
var result = AcBinaryDeserializer.Deserialize<T>(bytes, _options);
|
||||
if (_captureResult) _lastResult = result;
|
||||
}
|
||||
catch
|
||||
|
|
@ -183,7 +184,7 @@ internal sealed class AcBinaryNamedPipeRawByteArrayBenchmark : ISerializerBenchm
|
|||
try
|
||||
{
|
||||
Serialize();
|
||||
var result = _lastResult as TestOrder_All_True;
|
||||
var result = _lastResult as T;
|
||||
return result != null && BenchmarkLoop.DeepEqualsViaJson(_order, result);
|
||||
}
|
||||
finally
|
||||
|
|
|
|||
|
|
@ -21,6 +21,20 @@ internal interface ISerializerBenchmark
|
|||
BenchmarkDispatchMode DispatchMode { get; }
|
||||
/// <summary>Options preset name — e.g. "FastMode", "Default", "NoIntern", "WithCompression". Stays string because preset names are open-ended (per-instance constructor argument).</summary>
|
||||
string OptionsPreset { get; }
|
||||
/// <summary>
|
||||
/// CLR type of the order graph this benchmark serializes (e.g. <c>typeof(TestOrder_All_False)</c>,
|
||||
/// <c>typeof(TestOrder_All_True)</c>). Per-instance: AcBinary picks variant by options preset
|
||||
/// (<see cref="BenchmarkLoop.UsesAllFalseVariant"/>), MemPack / MsgPack always use <c>_All_False</c>.
|
||||
/// Concrete benchmarks return <c>typeof(T)</c> for their generic parameter.
|
||||
/// </summary>
|
||||
Type OrderType { get; }
|
||||
/// <summary>
|
||||
/// Derived display name for the <see cref="OrderType"/>. Default-interface impl reads
|
||||
/// <c>OrderType.Name</c>; concrete classes don't need to override. Surfaced in the SERIALIZER
|
||||
/// OPTIONS section of every output (.log, .LLM, console) — not in the per-row tables — so the
|
||||
/// reader correlates each preset with its TestOrder variant without inflating the result columns.
|
||||
/// </summary>
|
||||
string OrderTypeName => OrderType.Name;
|
||||
/// <summary>Synthesized display name from Engine + IoMode + OptionsPreset.</summary>
|
||||
string Name => $"{Engine.ToDisplay()} ({IoMode.ToDisplay()}, {OptionsPreset})";
|
||||
int SerializedSize { get; }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Tests.TestModels;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using MemoryPack;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
|
@ -9,22 +9,23 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// cell. WireMode-aligned options via <see cref="BenchmarkOptions.GetMemPack"/> so Compact ↔ UTF-8
|
||||
/// and FastWire ↔ UTF-16 are apples-to-apples on the string-encoding axis.
|
||||
/// </summary>
|
||||
internal sealed class MemoryPackBenchmark : ISerializerBenchmark
|
||||
internal sealed class MemoryPackBenchmark<T> : ISerializerBenchmark where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly MemoryPackSerializerOptions _options;
|
||||
private readonly byte[] _serialized;
|
||||
|
||||
public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.ByteArray;
|
||||
public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes => 0;
|
||||
public long SetupDeserializeAllocBytes => 0;
|
||||
public string? OptionsDescription => $"StringEncoding={_options.StringEncoding}";
|
||||
|
||||
public MemoryPackBenchmark(TestOrder_All_True order, string optionsPreset)
|
||||
public MemoryPackBenchmark(T order, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
OptionsPreset = optionsPreset;
|
||||
|
|
@ -36,12 +37,12 @@ internal sealed class MemoryPackBenchmark : ISerializerBenchmark
|
|||
public void Serialize() => MemoryPackSerializer.Serialize(_order, _options);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Deserialize() => MemoryPackSerializer.Deserialize<TestOrder_All_True>(_serialized, _options);
|
||||
public void Deserialize() => MemoryPackSerializer.Deserialize<T>(_serialized, _options);
|
||||
|
||||
public bool VerifyRoundTrip()
|
||||
{
|
||||
var bytes = MemoryPackSerializer.Serialize(_order, _options);
|
||||
var roundTripped = MemoryPackSerializer.Deserialize<TestOrder_All_True>(bytes, _options);
|
||||
var roundTripped = MemoryPackSerializer.Deserialize<T>(bytes, _options);
|
||||
return BenchmarkLoop.DeepEqualsViaJson(_order, roundTripped);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Tests.TestModels;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using MemoryPack;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
|
@ -10,9 +10,9 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// Apples-to-apples counterpart to <see cref="AcBinaryBufferWriterBenchmark"/> — MemoryPack's IBufferWriter
|
||||
/// is the path it's designed for.
|
||||
/// </summary>
|
||||
internal sealed class MemoryPackBufferWriterBenchmark : ISerializerBenchmark
|
||||
internal sealed class MemoryPackBufferWriterBenchmark<T> : ISerializerBenchmark where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly MemoryPackSerializerOptions _options;
|
||||
private readonly byte[] _serialized;
|
||||
private readonly ArrayBufferWriter<byte> _bufferWriter;
|
||||
|
|
@ -20,13 +20,14 @@ internal sealed class MemoryPackBufferWriterBenchmark : ISerializerBenchmark
|
|||
public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrReuse;
|
||||
public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes { get; }
|
||||
public long SetupDeserializeAllocBytes => 0;
|
||||
public string? OptionsDescription => $"StringEncoding={_options.StringEncoding}";
|
||||
|
||||
public MemoryPackBufferWriterBenchmark(TestOrder_All_True order, string optionsPreset)
|
||||
public MemoryPackBufferWriterBenchmark(T order, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
OptionsPreset = optionsPreset;
|
||||
|
|
@ -51,13 +52,13 @@ internal sealed class MemoryPackBufferWriterBenchmark : ISerializerBenchmark
|
|||
// BufWr semantic: read from a ReadOnlySequence<byte> overload (apples-to-apples with AcBinary's
|
||||
// BufWr Deser path). MemoryPack's ROS overload also single-segment-fast-paths internally.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Deserialize() => MemoryPackSerializer.Deserialize<TestOrder_All_True>(new ReadOnlySequence<byte>(_serialized), _options);
|
||||
public void Deserialize() => MemoryPackSerializer.Deserialize<T>(new ReadOnlySequence<byte>(_serialized), _options);
|
||||
|
||||
public bool VerifyRoundTrip()
|
||||
{
|
||||
_bufferWriter.ResetWrittenCount();
|
||||
MemoryPackSerializer.Serialize(_bufferWriter, _order, _options);
|
||||
var roundTripped = MemoryPackSerializer.Deserialize<TestOrder_All_True>(new ReadOnlySequence<byte>(_bufferWriter.WrittenMemory), _options);
|
||||
var roundTripped = MemoryPackSerializer.Deserialize<T>(new ReadOnlySequence<byte>(_bufferWriter.WrittenMemory), _options);
|
||||
return BenchmarkLoop.DeepEqualsViaJson(_order, roundTripped);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Tests.TestModels;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using MemoryPack;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
|
@ -9,22 +9,23 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// Benchmarks MemoryPack via the IBufferWriter overload, allocating a FRESH ArrayBufferWriter on EVERY call.
|
||||
/// Apples-to-apples counterpart to <see cref="AcBinaryFreshBufferWriterBenchmark"/>.
|
||||
/// </summary>
|
||||
internal sealed class MemoryPackFreshBufferWriterBenchmark : ISerializerBenchmark
|
||||
internal sealed class MemoryPackFreshBufferWriterBenchmark<T> : ISerializerBenchmark where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly MemoryPackSerializerOptions _options;
|
||||
private readonly byte[] _serialized;
|
||||
|
||||
public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrNew;
|
||||
public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes => 0;
|
||||
public long SetupDeserializeAllocBytes => 0;
|
||||
public string? OptionsDescription => $"StringEncoding={_options.StringEncoding}";
|
||||
|
||||
public MemoryPackFreshBufferWriterBenchmark(TestOrder_All_True order, string optionsPreset)
|
||||
public MemoryPackFreshBufferWriterBenchmark(T order, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
OptionsPreset = optionsPreset;
|
||||
|
|
@ -42,13 +43,13 @@ internal sealed class MemoryPackFreshBufferWriterBenchmark : ISerializerBenchmar
|
|||
// BufWr semantic: read from a ReadOnlySequence<byte> overload (apples-to-apples with AcBinary's
|
||||
// BufWr Deser path). MemoryPack's ROS overload also single-segment-fast-paths internally.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Deserialize() => MemoryPackSerializer.Deserialize<TestOrder_All_True>(new ReadOnlySequence<byte>(_serialized), _options);
|
||||
public void Deserialize() => MemoryPackSerializer.Deserialize<T>(new ReadOnlySequence<byte>(_serialized), _options);
|
||||
|
||||
public bool VerifyRoundTrip()
|
||||
{
|
||||
var abw = new ArrayBufferWriter<byte>();
|
||||
MemoryPackSerializer.Serialize(abw, _order, _options);
|
||||
var roundTripped = MemoryPackSerializer.Deserialize<TestOrder_All_True>(new ReadOnlySequence<byte>(abw.WrittenMemory), _options);
|
||||
var roundTripped = MemoryPackSerializer.Deserialize<T>(new ReadOnlySequence<byte>(abw.WrittenMemory), _options);
|
||||
return BenchmarkLoop.DeepEqualsViaJson(_order, roundTripped);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#if !AYCODE_NATIVEAOT
|
||||
#if !AYCODE_NATIVEAOT
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using MessagePack;
|
||||
using MessagePack.Resolvers;
|
||||
|
|
@ -12,22 +12,23 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// which uses Activator.CreateInstance on formatter types the AOT trimmer drops →
|
||||
/// MissingMethodException at runtime. Available for regular JIT runs (<c>dotnet run</c>) only.
|
||||
/// </summary>
|
||||
internal sealed class MessagePackBenchmark : ISerializerBenchmark
|
||||
internal sealed class MessagePackBenchmark<T> : ISerializerBenchmark where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly MessagePackSerializerOptions _options;
|
||||
private readonly byte[] _serialized;
|
||||
|
||||
public BenchmarkEngine Engine => BenchmarkEngine.MessagePack;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.ByteArray;
|
||||
public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MessagePack uses [MessagePackObject] source-generated formatters (StandardResolver)
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serialized.Length;
|
||||
public long SetupSerializeAllocBytes => 0;
|
||||
public long SetupDeserializeAllocBytes => 0;
|
||||
public string OptionsDescription { get; }
|
||||
|
||||
public MessagePackBenchmark(TestOrder_All_True order, string optionsPreset)
|
||||
public MessagePackBenchmark(T order, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
OptionsPreset = optionsPreset;
|
||||
|
|
@ -46,12 +47,12 @@ internal sealed class MessagePackBenchmark : ISerializerBenchmark
|
|||
public void Serialize() => MessagePackSerializer.Serialize(_order, _options);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Deserialize() => MessagePackSerializer.Deserialize<TestOrder_All_True>(_serialized, _options);
|
||||
public void Deserialize() => MessagePackSerializer.Deserialize<T>(_serialized, _options);
|
||||
|
||||
public bool VerifyRoundTrip()
|
||||
{
|
||||
var bytes = MessagePackSerializer.Serialize(_order, _options);
|
||||
var roundTripped = MessagePackSerializer.Deserialize<TestOrder_All_True>(bytes, _options);
|
||||
var roundTripped = MessagePackSerializer.Deserialize<T>(bytes, _options);
|
||||
return BenchmarkLoop.DeepEqualsViaJson(_order, roundTripped);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using AyCode.Core.Tests.TestModels;
|
||||
using AyCode.Core.Tests.TestModels;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
|
||||
|
|
@ -10,9 +10,9 @@ namespace AyCode.Core.Serializers.Console.Benchmarks;
|
|||
/// <c>BenchmarkLoop.CreateSerializers</c>); ranks far behind binary serializers on µs/op but provides
|
||||
/// a familiar JSON baseline when needed.
|
||||
/// </summary>
|
||||
internal sealed class SystemTextJsonBenchmark : ISerializerBenchmark
|
||||
internal sealed class SystemTextJsonBenchmark<T> : ISerializerBenchmark where T : class
|
||||
{
|
||||
private readonly TestOrder_All_True _order;
|
||||
private readonly T _order;
|
||||
private readonly JsonSerializerOptions _options;
|
||||
private readonly string _serialized;
|
||||
private readonly byte[] _serializedUtf8;
|
||||
|
|
@ -20,12 +20,13 @@ internal sealed class SystemTextJsonBenchmark : ISerializerBenchmark
|
|||
public BenchmarkEngine Engine => BenchmarkEngine.SystemTextJson;
|
||||
public BenchmarkIoMode IoMode => BenchmarkIoMode.String;
|
||||
public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.Runtime; // System.Text.Json default uses reflection-based metadata (no source generator opt-in here)
|
||||
public Type OrderType => typeof(T);
|
||||
public string OptionsPreset { get; }
|
||||
public int SerializedSize => _serializedUtf8.Length;
|
||||
public long SetupSerializeAllocBytes => 0;
|
||||
public long SetupDeserializeAllocBytes => 0;
|
||||
|
||||
public SystemTextJsonBenchmark(TestOrder_All_True order, string optionsPreset)
|
||||
public SystemTextJsonBenchmark(T order, string optionsPreset)
|
||||
{
|
||||
_order = order;
|
||||
OptionsPreset = optionsPreset;
|
||||
|
|
@ -43,12 +44,12 @@ internal sealed class SystemTextJsonBenchmark : ISerializerBenchmark
|
|||
public void Serialize() => JsonSerializer.Serialize(_order, _options);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Deserialize() => JsonSerializer.Deserialize<TestOrder_All_True>(_serialized, _options);
|
||||
public void Deserialize() => JsonSerializer.Deserialize<T>(_serialized, _options);
|
||||
|
||||
public bool VerifyRoundTrip()
|
||||
{
|
||||
var json = JsonSerializer.Serialize(_order, _options);
|
||||
var roundTripped = JsonSerializer.Deserialize<TestOrder_All_True>(json, _options);
|
||||
var roundTripped = JsonSerializer.Deserialize<T>(json, _options);
|
||||
return BenchmarkLoop.DeepEqualsViaJson(_order, roundTripped);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,10 +172,10 @@ internal static class Output
|
|||
System.Console.WriteLine("║ GROUPED RESULTS BY TEST DATA ║");
|
||||
System.Console.WriteLine("╚══════════════════════════════════════════════════════════════════════════════════════════════════════╝");
|
||||
|
||||
// Print serializer options
|
||||
// Print serializer options. [OrderType] suffix shows which TestOrder variant each preset serialised.
|
||||
var optionsMap = results
|
||||
.Where(r => r.OptionsDescription != null)
|
||||
.Select(r => (r.SerializerName, r.OptionsDescription!))
|
||||
.Select(r => (r.SerializerName, r.OrderTypeName, r.OptionsDescription!))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
|
|
@ -183,8 +183,8 @@ internal static class Output
|
|||
{
|
||||
System.Console.WriteLine();
|
||||
System.Console.WriteLine(" Serializer Options:");
|
||||
foreach (var (name, opts) in optionsMap)
|
||||
System.Console.WriteLine($" {name}: {opts}");
|
||||
foreach (var (name, orderType, opts) in optionsMap)
|
||||
System.Console.WriteLine($" {name} [{orderType}]: {opts}");
|
||||
}
|
||||
|
||||
foreach (var testData in testDataSets)
|
||||
|
|
@ -415,8 +415,10 @@ internal static class Output
|
|||
var logFilePath = Path.Combine(Configuration.ResultsDirectory, $"{baseFileName}.log");
|
||||
var outputFilePath = Path.Combine(Configuration.ResultsDirectory, $"{baseFileName}.output");
|
||||
|
||||
// Save binary output to separate .output file
|
||||
var largeTestData = testDataSets.FirstOrDefault(t => t.Name.StartsWith("Large"));
|
||||
// Save binary output to separate .output file.
|
||||
// Cast to TestDataSet<TestOrder_All_False> because Phase 1 hardcodes the benchmark variant.
|
||||
// Phase 2 will replace the cast with an options-driven dispatch (matching CreateSerializers).
|
||||
var largeTestData = testDataSets.FirstOrDefault(t => t.Name.StartsWith("Large")) as TestDataSet<TestOrder_All_False>;
|
||||
if (largeTestData != null)
|
||||
{
|
||||
var outputSb = new StringBuilder();
|
||||
|
|
@ -450,17 +452,21 @@ internal static class Output
|
|||
sb.AppendLine("╚══════════════════════════════════════════════════════════════════════════════════════════════════════╝");
|
||||
sb.AppendLine();
|
||||
|
||||
// Serializer options summary
|
||||
// Serializer options summary. The bracketed [OrderType] suffix shows which TestOrder variant
|
||||
// graph each benchmark serialised — AcBinary picks variant per options preset
|
||||
// (FastMode → _All_False, Default → _All_True; see BenchmarkLoop.UsesAllFalseVariant),
|
||||
// MemPack / MsgPack always use _All_False. Distinct() de-dupes across cells (each preset
|
||||
// appears once even though it runs on every test data set).
|
||||
var optionsMap = results
|
||||
.Where(r => r.OptionsDescription != null)
|
||||
.Select(r => (r.SerializerName, r.OptionsDescription!))
|
||||
.Select(r => (r.SerializerName, r.OrderTypeName, r.OptionsDescription!))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
if (optionsMap.Count > 0)
|
||||
{
|
||||
sb.AppendLine("=== SERIALIZER OPTIONS ===");
|
||||
foreach (var (name, opts) in optionsMap)
|
||||
sb.AppendLine($" {name}: {opts}");
|
||||
foreach (var (name, orderType, opts) in optionsMap)
|
||||
sb.AppendLine($" {name} [{orderType}]: {opts}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
|
|
@ -574,10 +580,11 @@ internal static class Output
|
|||
sb.AppendLine($"Charset: {Configuration.GetCurrentCharsetName()} | Iterations: per-cell adaptive (target ~{Configuration.TargetSampleMs} ms/sample) | Warmup: {Configuration.WarmupIterations} per phase (Ser/Des isolated) | Samples: {Configuration.BenchmarkSamples} (median) + 1 pilot discarded | .NET: {Environment.Version} | TestType: {testTypeName} | UnstableCV threshold: {Configuration.UnstableCVThreshold * 100:F0}%");
|
||||
sb.AppendLine("Baseline: MemoryPack (Byte[]) (SOTA reference) | Verified: round-trip correctness checked once per cell before warmup");
|
||||
|
||||
// Options summary
|
||||
// Options summary. Bracketed [OrderType] surfaces the TestOrder variant each preset serialised —
|
||||
// see SaveResults for the variant-dispatch rationale.
|
||||
var optionsMap = results
|
||||
.Where(r => r.OptionsDescription != null)
|
||||
.Select(r => (r.SerializerName, r.OptionsDescription!))
|
||||
.Select(r => (r.SerializerName, r.OrderTypeName, r.OptionsDescription!))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
if (optionsMap.Count > 0)
|
||||
|
|
@ -585,8 +592,8 @@ internal static class Output
|
|||
sb.AppendLine();
|
||||
sb.AppendLine("## Options");
|
||||
sb.AppendLine();
|
||||
foreach (var (name, opts) in optionsMap)
|
||||
sb.AppendLine($"- **{name}**: {opts}");
|
||||
foreach (var (name, orderType, opts) in optionsMap)
|
||||
sb.AppendLine($"- **{name} [{orderType}]**: {opts}");
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
|
|
|
|||
|
|
@ -66,10 +66,15 @@ public class AcBinarySerializerSGenRuntimeCompatibilityTests
|
|||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<TestDataSet> GetTargetDataSets()
|
||||
private static IEnumerable<TestDataSet<TestOrder_All_True>> GetTargetDataSets()
|
||||
{
|
||||
// SGen↔Runtime compatibility test depends on TestOrder_All_True graphs (the AssertOrderEquivalent
|
||||
// signature + JSON canonicalisation are typed for _All_True). The bare-name BenchmarkTestDataProvider
|
||||
// alias closes the generic provider on _All_True — Phase 1 benchmark uses the sibling
|
||||
// BenchmarkTestDataProvider_All_False alias instead.
|
||||
return BenchmarkTestDataProvider
|
||||
.CreateTestDataSets()
|
||||
.Cast<TestDataSet<TestOrder_All_True>>()
|
||||
.Where(x => x.Name.StartsWith("Large") || x.Name.StartsWith("Deep"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ namespace AyCode.Core.Tests.TestModels;
|
|||
|
||||
/// <summary>
|
||||
/// Charset suffix presets for the per-property string augmentation in
|
||||
/// <c>BenchmarkTestDataProvider.ToLongString</c>. The benchmark applies the configured suffix
|
||||
/// to every short (≤ <c>FixStrMaxLength</c>) string property across the test data graph (via reflection
|
||||
/// in <c>BenchmarkTestDataProvider.EnsureAllStringsBypassFixStr</c>), producing long-string
|
||||
/// benchmark payloads with a controlled UTF-8 content profile.
|
||||
/// <c>BenchmarkStringSupport.ToLongString</c>. The benchmark applies the configured suffix to every
|
||||
/// short (≤ <c>FixStrMaxLength</c>) string property across the test data graph (via reflection in
|
||||
/// <c>BenchmarkStringSupport.EnsureAllStringsBypassFixStr</c>), producing long-string benchmark payloads
|
||||
/// with a controlled UTF-8 content profile.
|
||||
///
|
||||
/// Switch by assigning to <see cref="BenchmarkTestDataProvider.LongStringSuffix"/> from the interactive
|
||||
/// Settings → Charset submenu (or programmatically). The active charset is recorded in the .LLM
|
||||
|
|
@ -18,7 +18,7 @@ namespace AyCode.Core.Tests.TestModels;
|
|||
/// </summary>
|
||||
public static class CharsetSuffixes
|
||||
{
|
||||
/// <summary>Empty suffix — short Hungarian baseline strings (e.g. "SharedTag_All_True") stay short, hitting
|
||||
/// <summary>Empty suffix — short Hungarian baseline strings (e.g. "SharedTag") stay short, hitting
|
||||
/// the FixStr fast-path. Stress-test for FixStr / short-string code paths. Note: the baseline
|
||||
/// property values remain Hungarian; only the suffix is empty. Despite the "FixAscii" name, this
|
||||
/// option does NOT change baseline values to ASCII — it suppresses the suffix that would otherwise
|
||||
|
|
@ -47,17 +47,19 @@ public static class CharsetSuffixes
|
|||
public const string Mixed = " árvíz 你好 Привет 😀";
|
||||
}
|
||||
|
||||
public static class BenchmarkTestDataProvider
|
||||
{
|
||||
private const int FixStrMaxLength = 31;
|
||||
// ============================================================================================
|
||||
// Cross-family shared state. The charset suffix is a global benchmark configuration — settable
|
||||
// once via the interactive Menu, applied uniformly to every family's data construction. Lives in
|
||||
// a non-generic helper so it ISN'T per-closed-generic (which would cause the Menu setter to affect
|
||||
// only one family). The <see cref="BenchmarkTestDataProvider.LongStringSuffix"/> forwarding
|
||||
// property preserves the existing Menu.cs API surface.
|
||||
// ============================================================================================
|
||||
|
||||
/// <summary>
|
||||
/// Active long-string suffix appended to short string properties during benchmark data construction.
|
||||
/// Defaults to <see cref="CharsetSuffixes.Latin1Long"/> (~47-char Latin1 mixed) — backward-compatible
|
||||
/// in spirit with the prior fixed default (Latin1 mixed family, ~32 char). Switch from
|
||||
/// <see cref="CharsetSuffixes"/> to measure other UTF-8 content profiles.
|
||||
/// </summary>
|
||||
public static string LongStringSuffix = CharsetSuffixes.Latin1Long;
|
||||
internal static class BenchmarkStringSupport
|
||||
{
|
||||
internal const int FixStrMaxLength = 31;
|
||||
|
||||
internal static string LongStringSuffix = CharsetSuffixes.Latin1Long;
|
||||
|
||||
private sealed class ReferenceComparer : IEqualityComparer<object>
|
||||
{
|
||||
|
|
@ -66,219 +68,7 @@ public static class BenchmarkTestDataProvider
|
|||
public int GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj);
|
||||
}
|
||||
|
||||
public static List<TestDataSet> CreateTestDataSets(bool resetId = true)
|
||||
{
|
||||
return new List<TestDataSet>
|
||||
{
|
||||
CreateSmallTestData(resetId),
|
||||
CreateMediumTestData(resetId),
|
||||
CreateLargeTestData(resetId),
|
||||
CreateRepeatedStringsTestData(resetId),
|
||||
CreateDeepNestedTestData(resetId)
|
||||
};
|
||||
}
|
||||
|
||||
private static TestDataSet CreateSmallTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) TestDataFactory.ResetIdCounter();
|
||||
|
||||
var sharedTag = TestDataFactory.CreateTag("SharedTag_All_True");
|
||||
var sharedUser = TestDataFactory.CreateUser("shareduser");
|
||||
|
||||
var order = TestDataFactory.CreateOrder(
|
||||
itemCount: 2,
|
||||
palletsPerItem: 2,
|
||||
measurementsPerPallet: 2,
|
||||
pointsPerMeasurement: 2,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser);
|
||||
|
||||
EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet("Small (2x2x2x2)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static TestDataSet CreateMediumTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) TestDataFactory.ResetIdCounter();
|
||||
|
||||
var sharedTag = TestDataFactory.CreateTag("SharedTag_All_True");
|
||||
var sharedUser = TestDataFactory.CreateUser("shareduser");
|
||||
var sharedMeta = TestDataFactory.CreateMetadata("shared", withChild: true);
|
||||
|
||||
var sharedPreferences = new UserPreferences_All_True
|
||||
{
|
||||
Theme = "dark",
|
||||
Language = "hungarian",
|
||||
NotificationsEnabled = true,
|
||||
EmailDigestFrequency = "weekly"
|
||||
};
|
||||
sharedUser.Preferences = sharedPreferences;
|
||||
|
||||
var order = TestDataFactory.CreateOrder(
|
||||
itemCount: 3,
|
||||
palletsPerItem: 3,
|
||||
measurementsPerPallet: 3,
|
||||
pointsPerMeasurement: 4,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser,
|
||||
sharedMetadata: sharedMeta,
|
||||
sharedPreferences: sharedPreferences);
|
||||
|
||||
EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet("Medium (3x3x3x4)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static TestDataSet CreateLargeTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) TestDataFactory.ResetIdCounter();
|
||||
|
||||
var sharedTag = TestDataFactory.CreateTag("SharedTag_All_True");
|
||||
var sharedUser = TestDataFactory.CreateUser("shareduser");
|
||||
|
||||
var sharedPreferences = new UserPreferences_All_True
|
||||
{
|
||||
Theme = "light",
|
||||
Language = "german",
|
||||
NotificationsEnabled = false,
|
||||
EmailDigestFrequency = "daily"
|
||||
};
|
||||
sharedUser.Preferences = sharedPreferences;
|
||||
|
||||
var order = TestDataFactory.CreateOrder(
|
||||
itemCount: 5,
|
||||
palletsPerItem: 5,
|
||||
measurementsPerPallet: 5,
|
||||
pointsPerMeasurement: 10,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser,
|
||||
sharedPreferences: sharedPreferences);
|
||||
|
||||
EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet("Large (5x5x5x10)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static TestDataSet CreateRepeatedStringsTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) TestDataFactory.ResetIdCounter();
|
||||
|
||||
var sharedTag = TestDataFactory.CreateTag("RepeatedTag");
|
||||
var sharedUser = TestDataFactory.CreateUser("repeateduser");
|
||||
|
||||
var sharedPreferences = new UserPreferences_All_True
|
||||
{
|
||||
Theme = "dark",
|
||||
Language = "hungarian",
|
||||
NotificationsEnabled = true,
|
||||
EmailDigestFrequency = "weekly"
|
||||
};
|
||||
sharedUser.Preferences = sharedPreferences;
|
||||
|
||||
var order = TestDataFactory.CreateOrder(
|
||||
itemCount: 10,
|
||||
palletsPerItem: 2,
|
||||
measurementsPerPallet: 2,
|
||||
pointsPerMeasurement: 2,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser,
|
||||
sharedPreferences: sharedPreferences);
|
||||
|
||||
// Repeated string fields — ProductName on items + PalletCode on pallets. Both are common
|
||||
// across the hierarchy, exercising string-interning deduplication on the Default preset
|
||||
// (which has UseStringInterning = All). Targeting ~20% repeated-string share overall.
|
||||
// Baselines are short ASCII (≤ FixStrMaxLength) so EnsureAllStringsBypassFixStr appends the
|
||||
// active CharsetSuffix — the resulting payload's UTF-8 content profile is governed entirely
|
||||
// by the selected charset (not contaminated by hard-coded Hungarian baseline values).
|
||||
foreach (var item in order.Items)
|
||||
{
|
||||
item.Status = TestStatus.Processing;
|
||||
item.ProductName = "ProductName";
|
||||
|
||||
foreach (var pallet in item.Pallets)
|
||||
{
|
||||
pallet.PalletCode = "PalletCode";
|
||||
}
|
||||
}
|
||||
|
||||
EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet("Repeated Strings (10 items)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static TestDataSet CreateDeepNestedTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) TestDataFactory.ResetIdCounter();
|
||||
|
||||
var sharedTag = TestDataFactory.CreateTag("DeepTag");
|
||||
var sharedUser = TestDataFactory.CreateUser("deepuser");
|
||||
var sharedCategory = TestDataFactory.CreateCategory("DeepCategory");
|
||||
|
||||
var sharedPreferences = new UserPreferences_All_True
|
||||
{
|
||||
Theme = "light",
|
||||
Language = "french",
|
||||
NotificationsEnabled = false,
|
||||
EmailDigestFrequency = "monthly"
|
||||
};
|
||||
sharedUser.Preferences = sharedPreferences;
|
||||
|
||||
var order = TestDataFactory.CreateOrder(
|
||||
itemCount: 2,
|
||||
palletsPerItem: 4,
|
||||
measurementsPerPallet: 4,
|
||||
pointsPerMeasurement: 8,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser,
|
||||
sharedPreferences: sharedPreferences,
|
||||
sharedCategory: sharedCategory);
|
||||
|
||||
EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet("Deep Nested (2x4x4x8)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static void ClearDeepLevelRefs(TestOrder_All_True order)
|
||||
{
|
||||
// Keep shared IId refs at the pallet level (Tag + Inspector) — these contribute the bulk of
|
||||
// the ~20% IId-ref share that the test data targets. Only Category is cleared at this level
|
||||
// (one-of-three clears keep the share moderate). The deeper measurement / point levels are
|
||||
// cleared entirely so deep-tree ref noise does not skew the share upward beyond ~20%.
|
||||
foreach (var item in order.Items)
|
||||
{
|
||||
foreach (var pallet in item.Pallets)
|
||||
{
|
||||
// pallet.Tag = null; // KEEP for ~20% IId-ref share (was cleared)
|
||||
// pallet.Inspector = null; // KEEP for ~20% IId-ref share (was cleared)
|
||||
pallet.Category = null;
|
||||
|
||||
foreach (var measurement in pallet.Measurements)
|
||||
{
|
||||
measurement.Tag = null;
|
||||
measurement.Operator = null;
|
||||
|
||||
foreach (var point in measurement.Points)
|
||||
{
|
||||
point.Tag = null;
|
||||
point.Verifier = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureAllStringsBypassFixStr(object? root)
|
||||
internal static void EnsureAllStringsBypassFixStr(object? root)
|
||||
{
|
||||
if (root == null) return;
|
||||
|
||||
|
|
@ -338,10 +128,318 @@ public static class BenchmarkTestDataProvider
|
|||
}
|
||||
}
|
||||
|
||||
public class TestDataSet<TOrder>
|
||||
// ============================================================================================
|
||||
// Generic test-data provider. One closing-generic alias per family — see
|
||||
// <see cref="BenchmarkTestDataProvider"/> (the <c>_All_True</c> family, MSTEST-compatible name) and
|
||||
// <see cref="BenchmarkTestDataProvider_All_False"/> (the <c>_All_False</c> family, Phase 1 benchmark
|
||||
// target). The five cell-creator methods + ClearDeepLevelRefs are written once on the generic base,
|
||||
// using the constrained <c>TestDataFactory<TOrder, ...></c> for per-family element creation.
|
||||
// ============================================================================================
|
||||
|
||||
public abstract class BenchmarkTestDataProvider<TOrder, TItem, TPallet, TMeasurement, TPoint, TTag, TUser, TCategory, TMetadata, TPreferences>
|
||||
where TOrder : TestOrderBase<TItem, TTag, TUser, TCategory, TMetadata, TPreferences>, new()
|
||||
where TItem : TestOrderItemBase<TPallet, TTag, TUser, TMetadata, TOrder, TPreferences>, new()
|
||||
where TPallet : TestPalletBase<TMeasurement, TTag, TUser, TCategory, TMetadata, TItem, TPreferences>, new()
|
||||
where TMeasurement : TestMeasurementBase<TPoint, TTag, TUser, TPallet, TPreferences>, new()
|
||||
where TPoint : TestMeasurementPointBase<TTag, TUser, TMeasurement, TPreferences>, new()
|
||||
where TTag : SharedTagBase, new()
|
||||
where TUser : SharedUserBase<TPreferences>, new()
|
||||
where TCategory : SharedCategoryBase, new()
|
||||
where TMetadata : MetadataInfoBase<TMetadata>, new()
|
||||
where TPreferences : UserPreferencesBase, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Active long-string suffix appended to short string properties during benchmark data construction.
|
||||
/// Forwards to <see cref="BenchmarkStringSupport.LongStringSuffix"/> (a non-generic shared field) so
|
||||
/// the setter is family-agnostic — both <c>BenchmarkTestDataProvider.LongStringSuffix = …</c> and
|
||||
/// <c>BenchmarkTestDataProvider_All_False.LongStringSuffix = …</c> route to the same backing value.
|
||||
/// Without this forwarding, a per-closed-generic static field on the base would store the suffix
|
||||
/// independently per family — the Menu setter would only affect whichever alias it addressed.
|
||||
/// </summary>
|
||||
public static string LongStringSuffix
|
||||
{
|
||||
get => BenchmarkStringSupport.LongStringSuffix;
|
||||
set => BenchmarkStringSupport.LongStringSuffix = value;
|
||||
}
|
||||
|
||||
// Shortcut alias for the matching factory closing-generic. Saves typing the 10-param cluster
|
||||
// on every Create* call inside this class.
|
||||
private static class Factory
|
||||
{
|
||||
public static void ResetIdCounter() =>
|
||||
TestDataFactory<TOrder, TItem, TPallet, TMeasurement, TPoint, TTag, TUser, TCategory, TMetadata, TPreferences>.ResetIdCounter();
|
||||
public static TTag CreateTag(string? name = null) =>
|
||||
TestDataFactory<TOrder, TItem, TPallet, TMeasurement, TPoint, TTag, TUser, TCategory, TMetadata, TPreferences>.CreateTag(name);
|
||||
public static TUser CreateUser(string? username = null) =>
|
||||
TestDataFactory<TOrder, TItem, TPallet, TMeasurement, TPoint, TTag, TUser, TCategory, TMetadata, TPreferences>.CreateUser(username);
|
||||
public static TCategory CreateCategory(string? name = null) =>
|
||||
TestDataFactory<TOrder, TItem, TPallet, TMeasurement, TPoint, TTag, TUser, TCategory, TMetadata, TPreferences>.CreateCategory(name);
|
||||
public static TMetadata CreateMetadata(string? key = null, bool withChild = false) =>
|
||||
TestDataFactory<TOrder, TItem, TPallet, TMeasurement, TPoint, TTag, TUser, TCategory, TMetadata, TPreferences>.CreateMetadata(key, withChild);
|
||||
public static TOrder CreateOrder(
|
||||
int itemCount, int palletsPerItem, int measurementsPerPallet, int pointsPerMeasurement,
|
||||
TTag? sharedTag = null, TUser? sharedUser = null, TMetadata? sharedMetadata = null,
|
||||
TPreferences? sharedPreferences = null, TCategory? sharedCategory = null) =>
|
||||
TestDataFactory<TOrder, TItem, TPallet, TMeasurement, TPoint, TTag, TUser, TCategory, TMetadata, TPreferences>.CreateOrder(
|
||||
itemCount, palletsPerItem, measurementsPerPallet, pointsPerMeasurement,
|
||||
sharedTag, sharedUser, sharedMetadata, sharedPreferences, sharedCategory);
|
||||
}
|
||||
|
||||
public static List<TestDataSet> CreateTestDataSets(bool resetId = true)
|
||||
{
|
||||
return new List<TestDataSet>
|
||||
{
|
||||
CreateSmallTestData(resetId),
|
||||
CreateMediumTestData(resetId),
|
||||
CreateLargeTestData(resetId),
|
||||
CreateRepeatedStringsTestData(resetId),
|
||||
CreateDeepNestedTestData(resetId)
|
||||
};
|
||||
}
|
||||
|
||||
private static TestDataSet<TOrder> CreateSmallTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) Factory.ResetIdCounter();
|
||||
|
||||
var sharedTag = Factory.CreateTag("SharedTag");
|
||||
var sharedUser = Factory.CreateUser("shareduser");
|
||||
|
||||
var order = Factory.CreateOrder(
|
||||
itemCount: 2,
|
||||
palletsPerItem: 2,
|
||||
measurementsPerPallet: 2,
|
||||
pointsPerMeasurement: 2,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser);
|
||||
|
||||
BenchmarkStringSupport.EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet<TOrder>("Small (2x2x2x2)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static TestDataSet<TOrder> CreateMediumTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) Factory.ResetIdCounter();
|
||||
|
||||
var sharedTag = Factory.CreateTag("SharedTag");
|
||||
var sharedUser = Factory.CreateUser("shareduser");
|
||||
var sharedMeta = Factory.CreateMetadata("shared", withChild: true);
|
||||
|
||||
var sharedPreferences = new TPreferences
|
||||
{
|
||||
Theme = "dark",
|
||||
Language = "hungarian",
|
||||
NotificationsEnabled = true,
|
||||
EmailDigestFrequency = "weekly"
|
||||
};
|
||||
sharedUser.Preferences = sharedPreferences;
|
||||
|
||||
var order = Factory.CreateOrder(
|
||||
itemCount: 3,
|
||||
palletsPerItem: 3,
|
||||
measurementsPerPallet: 3,
|
||||
pointsPerMeasurement: 4,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser,
|
||||
sharedMetadata: sharedMeta,
|
||||
sharedPreferences: sharedPreferences);
|
||||
|
||||
BenchmarkStringSupport.EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet<TOrder>("Medium (3x3x3x4)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static TestDataSet<TOrder> CreateLargeTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) Factory.ResetIdCounter();
|
||||
|
||||
var sharedTag = Factory.CreateTag("SharedTag");
|
||||
var sharedUser = Factory.CreateUser("shareduser");
|
||||
|
||||
var sharedPreferences = new TPreferences
|
||||
{
|
||||
Theme = "light",
|
||||
Language = "german",
|
||||
NotificationsEnabled = false,
|
||||
EmailDigestFrequency = "daily"
|
||||
};
|
||||
sharedUser.Preferences = sharedPreferences;
|
||||
|
||||
var order = Factory.CreateOrder(
|
||||
itemCount: 5,
|
||||
palletsPerItem: 5,
|
||||
measurementsPerPallet: 5,
|
||||
pointsPerMeasurement: 10,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser,
|
||||
sharedPreferences: sharedPreferences);
|
||||
|
||||
BenchmarkStringSupport.EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet<TOrder>("Large (5x5x5x10)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static TestDataSet<TOrder> CreateRepeatedStringsTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) Factory.ResetIdCounter();
|
||||
|
||||
var sharedTag = Factory.CreateTag("RepeatedTag");
|
||||
var sharedUser = Factory.CreateUser("repeateduser");
|
||||
|
||||
var sharedPreferences = new TPreferences
|
||||
{
|
||||
Theme = "dark",
|
||||
Language = "hungarian",
|
||||
NotificationsEnabled = true,
|
||||
EmailDigestFrequency = "weekly"
|
||||
};
|
||||
sharedUser.Preferences = sharedPreferences;
|
||||
|
||||
var order = Factory.CreateOrder(
|
||||
itemCount: 10,
|
||||
palletsPerItem: 2,
|
||||
measurementsPerPallet: 2,
|
||||
pointsPerMeasurement: 2,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser,
|
||||
sharedPreferences: sharedPreferences);
|
||||
|
||||
// Repeated string fields — ProductName on items + PalletCode on pallets. Both are common
|
||||
// across the hierarchy, exercising string-interning deduplication on the Default preset
|
||||
// (which has UseStringInterning = All). Targeting ~20% repeated-string share overall.
|
||||
// Baselines are short ASCII (≤ FixStrMaxLength) so EnsureAllStringsBypassFixStr appends the
|
||||
// active CharsetSuffix — the resulting payload's UTF-8 content profile is governed entirely
|
||||
// by the selected charset (not contaminated by hard-coded Hungarian baseline values).
|
||||
foreach (var item in order.Items)
|
||||
{
|
||||
item.Status = TestStatus.Processing;
|
||||
item.ProductName = "ProductName";
|
||||
|
||||
foreach (var pallet in item.Pallets)
|
||||
{
|
||||
pallet.PalletCode = "PalletCode";
|
||||
}
|
||||
}
|
||||
|
||||
BenchmarkStringSupport.EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet<TOrder>("Repeated Strings (10 items)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static TestDataSet<TOrder> CreateDeepNestedTestData(bool resetId = true)
|
||||
{
|
||||
if (resetId) Factory.ResetIdCounter();
|
||||
|
||||
var sharedTag = Factory.CreateTag("DeepTag");
|
||||
var sharedUser = Factory.CreateUser("deepuser");
|
||||
var sharedCategory = Factory.CreateCategory("DeepCategory");
|
||||
|
||||
var sharedPreferences = new TPreferences
|
||||
{
|
||||
Theme = "light",
|
||||
Language = "french",
|
||||
NotificationsEnabled = false,
|
||||
EmailDigestFrequency = "monthly"
|
||||
};
|
||||
sharedUser.Preferences = sharedPreferences;
|
||||
|
||||
var order = Factory.CreateOrder(
|
||||
itemCount: 2,
|
||||
palletsPerItem: 4,
|
||||
measurementsPerPallet: 4,
|
||||
pointsPerMeasurement: 8,
|
||||
sharedTag: sharedTag,
|
||||
sharedUser: sharedUser,
|
||||
sharedPreferences: sharedPreferences,
|
||||
sharedCategory: sharedCategory);
|
||||
|
||||
BenchmarkStringSupport.EnsureAllStringsBypassFixStr(order);
|
||||
|
||||
ClearDeepLevelRefs(order);
|
||||
|
||||
return new TestDataSet<TOrder>("Deep Nested (2x4x4x8)", order, iidRefPercent: 20);
|
||||
}
|
||||
|
||||
private static void ClearDeepLevelRefs(TOrder order)
|
||||
{
|
||||
// Keep shared IId refs at the pallet level (Tag + Inspector) — these contribute the bulk of
|
||||
// the ~20% IId-ref share that the test data targets. Only Category is cleared at this level
|
||||
// (one-of-three clears keep the share moderate). The deeper measurement / point levels are
|
||||
// cleared entirely so deep-tree ref noise does not skew the share upward beyond ~20%.
|
||||
foreach (var item in order.Items)
|
||||
{
|
||||
foreach (var pallet in item.Pallets)
|
||||
{
|
||||
// pallet.Tag = null; // KEEP for ~20% IId-ref share (was cleared)
|
||||
// pallet.Inspector = null; // KEEP for ~20% IId-ref share (was cleared)
|
||||
pallet.Category = null;
|
||||
|
||||
foreach (var measurement in pallet.Measurements)
|
||||
{
|
||||
measurement.Tag = null;
|
||||
measurement.Operator = null;
|
||||
|
||||
foreach (var point in measurement.Points)
|
||||
{
|
||||
point.Tag = null;
|
||||
point.Verifier = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================================
|
||||
// Closing-generic aliases for the provider. Same pattern as the factory: a bare-name class for
|
||||
// MSTEST backward compatibility (kept on _All_True), and a _All_False suffix variant for the
|
||||
// Phase 1 benchmark target. The static <c>LongStringSuffix</c> forwarding property lives on the
|
||||
// generic base above — accessible identically through either alias (<c>BenchmarkTestDataProvider.LongStringSuffix</c>
|
||||
// or <c>BenchmarkTestDataProvider_All_False.LongStringSuffix</c>), both routing to the same
|
||||
// <see cref="BenchmarkStringSupport.LongStringSuffix"/> shared field. Symmetric API surface across
|
||||
// families — no per-alias asymmetry.
|
||||
// ============================================================================================
|
||||
|
||||
/// <summary>
|
||||
/// <c>_All_True</c> family provider — preserves the bare-name API surface
|
||||
/// (<c>BenchmarkTestDataProvider.CreateTestDataSets()</c>) that the SGen-vs-runtime compatibility
|
||||
/// test depends on. <c>LongStringSuffix</c> is inherited from the generic base.
|
||||
/// </summary>
|
||||
public sealed class BenchmarkTestDataProvider : BenchmarkTestDataProvider<
|
||||
TestOrder_All_True, TestOrderItem_All_True, TestPallet_All_True, TestMeasurement_All_True, TestMeasurementPoint_All_True,
|
||||
SharedTag_All_True, SharedUser_All_True, SharedCategory_All_True, MetadataInfo_All_True, UserPreferences_All_True>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>_All_False</c> family provider — Phase 1 benchmark target. Inherits the generic cell-creator
|
||||
/// methods unchanged; the closed-generic <c>new TOrder()</c> calls inside the cell methods construct
|
||||
/// <c>TestOrder_All_False</c> graphs.
|
||||
/// </summary>
|
||||
public sealed class BenchmarkTestDataProvider_All_False : BenchmarkTestDataProvider<
|
||||
TestOrder_All_False, TestOrderItem_All_False, TestPallet_All_False, TestMeasurement_All_False, TestMeasurementPoint_All_False,
|
||||
SharedTag_All_False, SharedUser_All_False, SharedCategory_All_False, MetadataInfo_All_False, UserPreferences_All_False>
|
||||
{
|
||||
}
|
||||
|
||||
// ============================================================================================
|
||||
// TestDataSet — abstract metadata base + generic-ordered concrete. Orchestration code iterates
|
||||
// over the base type (Name/DisplayName/TypeName/IIdRefPercent only); concrete consumers
|
||||
// (CreateSerializers, Output binary-output dump) downcast to TestDataSet<TOrder> to access the
|
||||
// typed Order.
|
||||
// ============================================================================================
|
||||
|
||||
public abstract class TestDataSet
|
||||
{
|
||||
public string Name { get; }
|
||||
public TOrder Order { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Percentage of IId shared references in the data (0-100).
|
||||
|
|
@ -349,14 +447,20 @@ public class TestDataSet<TOrder>
|
|||
/// </summary>
|
||||
public int IIdRefPercent { get; }
|
||||
|
||||
public TestDataSet(string name, TOrder order, int iidRefPercent = 0)
|
||||
// Type-keyed variant registry. Phase 2 multi-variant dispatch: AcBinary's options preset
|
||||
// decides which variant graph it serializes (FastMode → _All_False, Default → _All_True),
|
||||
// while MemPack/MsgPack canonically use one (typically _All_True). The cells build all
|
||||
// known variants upfront and register them here so CreateSerializers can hand each benchmark
|
||||
// its matching graph instance.
|
||||
private readonly Dictionary<Type, object> _variants = new();
|
||||
|
||||
protected TestDataSet(string name, int iidRefPercent)
|
||||
{
|
||||
Name = name;
|
||||
Order = order;
|
||||
IIdRefPercent = iidRefPercent;
|
||||
}
|
||||
|
||||
public string TypeName => Order.GetType().Name;
|
||||
public abstract string TypeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets display name including IId ref percentage if set.
|
||||
|
|
@ -364,12 +468,43 @@ public class TestDataSet<TOrder>
|
|||
public string DisplayName => IIdRefPercent > 0
|
||||
? $"{Name} [{IIdRefPercent}% IId refs]"
|
||||
: Name;
|
||||
|
||||
/// <summary>
|
||||
/// Register a variant graph for this cell. Called by builders. Idempotent on the same type
|
||||
/// (last-write-wins, no error) so an alias's primary registration is harmless even if
|
||||
/// cross-registration adds the same variant later.
|
||||
/// </summary>
|
||||
public void RegisterVariant<T>(T variant) where T : class => _variants[typeof(T)] = variant;
|
||||
|
||||
/// <summary>
|
||||
/// Get a registered variant by type. Throws <see cref="InvalidOperationException"/> if not
|
||||
/// registered — fail-fast surfaces a mismatch between the variant a benchmark expects and
|
||||
/// what the cell-builder populated.
|
||||
/// </summary>
|
||||
public T GetOrder<T>() where T : class
|
||||
{
|
||||
if (_variants.TryGetValue(typeof(T), out var v)) return (T)v;
|
||||
throw new InvalidOperationException($"Variant '{typeof(T).Name}' not registered for cell '{Name}' (registered: {string.Join(", ", _variants.Keys.Select(k => k.Name))})");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether a variant is registered. Use to gate optional benchmarks that may not have
|
||||
/// their variant prepared in every cell.
|
||||
/// </summary>
|
||||
public bool HasOrder<T>() where T : class => _variants.ContainsKey(typeof(T));
|
||||
}
|
||||
|
||||
public sealed class TestDataSet : TestDataSet<TestOrder_All_True>
|
||||
public sealed class TestDataSet<TOrder> : TestDataSet
|
||||
where TOrder : class
|
||||
{
|
||||
public TestDataSet(string name, TestOrder_All_True order, int iidRefPercent = 0)
|
||||
: base(name, order, iidRefPercent)
|
||||
public TOrder Order { get; }
|
||||
|
||||
public TestDataSet(string name, TOrder order, int iidRefPercent = 0)
|
||||
: base(name, iidRefPercent)
|
||||
{
|
||||
Order = order;
|
||||
RegisterVariant(order); // primary registers itself
|
||||
}
|
||||
|
||||
public override string TypeName => Order.GetType().Name;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ public abstract class SharedCategoryBase : IId<int>
|
|||
public DateTime? UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
public abstract class SharedUserBase : IId<int>
|
||||
public abstract class SharedUserBase<TPreferences> : IId<int>
|
||||
where TPreferences : UserPreferencesBase
|
||||
{
|
||||
[Key(0)]
|
||||
public int Id { get; set; }
|
||||
|
|
@ -72,7 +73,7 @@ public abstract class SharedUserBase : IId<int>
|
|||
[Key(8)]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
[Key(9)]
|
||||
public UserPreferences_All_True? Preferences { get; set; }
|
||||
public TPreferences? Preferences { get; set; }
|
||||
}
|
||||
|
||||
public abstract class UserPreferencesBase
|
||||
|
|
@ -90,7 +91,8 @@ public abstract class UserPreferencesBase
|
|||
public string? EmailDigestFrequency { get; set; }
|
||||
}
|
||||
|
||||
public abstract class MetadataInfoBase
|
||||
public abstract class MetadataInfoBase<TSelf>
|
||||
where TSelf : MetadataInfoBase<TSelf>
|
||||
{
|
||||
[AcStringIntern(true)]
|
||||
[Key(0)]
|
||||
|
|
@ -102,14 +104,20 @@ public abstract class MetadataInfoBase
|
|||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[Key(3)]
|
||||
public MetadataInfo_All_True? ChildMetadata { get; set; }
|
||||
public TSelf? ChildMetadata { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Order Hierarchy Base Types
|
||||
|
||||
public abstract class TestOrderBase : IId<int>
|
||||
public abstract class TestOrderBase<TItem, TTag, TUser, TCategory, TMetadata, TPreferences> : IId<int>
|
||||
where TItem : class
|
||||
where TTag : SharedTagBase
|
||||
where TUser : SharedUserBase<TPreferences>
|
||||
where TCategory : SharedCategoryBase
|
||||
where TMetadata : MetadataInfoBase<TMetadata>
|
||||
where TPreferences : UserPreferencesBase
|
||||
{
|
||||
[Key(0)]
|
||||
public int Id { get; set; }
|
||||
|
|
@ -130,35 +138,35 @@ public abstract class TestOrderBase : IId<int>
|
|||
public decimal TotalAmount { get; set; }
|
||||
|
||||
[Key(6)]
|
||||
public List<TestOrderItem_All_True> Items { get; set; } = [];
|
||||
public List<TItem> Items { get; set; } = [];
|
||||
|
||||
[Key(7)]
|
||||
public SharedTag_All_True? PrimaryTag { get; set; }
|
||||
public TTag? PrimaryTag { get; set; }
|
||||
|
||||
[Key(8)]
|
||||
public SharedTag_All_True? SecondaryTag { get; set; }
|
||||
public TTag? SecondaryTag { get; set; }
|
||||
|
||||
[Key(9)]
|
||||
public SharedUser_All_True? Owner { get; set; }
|
||||
public TUser? Owner { get; set; }
|
||||
|
||||
[Key(10)]
|
||||
public SharedCategory_All_True? Category { get; set; }
|
||||
public TCategory? Category { get; set; }
|
||||
|
||||
[Key(11)]
|
||||
public List<SharedTag_All_True> Tags { get; set; } = [];
|
||||
public List<TTag> Tags { get; set; } = [];
|
||||
|
||||
[Key(12)]
|
||||
public MetadataInfo_All_True? OrderMetadata { get; set; }
|
||||
public TMetadata? OrderMetadata { get; set; }
|
||||
|
||||
[Key(13)]
|
||||
public MetadataInfo_All_True? AuditMetadata { get; set; }
|
||||
public TMetadata? AuditMetadata { get; set; }
|
||||
|
||||
[Key(14)]
|
||||
public List<MetadataInfo_All_True> MetadataList { get; set; } = [];
|
||||
public List<TMetadata> MetadataList { get; set; } = [];
|
||||
|
||||
[JsonNoMergeCollection]
|
||||
[Key(15)]
|
||||
public List<TestOrderItem_All_True> NoMergeItems { get; set; } = [];
|
||||
public List<TItem> NoMergeItems { get; set; } = [];
|
||||
|
||||
[MemoryPackIgnore]
|
||||
[JsonIgnore]
|
||||
|
|
@ -167,7 +175,13 @@ public abstract class TestOrderBase : IId<int>
|
|||
public object? Parent { get; set; }
|
||||
}
|
||||
|
||||
public abstract class TestOrderItemBase : IId<int>
|
||||
public abstract class TestOrderItemBase<TPallet, TTag, TUser, TMetadata, TParentOrder, TPreferences> : IId<int>
|
||||
where TPallet : class
|
||||
where TTag : SharedTagBase
|
||||
where TUser : SharedUserBase<TPreferences>
|
||||
where TMetadata : MetadataInfoBase<TMetadata>
|
||||
where TParentOrder : class
|
||||
where TPreferences : UserPreferencesBase
|
||||
{
|
||||
[Key(0)]
|
||||
public int Id { get; set; }
|
||||
|
|
@ -186,25 +200,32 @@ public abstract class TestOrderItemBase : IId<int>
|
|||
public TestStatus Status { get; set; } = TestStatus.Pending;
|
||||
|
||||
[Key(5)]
|
||||
public List<TestPallet_All_True> Pallets { get; set; } = [];
|
||||
public List<TPallet> Pallets { get; set; } = [];
|
||||
|
||||
[Key(6)]
|
||||
public SharedTag_All_True? Tag { get; set; }
|
||||
public TTag? Tag { get; set; }
|
||||
|
||||
[Key(7)]
|
||||
public SharedUser_All_True? Assignee { get; set; }
|
||||
public TUser? Assignee { get; set; }
|
||||
|
||||
[Key(8)]
|
||||
public MetadataInfo_All_True? ItemMetadata { get; set; }
|
||||
public TMetadata? ItemMetadata { get; set; }
|
||||
|
||||
[MemoryPackIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
[BsonIgnore]
|
||||
public TestOrder_All_True? ParentOrder { get; set; }
|
||||
public TParentOrder? ParentOrder { get; set; }
|
||||
}
|
||||
|
||||
public abstract class TestPalletBase : IId<int>
|
||||
public abstract class TestPalletBase<TMeasurement, TTag, TUser, TCategory, TMetadata, TParentItem, TPreferences> : IId<int>
|
||||
where TMeasurement : class
|
||||
where TTag : SharedTagBase
|
||||
where TUser : SharedUserBase<TPreferences>
|
||||
where TCategory : SharedCategoryBase
|
||||
where TMetadata : MetadataInfoBase<TMetadata>
|
||||
where TParentItem : class
|
||||
where TPreferences : UserPreferencesBase
|
||||
{
|
||||
[Key(0)]
|
||||
public int Id { get; set; }
|
||||
|
|
@ -222,28 +243,33 @@ public abstract class TestPalletBase : IId<int>
|
|||
public double Weight { get; set; }
|
||||
|
||||
[Key(5)]
|
||||
public List<TestMeasurement_All_True> Measurements { get; set; } = [];
|
||||
public List<TMeasurement> Measurements { get; set; } = [];
|
||||
|
||||
[Key(6)]
|
||||
public SharedTag_All_True? Tag { get; set; }
|
||||
public TTag? Tag { get; set; }
|
||||
|
||||
[Key(7)]
|
||||
public SharedUser_All_True? Inspector { get; set; }
|
||||
public TUser? Inspector { get; set; }
|
||||
|
||||
[Key(8)]
|
||||
public SharedCategory_All_True? Category { get; set; }
|
||||
public TCategory? Category { get; set; }
|
||||
|
||||
[Key(9)]
|
||||
public MetadataInfo_All_True? PalletMetadata { get; set; }
|
||||
public TMetadata? PalletMetadata { get; set; }
|
||||
|
||||
[MemoryPackIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
[BsonIgnore]
|
||||
public TestOrderItem_All_True? ParentItem { get; set; }
|
||||
public TParentItem? ParentItem { get; set; }
|
||||
}
|
||||
|
||||
public abstract class TestMeasurementBase : IId<int>
|
||||
public abstract class TestMeasurementBase<TPoint, TTag, TUser, TParentPallet, TPreferences> : IId<int>
|
||||
where TPoint : class
|
||||
where TTag : SharedTagBase
|
||||
where TUser : SharedUserBase<TPreferences>
|
||||
where TParentPallet : class
|
||||
where TPreferences : UserPreferencesBase
|
||||
{
|
||||
[Key(0)]
|
||||
public int Id { get; set; }
|
||||
|
|
@ -258,22 +284,26 @@ public abstract class TestMeasurementBase : IId<int>
|
|||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[Key(4)]
|
||||
public List<TestMeasurementPoint_All_True> Points { get; set; } = [];
|
||||
public List<TPoint> Points { get; set; } = [];
|
||||
|
||||
[Key(5)]
|
||||
public SharedTag_All_True? Tag { get; set; }
|
||||
public TTag? Tag { get; set; }
|
||||
|
||||
[Key(6)]
|
||||
public SharedUser_All_True? Operator { get; set; }
|
||||
public TUser? Operator { get; set; }
|
||||
|
||||
[MemoryPackIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
[BsonIgnore]
|
||||
public TestPallet_All_True? ParentPallet { get; set; }
|
||||
public TParentPallet? ParentPallet { get; set; }
|
||||
}
|
||||
|
||||
public abstract class TestMeasurementPointBase : IId<int>
|
||||
public abstract class TestMeasurementPointBase<TTag, TUser, TParentMeasurement, TPreferences> : IId<int>
|
||||
where TTag : SharedTagBase
|
||||
where TUser : SharedUserBase<TPreferences>
|
||||
where TParentMeasurement : class
|
||||
where TPreferences : UserPreferencesBase
|
||||
{
|
||||
[Key(0)]
|
||||
public int Id { get; set; }
|
||||
|
|
@ -288,16 +318,16 @@ public abstract class TestMeasurementPointBase : IId<int>
|
|||
public DateTime MeasuredAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[Key(4)]
|
||||
public SharedTag_All_True? Tag { get; set; }
|
||||
public TTag? Tag { get; set; }
|
||||
|
||||
[Key(5)]
|
||||
public SharedUser_All_True? Verifier { get; set; }
|
||||
public TUser? Verifier { get; set; }
|
||||
|
||||
[MemoryPackIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
[BsonIgnore]
|
||||
public TestMeasurement_All_True? ParentMeasurement { get; set; }
|
||||
public TParentMeasurement? ParentMeasurement { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -6,73 +6,44 @@ using MessagePack;
|
|||
|
||||
namespace AyCode.Core.Tests.TestModels;
|
||||
|
||||
// ============================================================================
|
||||
// _All_True family — every leaf marked [AcBinarySerializable(true)] (opt-out).
|
||||
// All sub-references are _All_True-typed via the generic closing.
|
||||
// `sealed` to enable AcBinary's non-polymorphic fast-path (no type-discriminator).
|
||||
// ============================================================================
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class SharedTag_All_True : SharedTagBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class SharedTag_All_False : SharedTagBase
|
||||
public sealed partial class SharedTag_All_True : SharedTagBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class SharedCategory_All_True : SharedCategoryBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class SharedCategory_All_False : SharedCategoryBase
|
||||
public sealed partial class SharedCategory_All_True : SharedCategoryBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class SharedUser_All_True : SharedUserBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class SharedUser_All_False : SharedUserBase
|
||||
public sealed partial class SharedUser_All_True : SharedUserBase<UserPreferences_All_True>
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class UserPreferences_All_True : UserPreferencesBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class UserPreferences_All_False : UserPreferencesBase
|
||||
public sealed partial class UserPreferences_All_True : UserPreferencesBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class MetadataInfo_All_True : MetadataInfoBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class MetadataInfo_All_False : MetadataInfoBase
|
||||
public sealed partial class MetadataInfo_All_True : MetadataInfoBase<MetadataInfo_All_True>
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -82,14 +53,9 @@ public partial class MetadataInfo_All_False : MetadataInfoBase
|
|||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class TestOrder_All_True : TestOrderBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class TestOrder_All_False : TestOrderBase
|
||||
public sealed partial class TestOrder_All_True
|
||||
: TestOrderBase<TestOrderItem_All_True, SharedTag_All_True, SharedUser_All_True,
|
||||
SharedCategory_All_True, MetadataInfo_All_True, UserPreferences_All_True>
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -99,14 +65,9 @@ public partial class TestOrder_All_False : TestOrderBase
|
|||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class TestOrderItem_All_True : TestOrderItemBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class TestOrderItem_All_False : TestOrderItemBase
|
||||
public sealed partial class TestOrderItem_All_True
|
||||
: TestOrderItemBase<TestPallet_All_True, SharedTag_All_True, SharedUser_All_True,
|
||||
MetadataInfo_All_True, TestOrder_All_True, UserPreferences_All_True>
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -116,14 +77,10 @@ public partial class TestOrderItem_All_False : TestOrderItemBase
|
|||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class TestPallet_All_True : TestPalletBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class TestPallet_All_False : TestPalletBase
|
||||
public sealed partial class TestPallet_All_True
|
||||
: TestPalletBase<TestMeasurement_All_True, SharedTag_All_True, SharedUser_All_True,
|
||||
SharedCategory_All_True, MetadataInfo_All_True, TestOrderItem_All_True,
|
||||
UserPreferences_All_True>
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -133,14 +90,9 @@ public partial class TestPallet_All_False : TestPalletBase
|
|||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class TestMeasurement_All_True : TestMeasurementBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class TestMeasurement_All_False : TestMeasurementBase
|
||||
public sealed partial class TestMeasurement_All_True
|
||||
: TestMeasurementBase<TestMeasurementPoint_All_True, SharedTag_All_True, SharedUser_All_True,
|
||||
TestPallet_All_True, UserPreferences_All_True>
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -150,13 +102,95 @@ public partial class TestMeasurement_All_False : TestMeasurementBase
|
|||
[MemoryPackable]
|
||||
[AcBinarySerializable(true)]
|
||||
[MessagePackObject]
|
||||
public partial class TestMeasurementPoint_All_True : TestMeasurementPointBase
|
||||
public sealed partial class TestMeasurementPoint_All_True
|
||||
: TestMeasurementPointBase<SharedTag_All_True, SharedUser_All_True, TestMeasurement_All_True,
|
||||
UserPreferences_All_True>
|
||||
{
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// _All_False family — every leaf marked [AcBinarySerializable(false)] (opt-in).
|
||||
// All sub-references are _All_False-typed via the generic closing.
|
||||
// `sealed` to enable AcBinary's non-polymorphic fast-path.
|
||||
// ============================================================================
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public sealed partial class SharedTag_All_False : SharedTagBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public partial class TestMeasurementPoint_All_False : TestMeasurementPointBase
|
||||
public sealed partial class SharedCategory_All_False : SharedCategoryBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public sealed partial class SharedUser_All_False : SharedUserBase<UserPreferences_All_False>
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public sealed partial class UserPreferences_All_False : UserPreferencesBase
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public sealed partial class MetadataInfo_All_False : MetadataInfoBase<MetadataInfo_All_False>
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public sealed partial class TestOrder_All_False
|
||||
: TestOrderBase<TestOrderItem_All_False, SharedTag_All_False, SharedUser_All_False,
|
||||
SharedCategory_All_False, MetadataInfo_All_False, UserPreferences_All_False>
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public sealed partial class TestOrderItem_All_False
|
||||
: TestOrderItemBase<TestPallet_All_False, SharedTag_All_False, SharedUser_All_False,
|
||||
MetadataInfo_All_False, TestOrder_All_False, UserPreferences_All_False>
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public sealed partial class TestPallet_All_False
|
||||
: TestPalletBase<TestMeasurement_All_False, SharedTag_All_False, SharedUser_All_False,
|
||||
SharedCategory_All_False, MetadataInfo_All_False, TestOrderItem_All_False,
|
||||
UserPreferences_All_False>
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public sealed partial class TestMeasurement_All_False
|
||||
: TestMeasurementBase<TestMeasurementPoint_All_False, SharedTag_All_False, SharedUser_All_False,
|
||||
TestPallet_All_False, UserPreferences_All_False>
|
||||
{
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
[AcBinarySerializable(false)]
|
||||
[MessagePackObject]
|
||||
public sealed partial class TestMeasurementPoint_All_False
|
||||
: TestMeasurementPointBase<SharedTag_All_False, SharedUser_All_False, TestMeasurement_All_False,
|
||||
UserPreferences_All_False>
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,43 @@
|
|||
namespace AyCode.Core.Tests.TestModels;
|
||||
|
||||
/// <summary>
|
||||
/// Factory for creating test data hierarchies.
|
||||
/// Used by both unit tests and benchmarks.
|
||||
/// Generic factory for the 5-level test-data hierarchy (Order → OrderItem → Pallet → Measurement →
|
||||
/// MeasurementPoint) + cross-cutting shared types (Tag, Category, User, Metadata, UserPreferences).
|
||||
/// One closing-generic alias per family — see <see cref="TestDataFactory"/> (the <c>_All_True</c>
|
||||
/// family, kept on the bare class name for MSTEST backward compatibility) and
|
||||
/// <see cref="TestDataFactory_All_False"/> (the <c>_All_False</c> family).
|
||||
///
|
||||
/// All placeholder strings use Hungarian (UTF-8 multi-byte) content to exercise the UTF-8
|
||||
/// encoder/decoder path rather than the ASCII fast-path. This makes the benchmark reflect
|
||||
/// realistic i18n payloads, not just the FixStrAscii / StringAscii marker fast-paths.
|
||||
/// <para>The static <c>_idCounter</c> below is per-closed-generic (verified via C# smoke): each family
|
||||
/// has an independent ID sequence, so calls like <c>TestDataFactory.ResetIdCounter()</c> reset only the
|
||||
/// <c>_All_True</c> counter, leaving any <c>TestDataFactory_All_False.NextId()</c> sequence intact.
|
||||
/// Each family's "Reset → Next" pattern stays internally consistent.</para>
|
||||
///
|
||||
/// <para>All placeholder strings use Hungarian (UTF-8 multi-byte) content to exercise the UTF-8
|
||||
/// encoder/decoder path rather than the ASCII fast-path. This makes the benchmark reflect realistic
|
||||
/// i18n payloads, not just the FixStrAscii / StringAscii marker fast-paths.</para>
|
||||
/// </summary>
|
||||
public static class TestDataFactory
|
||||
public abstract class TestDataFactory<TOrder, TItem, TPallet, TMeasurement, TPoint, TTag, TUser, TCategory, TMetadata, TPreferences>
|
||||
where TOrder : TestOrderBase<TItem, TTag, TUser, TCategory, TMetadata, TPreferences>, new()
|
||||
where TItem : TestOrderItemBase<TPallet, TTag, TUser, TMetadata, TOrder, TPreferences>, new()
|
||||
where TPallet : TestPalletBase<TMeasurement, TTag, TUser, TCategory, TMetadata, TItem, TPreferences>, new()
|
||||
where TMeasurement : TestMeasurementBase<TPoint, TTag, TUser, TPallet, TPreferences>, new()
|
||||
where TPoint : TestMeasurementPointBase<TTag, TUser, TMeasurement, TPreferences>, new()
|
||||
where TTag : SharedTagBase, new()
|
||||
where TUser : SharedUserBase<TPreferences>, new()
|
||||
where TCategory : SharedCategoryBase, new()
|
||||
where TMetadata : MetadataInfoBase<TMetadata>, new()
|
||||
where TPreferences : UserPreferencesBase, new()
|
||||
{
|
||||
private static int _idCounter = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Reset the ID counter (call in test setup)
|
||||
/// Reset the ID counter (call in test setup). Resets ONLY this family's counter — sibling families
|
||||
/// keep their own independent counter state.
|
||||
/// </summary>
|
||||
public static void ResetIdCounter() => _idCounter = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Get the next unique ID
|
||||
/// Get the next unique ID. Per-family counter — see class docs for the isolation rationale.
|
||||
/// </summary>
|
||||
public static int NextId() => _idCounter++;
|
||||
|
||||
|
|
@ -27,10 +46,10 @@ public static class TestDataFactory
|
|||
/// <summary>
|
||||
/// Create a shared tag for cross-reference testing
|
||||
/// </summary>
|
||||
public static SharedTag_All_True CreateTag(string? name = null, string? color = null)
|
||||
public static TTag CreateTag(string? name = null, string? color = null)
|
||||
{
|
||||
var id = _idCounter++;
|
||||
return new SharedTag_All_True
|
||||
return new TTag
|
||||
{
|
||||
Id = id,
|
||||
Name = name ?? $"Címke-{id}",
|
||||
|
|
@ -45,10 +64,10 @@ public static class TestDataFactory
|
|||
/// <summary>
|
||||
/// Create a shared category
|
||||
/// </summary>
|
||||
public static SharedCategory_All_True CreateCategory(string? name = null, int? parentId = null)
|
||||
public static TCategory CreateCategory(string? name = null, int? parentId = null)
|
||||
{
|
||||
var id = _idCounter++;
|
||||
return new SharedCategory_All_True
|
||||
return new TCategory
|
||||
{
|
||||
Id = id,
|
||||
Name = name ?? $"Kategória-{id}",
|
||||
|
|
@ -64,10 +83,10 @@ public static class TestDataFactory
|
|||
/// <summary>
|
||||
/// Create a shared user for cross-reference testing
|
||||
/// </summary>
|
||||
public static SharedUser_All_True CreateUser(string? username = null, TestUserRole role = TestUserRole.User)
|
||||
public static TUser CreateUser(string? username = null, TestUserRole role = TestUserRole.User)
|
||||
{
|
||||
var id = _idCounter++;
|
||||
return new SharedUser_All_True
|
||||
return new TUser
|
||||
{
|
||||
Id = id,
|
||||
Username = username ?? $"felhasználó{id}",
|
||||
|
|
@ -78,7 +97,7 @@ public static class TestDataFactory
|
|||
Role = role,
|
||||
LastLoginAt = DateTime.UtcNow.AddHours(-id),
|
||||
CreatedAt = DateTime.UtcNow.AddYears(-1),
|
||||
Preferences = new UserPreferences_All_True
|
||||
Preferences = new TPreferences
|
||||
{
|
||||
Theme = id % 2 == 0 ? "sötét" : "világos",
|
||||
Language = "magyar",
|
||||
|
|
@ -91,10 +110,10 @@ public static class TestDataFactory
|
|||
/// <summary>
|
||||
/// Create metadata info (non-IId)
|
||||
/// </summary>
|
||||
public static MetadataInfo_All_True CreateMetadata(string? key = null, bool withChild = false)
|
||||
public static TMetadata CreateMetadata(string? key = null, bool withChild = false)
|
||||
{
|
||||
var id = _idCounter++;
|
||||
return new MetadataInfo_All_True
|
||||
return new TMetadata
|
||||
{
|
||||
Key = key ?? $"Metaadat-{id}",
|
||||
Value = $"MetaÉrték-{id}",
|
||||
|
|
@ -109,23 +128,23 @@ public static class TestDataFactory
|
|||
|
||||
/// <summary>
|
||||
/// Create a deep order hierarchy with configurable depth.
|
||||
/// Supports both IId-based (SharedTag_All_True, SharedUser, SharedCategory_All_True) and Non-IId (UserPreferences_All_True) shared references.
|
||||
/// Supports both IId-based (Tag, User, Category) and Non-IId (Preferences) shared references.
|
||||
/// </summary>
|
||||
public static TestOrder_All_True CreateOrder(
|
||||
public static TOrder CreateOrder(
|
||||
int itemCount = 2,
|
||||
int palletsPerItem = 2,
|
||||
int measurementsPerPallet = 2,
|
||||
int pointsPerMeasurement = 3,
|
||||
SharedTag_All_True? sharedTag = null,
|
||||
SharedUser_All_True? sharedUser = null,
|
||||
MetadataInfo_All_True? sharedMetadata = null,
|
||||
UserPreferences_All_True? sharedPreferences = null,
|
||||
SharedCategory_All_True? sharedCategory = null)
|
||||
TTag? sharedTag = null,
|
||||
TUser? sharedUser = null,
|
||||
TMetadata? sharedMetadata = null,
|
||||
TPreferences? sharedPreferences = null,
|
||||
TCategory? sharedCategory = null)
|
||||
{
|
||||
// If sharedUser is provided but no sharedPreferences, use the user's preferences as shared
|
||||
sharedPreferences ??= sharedUser?.Preferences;
|
||||
|
||||
var order = new TestOrder_All_True
|
||||
var order = new TOrder
|
||||
{
|
||||
Id = _idCounter++,
|
||||
OrderNumber = $"Megrendelés-{_idCounter:D4}",
|
||||
|
|
@ -165,20 +184,19 @@ public static class TestDataFactory
|
|||
|
||||
/// <summary>
|
||||
/// Create an order item with pallets.
|
||||
/// Supports both IId-based and Non-IId shared references.
|
||||
/// </summary>
|
||||
public static TestOrderItem_All_True CreateOrderItem(
|
||||
public static TItem CreateOrderItem(
|
||||
int palletCount = 2,
|
||||
int measurementsPerPallet = 2,
|
||||
int pointsPerMeasurement = 3,
|
||||
SharedTag_All_True? sharedTag = null,
|
||||
SharedUser_All_True? sharedUser = null,
|
||||
MetadataInfo_All_True? sharedMetadata = null,
|
||||
UserPreferences_All_True? sharedPreferences = null,
|
||||
SharedCategory_All_True? sharedCategory = null)
|
||||
TTag? sharedTag = null,
|
||||
TUser? sharedUser = null,
|
||||
TMetadata? sharedMetadata = null,
|
||||
TPreferences? sharedPreferences = null,
|
||||
TCategory? sharedCategory = null)
|
||||
{
|
||||
// Create assignee - if sharedUser provided, use it. Otherwise create new user with sharedPreferences
|
||||
SharedUser_All_True? assignee = sharedUser;
|
||||
TUser? assignee = sharedUser;
|
||||
if (assignee == null && sharedPreferences != null)
|
||||
{
|
||||
// Create a new user but with shared preferences (Non-IId ref testing)
|
||||
|
|
@ -186,7 +204,7 @@ public static class TestDataFactory
|
|||
assignee.Preferences = sharedPreferences;
|
||||
}
|
||||
|
||||
var item = new TestOrderItem_All_True
|
||||
var item = new TItem
|
||||
{
|
||||
Id = _idCounter++,
|
||||
ProductName = $"Termék-{_idCounter}",
|
||||
|
|
@ -215,21 +233,18 @@ public static class TestDataFactory
|
|||
return item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a pallet with measurements
|
||||
/// </summary>
|
||||
public static TestPallet_All_True CreatePallet(
|
||||
public static TPallet CreatePallet(
|
||||
int measurementCount = 2,
|
||||
int pointsPerMeasurement = 3,
|
||||
MetadataInfo_All_True? sharedMetadata = null,
|
||||
SharedTag_All_True? sharedTag = null,
|
||||
SharedUser_All_True? sharedInspector = null,
|
||||
SharedCategory_All_True? sharedCategory = null)
|
||||
TMetadata? sharedMetadata = null,
|
||||
TTag? sharedTag = null,
|
||||
TUser? sharedInspector = null,
|
||||
TCategory? sharedCategory = null)
|
||||
{
|
||||
var pallet = new TestPallet_All_True
|
||||
var pallet = new TPallet
|
||||
{
|
||||
Id = _idCounter++,
|
||||
PalletCode = $"Raklapkód-{_idCounter:D4}",
|
||||
|
|
@ -255,12 +270,12 @@ public static class TestDataFactory
|
|||
/// <summary>
|
||||
/// Create a measurement with points
|
||||
/// </summary>
|
||||
public static TestMeasurement_All_True CreateMeasurement(
|
||||
public static TMeasurement CreateMeasurement(
|
||||
int pointCount = 3,
|
||||
SharedTag_All_True? sharedTag = null,
|
||||
SharedUser_All_True? sharedOperator = null)
|
||||
TTag? sharedTag = null,
|
||||
TUser? sharedOperator = null)
|
||||
{
|
||||
var measurement = new TestMeasurement_All_True
|
||||
var measurement = new TMeasurement
|
||||
{
|
||||
Id = _idCounter++,
|
||||
Name = $"Mérés-{_idCounter}",
|
||||
|
|
@ -283,12 +298,12 @@ public static class TestDataFactory
|
|||
/// <summary>
|
||||
/// Create a measurement point
|
||||
/// </summary>
|
||||
public static TestMeasurementPoint_All_True CreateMeasurementPoint(
|
||||
SharedTag_All_True? sharedTag = null,
|
||||
SharedUser_All_True? sharedVerifier = null)
|
||||
public static TPoint CreateMeasurementPoint(
|
||||
TTag? sharedTag = null,
|
||||
TUser? sharedVerifier = null)
|
||||
{
|
||||
var id = _idCounter++;
|
||||
return new TestMeasurementPoint_All_True
|
||||
return new TPoint
|
||||
{
|
||||
Id = id,
|
||||
Label = $"MérőPont-{id}",
|
||||
|
|
@ -300,9 +315,24 @@ public static class TestDataFactory
|
|||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Benchmark Data Generation
|
||||
// ============================================================================================
|
||||
// Closing-generic aliases. Each family carries its own static <c>_idCounter</c> (per-closed-generic
|
||||
// isolation — see C# runtime semantics). The base-class generic methods are accessible through both
|
||||
// aliases unchanged.
|
||||
// ============================================================================================
|
||||
|
||||
/// <summary>
|
||||
/// <c>_All_True</c> family factory — preserves the bare-name API surface
|
||||
/// (<c>TestDataFactory.CreateTag(...)</c>, etc.) that the MSTEST tests and benchmark consumers depend
|
||||
/// on. Adds family-specific extras (<see cref="CreateBenchmarkOrder"/>, <see cref="CreateLargeScaleBenchmarkOrder"/>,
|
||||
/// <see cref="CreatePrimitiveTestData"/>) that the generic base intentionally doesn't carry.
|
||||
/// </summary>
|
||||
public sealed class TestDataFactory : TestDataFactory<
|
||||
TestOrder_All_True, TestOrderItem_All_True, TestPallet_All_True, TestMeasurement_All_True, TestMeasurementPoint_All_True,
|
||||
SharedTag_All_True, SharedUser_All_True, SharedCategory_All_True, MetadataInfo_All_True, UserPreferences_All_True>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a large graph for benchmarking with many cross-references.
|
||||
/// Creates approximately (itemCount * palletsPerItem * measurementsPerPallet * pointsPerMeasurement) objects.
|
||||
|
|
@ -322,8 +352,8 @@ public static class TestDataFactory
|
|||
|
||||
var order = new TestOrder_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
OrderNumber = $"MÉRŐTESZT-{_idCounter:D6}",
|
||||
Id = NextId(),
|
||||
OrderNumber = $"MÉRŐTESZT-{NextId():D6}",
|
||||
Status = TestStatus.Processing,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
TotalAmount = 999999.99m,
|
||||
|
|
@ -340,7 +370,7 @@ public static class TestDataFactory
|
|||
{
|
||||
var item = new TestOrderItem_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
Id = NextId(),
|
||||
ProductName = $"MérőTermék-{i}",
|
||||
Quantity = 100 + i * 10,
|
||||
UnitPrice = 25.99m + i,
|
||||
|
|
@ -355,7 +385,7 @@ public static class TestDataFactory
|
|||
{
|
||||
var pallet = new TestPallet_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
Id = NextId(),
|
||||
PalletCode = $"Raklapkód-{i}-{p}",
|
||||
TrayCount = 10 + p,
|
||||
Status = (TestStatus)(p % 4),
|
||||
|
|
@ -368,7 +398,7 @@ public static class TestDataFactory
|
|||
{
|
||||
var measurement = new TestMeasurement_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
Id = NextId(),
|
||||
Name = $"Mérés-{i}-{p}-{m}",
|
||||
TotalWeight = 50.0 + m * 10,
|
||||
CreatedAt = DateTime.UtcNow.AddMinutes(-m)
|
||||
|
|
@ -379,7 +409,7 @@ public static class TestDataFactory
|
|||
{
|
||||
var point = new TestMeasurementPoint_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
Id = NextId(),
|
||||
Label = $"MérőPnt-{i}-{p}-{m}-{pt}",
|
||||
Value = 1.0 + pt * 0.5,
|
||||
MeasuredAt = DateTime.UtcNow.AddSeconds(-pt)
|
||||
|
|
@ -401,11 +431,6 @@ public static class TestDataFactory
|
|||
/// Create a large-scale benchmark order similar to production workloads.
|
||||
/// Targets ~50,000-100,000+ IId objects with deep hierarchy and shared references.
|
||||
/// </summary>
|
||||
/// <param name="rootItemCount">Number of root items (default 500 for ~50K objects, use 2200 for production-like)</param>
|
||||
/// <param name="palletsPerItem">Pallets per item</param>
|
||||
/// <param name="measurementsPerPallet">Measurements per pallet</param>
|
||||
/// <param name="pointsPerMeasurement">Points per measurement</param>
|
||||
/// <returns>Large TestOrder_All_True with many IId references</returns>
|
||||
public static TestOrder_All_True CreateLargeScaleBenchmarkOrder(
|
||||
int rootItemCount = 500,
|
||||
int palletsPerItem = 3,
|
||||
|
|
@ -422,8 +447,8 @@ public static class TestDataFactory
|
|||
|
||||
var order = new TestOrder_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
OrderNumber = $"NAGYMÉRET-{_idCounter:D8}",
|
||||
Id = NextId(),
|
||||
OrderNumber = $"NAGYMÉRET-{NextId():D8}",
|
||||
Status = TestStatus.Processing,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
TotalAmount = 9999999.99m,
|
||||
|
|
@ -440,7 +465,7 @@ public static class TestDataFactory
|
|||
{
|
||||
var item = new TestOrderItem_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
Id = NextId(),
|
||||
ProductName = $"Termék-{i}",
|
||||
Quantity = 100 + i,
|
||||
UnitPrice = 10.99m + (i % 100),
|
||||
|
|
@ -455,7 +480,7 @@ public static class TestDataFactory
|
|||
{
|
||||
var pallet = new TestPallet_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
Id = NextId(),
|
||||
PalletCode = $"Raklapkód-{i}-{p}",
|
||||
TrayCount = 5 + (p % 10),
|
||||
Status = (TestStatus)(p % 4),
|
||||
|
|
@ -468,7 +493,7 @@ public static class TestDataFactory
|
|||
{
|
||||
var measurement = new TestMeasurement_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
Id = NextId(),
|
||||
Name = $"Mérés-{i}-{p}-{m}",
|
||||
TotalWeight = 10.0 + m,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
|
|
@ -479,7 +504,7 @@ public static class TestDataFactory
|
|||
{
|
||||
var point = new TestMeasurementPoint_All_True
|
||||
{
|
||||
Id = _idCounter++,
|
||||
Id = NextId(),
|
||||
Label = $"MérőPnt-{i}-{p}-{m}-{pt}",
|
||||
Value = pt * 0.1,
|
||||
MeasuredAt = DateTime.UtcNow
|
||||
|
|
@ -532,6 +557,16 @@ public static class TestDataFactory
|
|||
NullableIntNull = null
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>_All_False</c> family factory — benchmark Phase 1 target. The generic-base factory methods
|
||||
/// produce <c>_All_False</c>-typed graphs via the closed-generic <c>new T()</c> calls. No
|
||||
/// family-specific extras here (the legacy <see cref="TestDataFactory.CreateBenchmarkOrder"/> etc.
|
||||
/// stay on the <c>_All_True</c> alias because their existing consumers are <c>_All_True</c>-tied).
|
||||
/// </summary>
|
||||
public sealed class TestDataFactory_All_False : TestDataFactory<
|
||||
TestOrder_All_False, TestOrderItem_All_False, TestPallet_All_False, TestMeasurement_All_False, TestMeasurementPoint_All_False,
|
||||
SharedTag_All_False, SharedUser_All_False, SharedCategory_All_False, MetadataInfo_All_False, UserPreferences_All_False>
|
||||
{
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue