using AyCode.Core.Serializers; using AyCode.Core.Serializers.Attributes; using AyCode.Core.Serializers.Binaries; using MemoryPack; using System.Reflection; namespace AyCode.Core.Benchmarks.Workloads.Scenarios; /// /// Per-engine options-formatting + selection helpers shared by all benchmark rows. Centralizes /// the Options-column display string (so the .log / .LLM / console headers stay consistent), the /// MemoryPack WireMode-aligned options selection (so AcBinary FastWire ↔ MemoryPack UTF-16 /// comparisons stay apples-to-apples), and the cached attribute-flag aggregation. /// public static class BenchmarkOptions { /// /// Aggregated feature flags across every type tagged with /// the attribute in the loaded assemblies. Cached on first access (single reflection scan at startup). /// Used by so the per-row Options column shows BOTH the configured /// options-level value AND the effective attribute-level enable flag — a feature flagged off at the /// type level overrides the options regardless of preset, and that asymmetry must surface in the log /// to avoid misreading a "RefHandling=OnlyId" / "Interning=All" line as actually active. /// Aggregation rule: if ALL tagged types have the feature enabled → true; if any tagged type /// disables it → false (a single disabling type suppresses the feature on the type-graph). /// public static readonly (bool refHandling, bool internString, bool metadata, bool idTracking, bool propertyFilter) AttrFlags = ScanAttributeFlags(); private static (bool refHandling, bool internString, bool metadata, bool idTracking, bool propertyFilter) ScanAttributeFlags() { var attrs = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => { try { return a.GetTypes(); } catch { return Array.Empty(); } }) .Select(t => t.GetCustomAttribute()) .Where(a => a != null) .ToList(); if (attrs.Count == 0) return (false, false, false, false, false); return ( refHandling: attrs.All(a => a!.EnableRefHandlingFeature), internString: attrs.All(a => a!.EnableInternStringFeature), metadata: attrs.All(a => a!.EnableMetadataFeature), idTracking: attrs.All(a => a!.EnableIdTrackingFeature), propertyFilter: attrs.All(a => a!.EnablePropertyFilterFeature)); } /// /// Common Options-column formatter for every AcBinary serializer benchmark row. Renders the /// configured options-level value AND the effective attribute-level enable flag side-by-side /// (e.g. Interning=All(opt) | False (attr)) so attribute-suppressed features cannot /// silently mislead. Pass any benchmark-specific extras (e.g. ", BufferSize=4096B") /// in — they are appended after the common fields. /// public static string BuildAcBinary(AcBinarySerializerOptions options, string extra = "") { // PropertyFilter: opt-side is "Set"/"None" depending on whether a callback is registered (the callback // itself isn't a meaningful display value); attr-side is the cross-type-aggregated bool (true = every // tagged type has the feature enabled, false = at least one type opted out via // [AcBinarySerializable(enablePropertyFilterFeature: false)] → SGen-emit + Runtime hot-loop both gate). var propFilterOpt = options.PropertyFilter == null ? "None" : "Set"; return $"WireMode={options.WireMode}, " + $"RefHandling={options.ReferenceHandling}(opt) | {AttrFlags.refHandling} (attr), " + $"Interning={options.UseStringInterning}(opt) | {AttrFlags.internString} (attr), " + $"Metadata={options.UseMetadata}(opt) | {AttrFlags.metadata} (attr), " + $"PropertyFilter={propFilterOpt}(opt) | {AttrFlags.propertyFilter} (attr), " + $"SGen={options.UseGeneratedCode}, " + $"Compression={options.UseCompression}{extra}"; } /// /// Returns MemoryPack serializer options aligned with the given for a fair /// apples-to-apples wire-format comparison: /// /// (UTF-8) — both /// engines encode UTF-8, comparison is purely about header / tier / dispatch overhead. /// (UTF-16 raw memcpy) — /// both engines write UTF-16 raw bytes, so wire-size and CPU comparison reflect the same string-encoding family. /// /// Without this alignment the FastWire vs MemPack-default comparison conflates two unrelated dimensions /// (UTF-16 raw vs UTF-8 encoded) and produces a misleading +40% wire-size delta that is structurally /// the encoding-family difference, NOT an AcBinary-specific overhead. /// public static MemoryPackSerializerOptions GetMemPack(WireMode wireMode) => wireMode == WireMode.Fast ? MemoryPackSerializerOptions.Utf16 : MemoryPackSerializerOptions.Default; }