Optimize scan codegen with compile-time scan analysis
Added compile-time scan requirement flags to SerializableClassInfo and PropInfo, and implemented recursive analysis to determine if scan work is needed for reference tracking and string interning. Updated code generation to emit scan code only when necessary, with runtime guards based on compile-time analysis. Changed AcBinarySerializerOptions.UseMetadata default to false. Increased JIT wait in Program.cs for more reliable benchmarking. These changes reduce unnecessary scan calls and improve performance.
This commit is contained in:
parent
3e935cad2f
commit
cb2ee24a4c
|
|
@ -175,7 +175,7 @@ public static class Program
|
|||
}
|
||||
|
||||
// Wait for tiered JIT background compilation to complete
|
||||
Thread.Sleep(2000);
|
||||
Thread.Sleep(3000);
|
||||
|
||||
// Run benchmarks
|
||||
System.Console.WriteLine($"Running benchmarks ({TestIterations} iterations)...\n");
|
||||
|
|
|
|||
|
|
@ -114,6 +114,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
bool hasGenWriter = false;
|
||||
bool propTypeIsIId = false;
|
||||
bool propEnableMetadata = true;
|
||||
bool childNeedsIdScan = true;
|
||||
bool childNeedsAllRefScan = true;
|
||||
bool childNeedsInternScan = true;
|
||||
string? writerClassName = null;
|
||||
string? propIdTypeName = null;
|
||||
int childTypeNameHash = 0;
|
||||
|
|
@ -133,6 +136,10 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
{
|
||||
// Read child type's EnableMetadataFeature
|
||||
propEnableMetadata = ReadEnableMetadata(resolvedType);
|
||||
var childScanFlags = ComputeNeedsScan(resolvedType);
|
||||
childNeedsIdScan = childScanFlags.needsIdScan;
|
||||
childNeedsAllRefScan = childScanFlags.needsAllRefScan;
|
||||
childNeedsInternScan = childScanFlags.needsInternScan;
|
||||
var iidIface = resolvedType.AllInterfaces.FirstOrDefault(i =>
|
||||
i.IsGenericType &&
|
||||
i.OriginalDefinition.ToDisplayString() == "AyCode.Core.Interfaces.IId<T>");
|
||||
|
|
@ -160,6 +167,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
bool elemHasGenWriter = false;
|
||||
bool elemIsIId = false;
|
||||
bool elemEnableMetadata = true;
|
||||
bool elemNeedsIdScan = true;
|
||||
bool elemNeedsAllRefScan = true;
|
||||
bool elemNeedsInternScan = true;
|
||||
string? elemWriterClassName = null;
|
||||
string? elemIdTypeName = null;
|
||||
string? collKind = null;
|
||||
|
|
@ -206,6 +216,10 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
{
|
||||
// Read element type's EnableMetadataFeature
|
||||
elemEnableMetadata = ReadEnableMetadata(resolvedElem);
|
||||
var elemScanFlags = ComputeNeedsScan(resolvedElem);
|
||||
elemNeedsIdScan = elemScanFlags.needsIdScan;
|
||||
elemNeedsAllRefScan = elemScanFlags.needsAllRefScan;
|
||||
elemNeedsInternScan = elemScanFlags.needsInternScan;
|
||||
var elemIidIface = resolvedElem.AllInterfaces.FirstOrDefault(ifc =>
|
||||
ifc.IsGenericType &&
|
||||
ifc.OriginalDefinition.ToDisplayString() == "AyCode.Core.Interfaces.IId<T>");
|
||||
|
|
@ -238,7 +252,9 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
elemKind, elemHasGenWriter, elemIsIId, elemWriterClassName, elemIdTypeName, collKind, elemFullTypeName,
|
||||
childTypeNameHash, childPropertyHashes,
|
||||
elementTypeNameHash, elementPropertyHashes,
|
||||
propEnableMetadata, elemEnableMetadata));
|
||||
propEnableMetadata, elemEnableMetadata,
|
||||
childNeedsIdScan, childNeedsAllRefScan, childNeedsInternScan,
|
||||
elemNeedsIdScan, elemNeedsAllRefScan, elemNeedsInternScan));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +279,8 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
var className = BuildFlatName(typeSymbol);
|
||||
var typeNameHash = ComputeFnvHash(typeSymbol.Name);
|
||||
var propertyNameHashes = properties.Select(prop => ComputeFnvHash(prop.Name)).ToArray();
|
||||
return new SerializableClassInfo(namespaceName, className, typeSymbol.ToDisplayString(), properties, isIId, idTypeName, enableRefHandling, typeNameHash, propertyNameHashes, enableMetadata);
|
||||
var selfScanFlags = ComputeNeedsScan(typeSymbol);
|
||||
return new SerializableClassInfo(namespaceName, className, typeSymbol.ToDisplayString(), properties, isIId, idTypeName, enableRefHandling, typeNameHash, propertyNameHashes, enableMetadata, selfScanFlags.needsIdScan, selfScanFlags.needsAllRefScan, selfScanFlags.needsInternScan);
|
||||
}
|
||||
|
||||
private static void Execute(ImmutableArray<SerializableClassInfo?> classes, SourceProductionContext context)
|
||||
|
|
@ -343,6 +360,25 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine(" public void ScanObject<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context, int depth) where TOutput : struct, IBinaryOutputBase");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
// Compile-time proven: no scan work needed for this type
|
||||
if (!ci.NeedsScan)
|
||||
{
|
||||
sb.AppendLine(" // NeedsScan=false: no ref tracking, no string interning, no scannable children");
|
||||
sb.AppendLine(" }");
|
||||
return;
|
||||
}
|
||||
|
||||
// Early return: skip scan when no active runtime feature matches this type's needs
|
||||
if (!ci.NeedsIdScan)
|
||||
{
|
||||
if (ci.NeedsAllRefScan && ci.NeedsInternScan)
|
||||
sb.AppendLine(" if (context.ReferenceHandling != ReferenceHandlingMode.All && !context.UseStringInterning) return;");
|
||||
else if (ci.NeedsAllRefScan)
|
||||
sb.AppendLine(" if (context.ReferenceHandling != ReferenceHandlingMode.All) return;");
|
||||
else if (ci.NeedsInternScan)
|
||||
sb.AppendLine(" if (!context.UseStringInterning) return;");
|
||||
}
|
||||
|
||||
// Null/depth guard — matches runtime ScanValue entry
|
||||
sb.AppendLine(" if (value == null || depth > context.MaxDepth) return;");
|
||||
sb.AppendLine();
|
||||
|
|
@ -634,13 +670,40 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
/// </summary>
|
||||
private static void EmitScanComplexSGen(StringBuilder sb, PropInfo p, string a, string i)
|
||||
{
|
||||
// Compile-time proven: child scan is no-op — skip entirely
|
||||
if (!p.ChildNeedsScan) return;
|
||||
|
||||
var writer = p.WriterClassName;
|
||||
var childVar = $"sc_{p.Name}";
|
||||
|
||||
// Null check only — ScanObject handles depth + ref tracking internally
|
||||
sb.AppendLine($"{i}var {childVar} = {a};");
|
||||
sb.AppendLine($"{i}if ({childVar} != null)");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({childVar}, context, depth + 1);");
|
||||
// 3-axis guard: IId → always call, AllRef → guard All mode, Intern → guard UseStringInterning
|
||||
string? guard = null;
|
||||
if (!p.ChildNeedsIdScan)
|
||||
{
|
||||
if (p.ChildNeedsAllRefScan && p.ChildNeedsInternScan)
|
||||
guard = "context.ReferenceHandling == ReferenceHandlingMode.All || context.UseStringInterning";
|
||||
else if (p.ChildNeedsAllRefScan)
|
||||
guard = "context.ReferenceHandling == ReferenceHandlingMode.All";
|
||||
else if (p.ChildNeedsInternScan)
|
||||
guard = "context.UseStringInterning";
|
||||
}
|
||||
|
||||
if (guard != null)
|
||||
{
|
||||
sb.AppendLine($"{i}if ({guard})");
|
||||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} var {childVar} = {a};");
|
||||
sb.AppendLine($"{i} if ({childVar} != null)");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({childVar}, context, depth + 1);");
|
||||
sb.AppendLine($"{i}}}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// IId in subtree — always call (active in OnlyId + All)
|
||||
sb.AppendLine($"{i}var {childVar} = {a};");
|
||||
sb.AppendLine($"{i}if ({childVar} != null)");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({childVar}, context, depth + 1);");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -701,12 +764,32 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
// Complex element collection with SGen writer
|
||||
if (p.ElementKind == PropertyTypeKind.Complex && p.ElementHasGeneratedWriter && p.CollectionKind != null)
|
||||
{
|
||||
// Compile-time proven: element scan is no-op — skip entirely
|
||||
if (!p.ElementNeedsScan) return;
|
||||
|
||||
var writer = p.ElementWriterClassName;
|
||||
|
||||
sb.AppendLine($"{i}var scol_{p.Name} = {a};");
|
||||
sb.AppendLine($"{i}if (scol_{p.Name} != null)");
|
||||
// 3-axis guard: IId → always scan, AllRef → guard All mode, Intern → guard UseStringInterning
|
||||
string? elemGuard = null;
|
||||
if (!p.ElementNeedsIdScan)
|
||||
{
|
||||
if (p.ElementNeedsAllRefScan && p.ElementNeedsInternScan)
|
||||
elemGuard = "context.ReferenceHandling == ReferenceHandlingMode.All || context.UseStringInterning";
|
||||
else if (p.ElementNeedsAllRefScan)
|
||||
elemGuard = "context.ReferenceHandling == ReferenceHandlingMode.All";
|
||||
else if (p.ElementNeedsInternScan)
|
||||
elemGuard = "context.UseStringInterning";
|
||||
}
|
||||
|
||||
// Guard entire collection scan with runtime check when no IId in element subtree
|
||||
if (elemGuard != null)
|
||||
sb.AppendLine($"{i}if ({elemGuard})");
|
||||
|
||||
sb.AppendLine($"{i}{{");
|
||||
sb.AppendLine($"{i} var snd_{p.Name} = depth + 1;");
|
||||
sb.AppendLine($"{i} var scol_{p.Name} = {a};");
|
||||
sb.AppendLine($"{i} if (scol_{p.Name} != null)");
|
||||
sb.AppendLine($"{i} {{");
|
||||
sb.AppendLine($"{i} var snd_{p.Name} = depth + 1;");
|
||||
|
||||
if (p.CollectionKind == "Array")
|
||||
{
|
||||
|
|
@ -731,6 +814,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
sb.AppendLine($"{i} if ({e} == null) continue;");
|
||||
sb.AppendLine($"{i} {writer}.Instance.ScanObject({e}, context, snd_{p.Name});");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i} }}");
|
||||
sb.AppendLine($"{i}}}");
|
||||
return;
|
||||
}
|
||||
|
|
@ -1168,6 +1252,116 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes whether a type needs scan pass work, split into ref tracking and string interning.
|
||||
/// Uses a per-call HashSet to guard against circular references (no static cache —
|
||||
/// static state is unsafe in incremental generators as it persists across builds).
|
||||
/// Returns (needsRefScan, needsInternScan) — these are independent axes.
|
||||
/// </summary>
|
||||
private static (bool needsIdScan, bool needsAllRefScan, bool needsInternScan) ComputeNeedsScan(ITypeSymbol type)
|
||||
{
|
||||
return ComputeNeedsScanCore(type, new HashSet<string>());
|
||||
}
|
||||
|
||||
private static (bool needsIdScan, bool needsAllRefScan, bool needsInternScan) ComputeNeedsScanCore(ITypeSymbol type, HashSet<string> visiting)
|
||||
{
|
||||
// Circular reference guard: if already visiting this type, assume true (safe fallback)
|
||||
var key = type.ToDisplayString();
|
||||
if (!visiting.Add(key))
|
||||
return (true, true, true);
|
||||
|
||||
// Read [AcBinarySerializable] flags
|
||||
var attr = type.GetAttributes().FirstOrDefault(a =>
|
||||
a.AttributeClass?.ToDisplayString() == AttributeName);
|
||||
|
||||
bool enableIdTracking = true, enableRefHandling = true, enableInternString = true;
|
||||
if (attr != null)
|
||||
{
|
||||
if (attr.ConstructorArguments.Length == 1)
|
||||
{
|
||||
var all = (bool)attr.ConstructorArguments[0].Value!;
|
||||
enableIdTracking = enableRefHandling = enableInternString = all;
|
||||
}
|
||||
else if (attr.ConstructorArguments.Length == 4)
|
||||
{
|
||||
enableIdTracking = (bool)attr.ConstructorArguments[1].Value!;
|
||||
enableRefHandling = (bool)attr.ConstructorArguments[2].Value!;
|
||||
enableInternString = (bool)attr.ConstructorArguments[3].Value!;
|
||||
}
|
||||
}
|
||||
|
||||
// IId tracking: active in OnlyId + All modes
|
||||
var isIId = enableIdTracking && type.AllInterfaces.Any(i =>
|
||||
i.IsGenericType && i.OriginalDefinition.ToDisplayString() == "AyCode.Core.Interfaces.IId<T>");
|
||||
var needsIdScan = isIId;
|
||||
// Non-IId ref tracking: active only in All mode
|
||||
var needsAllRefScan = !isIId && enableRefHandling;
|
||||
var needsInternScan = false;
|
||||
|
||||
// Check properties for string interning or complex children
|
||||
foreach (var member in type.GetMembers())
|
||||
{
|
||||
if (member is not IPropertySymbol p ||
|
||||
p.DeclaredAccessibility != Accessibility.Public ||
|
||||
p.GetMethod == null || p.SetMethod == null ||
|
||||
p.IsIndexer || p.IsStatic)
|
||||
continue;
|
||||
|
||||
var hasIgnore = p.GetAttributes().Any(a =>
|
||||
{
|
||||
var name = a.AttributeClass?.Name ?? "";
|
||||
return name == "JsonIgnoreAttribute" || name == "IgnoreMemberAttribute" || name == "BsonIgnoreAttribute";
|
||||
});
|
||||
if (hasIgnore) continue;
|
||||
|
||||
// Early exit: if all flags are already true, no need to check more properties
|
||||
if (needsIdScan && needsAllRefScan && needsInternScan) break;
|
||||
|
||||
var kind = GetKind(p.Type);
|
||||
|
||||
// String with interning?
|
||||
if (enableInternString && kind == PropertyTypeKind.String)
|
||||
{
|
||||
var internAttr = p.GetAttributes().FirstOrDefault(a =>
|
||||
a.AttributeClass?.ToDisplayString() == "AyCode.Core.Serializers.Binaries.AcStringInternAttribute");
|
||||
if (internAttr == null || (internAttr.ConstructorArguments.Length == 1 && (bool)internAttr.ConstructorArguments[0].Value!))
|
||||
needsInternScan = true;
|
||||
}
|
||||
|
||||
// Complex child → recurse
|
||||
if (kind == PropertyTypeKind.Complex)
|
||||
{
|
||||
var resolved = p.Type is INamedTypeSymbol nt ? nt.OriginalDefinition : p.Type;
|
||||
var childFlags = ComputeNeedsScanCore(resolved, visiting);
|
||||
needsIdScan |= childFlags.needsIdScan;
|
||||
needsAllRefScan |= childFlags.needsAllRefScan;
|
||||
needsInternScan |= childFlags.needsInternScan;
|
||||
}
|
||||
|
||||
// Collection → check element type
|
||||
if (kind == PropertyTypeKind.Collection)
|
||||
{
|
||||
var elemType = GetCollectionElementType(p.Type);
|
||||
if (elemType != null)
|
||||
{
|
||||
var elemKind = GetKind(elemType);
|
||||
if (enableInternString && elemKind == PropertyTypeKind.String)
|
||||
needsInternScan = true;
|
||||
if (elemKind == PropertyTypeKind.Complex)
|
||||
{
|
||||
var resolvedElem = elemType is INamedTypeSymbol ne ? ne.OriginalDefinition : elemType;
|
||||
var elemFlags = ComputeNeedsScanCore(resolvedElem, visiting);
|
||||
needsIdScan |= elemFlags.needsIdScan;
|
||||
needsAllRefScan |= elemFlags.needsAllRefScan;
|
||||
needsInternScan |= elemFlags.needsInternScan;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (needsIdScan, needsAllRefScan, needsInternScan);
|
||||
}
|
||||
|
||||
#region FNV-1a Hash (compile-time)
|
||||
|
||||
private static int ComputeFnvHash(string value)
|
||||
|
|
@ -1315,8 +1509,18 @@ internal sealed class SerializableClassInfo
|
|||
public int[] PropertyNameHashes { get; }
|
||||
/// <summary>When false, skip inline metadata and use markerless property write for this type.</summary>
|
||||
public bool EnableMetadata { get; }
|
||||
public SerializableClassInfo(string ns, string cn, string ftn, List<PropInfo> p, bool isIId, string? idTypeName, bool enableRefHandling, int typeNameHash, int[] propertyNameHashes, bool enableMetadata)
|
||||
{ Namespace = ns; ClassName = cn; FullTypeName = ftn; Properties = p; IsIId = isIId; IdTypeName = idTypeName; EnableRefHandling = enableRefHandling; TypeNameHash = typeNameHash; PropertyNameHashes = propertyNameHashes; EnableMetadata = enableMetadata; }
|
||||
/// <summary>When true, type subtree has IId types needing scan (active in OnlyId + All).</summary>
|
||||
public bool NeedsIdScan { get; }
|
||||
/// <summary>When true, type subtree has non-IId ref tracking (active only in All mode).</summary>
|
||||
public bool NeedsAllRefScan { get; }
|
||||
/// <summary>When true, type subtree needs string interning scan.</summary>
|
||||
public bool NeedsInternScan { get; }
|
||||
/// <summary>Derived: NeedsIdScan || NeedsAllRefScan.</summary>
|
||||
public bool NeedsRefScan => NeedsIdScan || NeedsAllRefScan;
|
||||
/// <summary>Derived: any scan axis active.</summary>
|
||||
public bool NeedsScan => NeedsIdScan || NeedsAllRefScan || NeedsInternScan;
|
||||
public SerializableClassInfo(string ns, string cn, string ftn, List<PropInfo> p, bool isIId, string? idTypeName, bool enableRefHandling, int typeNameHash, int[] propertyNameHashes, bool enableMetadata, bool needsIdScan, bool needsAllRefScan, bool needsInternScan)
|
||||
{ Namespace = ns; ClassName = cn; FullTypeName = ftn; Properties = p; IsIId = isIId; IdTypeName = idTypeName; EnableRefHandling = enableRefHandling; TypeNameHash = typeNameHash; PropertyNameHashes = propertyNameHashes; EnableMetadata = enableMetadata; NeedsIdScan = needsIdScan; NeedsAllRefScan = needsAllRefScan; NeedsInternScan = needsInternScan; }
|
||||
}
|
||||
|
||||
internal sealed class PropInfo
|
||||
|
|
@ -1375,6 +1579,26 @@ internal sealed class PropInfo
|
|||
public bool ChildEnableMetadata { get; }
|
||||
/// <summary>When false, collection element type skips inline metadata in generated code.</summary>
|
||||
public bool ElementEnableMetadata { get; }
|
||||
/// <summary>When true, child subtree has IId types needing scan (active in OnlyId + All).</summary>
|
||||
public bool ChildNeedsIdScan { get; }
|
||||
/// <summary>When true, child subtree has non-IId ref tracking (active only in All mode).</summary>
|
||||
public bool ChildNeedsAllRefScan { get; }
|
||||
/// <summary>When true, child subtree needs string interning scan.</summary>
|
||||
public bool ChildNeedsInternScan { get; }
|
||||
/// <summary>Derived: ChildNeedsIdScan || ChildNeedsAllRefScan.</summary>
|
||||
public bool ChildNeedsRefScan => ChildNeedsIdScan || ChildNeedsAllRefScan;
|
||||
/// <summary>Derived: any child scan axis active.</summary>
|
||||
public bool ChildNeedsScan => ChildNeedsIdScan || ChildNeedsAllRefScan || ChildNeedsInternScan;
|
||||
/// <summary>When true, element subtree has IId types needing scan (active in OnlyId + All).</summary>
|
||||
public bool ElementNeedsIdScan { get; }
|
||||
/// <summary>When true, element subtree has non-IId ref tracking (active only in All mode).</summary>
|
||||
public bool ElementNeedsAllRefScan { get; }
|
||||
/// <summary>When true, element subtree needs string interning scan.</summary>
|
||||
public bool ElementNeedsInternScan { get; }
|
||||
/// <summary>Derived: ElementNeedsIdScan || ElementNeedsAllRefScan.</summary>
|
||||
public bool ElementNeedsRefScan => ElementNeedsIdScan || ElementNeedsAllRefScan;
|
||||
/// <summary>Derived: any element scan axis active.</summary>
|
||||
public bool ElementNeedsScan => ElementNeedsIdScan || ElementNeedsAllRefScan || ElementNeedsInternScan;
|
||||
|
||||
public PropInfo(string n, string tn, string tnForTypeof, PropertyTypeKind tk, bool nullable,
|
||||
bool? stringInternAttr = null, bool hasGeneratedWriter = false, bool isIId = false, string? writerClassName = null, string? idTypeName = null,
|
||||
|
|
@ -1382,7 +1606,9 @@ internal sealed class PropInfo
|
|||
string? elementWriterClassName = null, string? elementIdTypeName = null, string? collectionKind = null, string? elementFullTypeName = null,
|
||||
int childTypeNameHash = 0, int[]? childPropertyHashes = null,
|
||||
int elementTypeNameHash = 0, int[]? elementPropertyHashes = null,
|
||||
bool childEnableMetadata = true, bool elementEnableMetadata = true)
|
||||
bool childEnableMetadata = true, bool elementEnableMetadata = true,
|
||||
bool childNeedsIdScan = true, bool childNeedsAllRefScan = true, bool childNeedsInternScan = true,
|
||||
bool elementNeedsIdScan = true, bool elementNeedsAllRefScan = true, bool elementNeedsInternScan = true)
|
||||
{
|
||||
Name = n;
|
||||
TypeName = tn;
|
||||
|
|
@ -1406,6 +1632,12 @@ internal sealed class PropInfo
|
|||
ElementPropertyHashes = elementPropertyHashes;
|
||||
ChildEnableMetadata = childEnableMetadata;
|
||||
ElementEnableMetadata = elementEnableMetadata;
|
||||
ChildNeedsIdScan = childNeedsIdScan;
|
||||
ChildNeedsAllRefScan = childNeedsAllRefScan;
|
||||
ChildNeedsInternScan = childNeedsInternScan;
|
||||
ElementNeedsIdScan = elementNeedsIdScan;
|
||||
ElementNeedsAllRefScan = elementNeedsAllRefScan;
|
||||
ElementNeedsInternScan = elementNeedsInternScan;
|
||||
// Mirror runtime _interningFlags computation from BinaryPropertyAccessorBase
|
||||
int flags = 0;
|
||||
if (stringInternAttr == true) flags |= (1 << 1); // Attribute bit
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
|
|||
/// allowing the deserializer to match properties by name between different types.
|
||||
/// Default: false (no overhead)
|
||||
/// </summary>
|
||||
public bool UseMetadata { get; set; } = true;
|
||||
public bool UseMetadata { get; set; } = false;
|
||||
|
||||
public bool UseGeneratedCode { get; set; } = true;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue