diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContext.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContext.cs index 53f7b13..2e48d67 100644 --- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContext.cs +++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContext.cs @@ -33,6 +33,7 @@ public static partial class AcBinaryDeserializer private DupEntry[]? _dupEntries; // Footer: (position, cacheIndex) pairs sorted by position private string[]? _internStringCache; // Cache for duplicated strings only private int _dupCheckIndex; // Current position in _dupEntries + private int _nextDupPosition; // Cached next dup position - avoids array access in hot path /// /// Heap-allocated context class for IId-based reference tracking. @@ -87,6 +88,7 @@ public static partial class AcBinaryDeserializer _dupEntries = null; _internStringCache = null; _dupCheckIndex = 0; + _nextDupPosition = int.MaxValue; HasMetadata = false; IsMergeMode = false; @@ -191,6 +193,7 @@ public static partial class AcBinaryDeserializer { _dupEntries = Array.Empty(); _internStringCache = Array.Empty(); + _nextDupPosition = int.MaxValue; } else { @@ -204,6 +207,8 @@ public static partial class AcBinaryDeserializer // Cache size: dupCount (cacheIndex is always 0, 1, 2, ..., dupCount-1) _internStringCache = new string[dupCount]; + // Cache first dup position for ultra-fast hot path + _nextDupPosition = _dupEntries[0].Position; } // Seek back to data position @@ -560,24 +565,27 @@ public static partial class AcBinaryDeserializer /// /// Registers an interned string during body read (StringInternNew). /// Uses position-based check for 100% reliable cache matching. + /// Ultra-fast: single int comparison in hot path. /// /// The string value read from stream /// Stream position BEFORE reading the string (type code position) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RegisterInternedString(string value, int streamPosition) { - // Fast path: no duplicates or already processed all - var entries = _dupEntries; - if (entries == null || (uint)_dupCheckIndex >= (uint)entries.Length) + // Ultra-fast hot path: single int comparison + if (streamPosition != _nextDupPosition) return; - // Check if this position matches the next expected duplicate - ref var entry = ref entries[_dupCheckIndex]; - if (entry.Position == streamPosition) - { - _internStringCache![entry.CacheIndex] = value; - _dupCheckIndex++; - } + // Match! Store in cache and advance to next dup position + var entries = _dupEntries!; + var idx = _dupCheckIndex; + _internStringCache![entries[idx].CacheIndex] = value; + + idx++; + _dupCheckIndex = idx; + _nextDupPosition = idx < entries.Length + ? entries[idx].Position + : int.MaxValue; } /// @@ -586,12 +594,12 @@ public static partial class AcBinaryDeserializer [MethodImpl(MethodImplOptions.AggressiveInlining)] public string GetInternedString(int cacheIndex) { - if (_internStringCache == null || (uint)cacheIndex >= (uint)_internStringCache.Length) - { - throw new AcBinaryDeserializationException($"Invalid interned string cache index '{cacheIndex}'.", _position); - } + //if (_internStringCache == null || cacheIndex >= _internStringCache.Length) + //{ + // throw new AcBinaryDeserializationException($"Invalid interned string cache index '{cacheIndex}'.", _position); + //} - var result = _internStringCache[cacheIndex]; + var result = _internStringCache![cacheIndex]; if (result == null) { throw new AcBinaryDeserializationException( diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs index bdb8d03..19ed51b 100644 --- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs +++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs @@ -794,23 +794,23 @@ public static partial class AcBinaryDeserializer return str; } - /// - /// Read a string and register it in the intern table for future references. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string ReadAndInternString(ref BinaryDeserializationContext context, int streamPosition) - { - var length = (int)context.ReadVarUInt(); - if (length == 0) return string.Empty; - var str = context.ReadStringUtf8(length); - // Always register strings that meet the minimum intern length threshold - if (str.Length >= context.MinStringInternLength) - { - context.RegisterInternedString(str, streamPosition); - } + ///// + ///// Read a string and register it in the intern table for future references. + ///// + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //private static string ReadAndInternString(ref BinaryDeserializationContext context, int streamPosition) + //{ + // var length = (int)context.ReadVarUInt(); + // if (length == 0) return string.Empty; + // var str = context.ReadStringUtf8(length); + // // Always register strings that meet the minimum intern length threshold + // if (str.Length >= context.MinStringInternLength) + // { + // context.RegisterInternedString(str, streamPosition); + // } - return str; - } + // return str; + //} [MethodImpl(MethodImplOptions.AggressiveInlining)] private static object ReadInt32Value(ref BinaryDeserializationContext context, Type targetType) @@ -1427,23 +1427,23 @@ public static partial class AcBinaryDeserializer context.RegisterInternedString(str, streamPosition); } - /// - /// Skip a string but still register it in the intern table if it meets the length threshold. - /// - /// Deserialization context - /// Position before the type code was read - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void SkipAndInternString(ref BinaryDeserializationContext context, int streamPosition) - { - var byteLen = (int)context.ReadVarUInt(); - if (byteLen == 0) return; + ///// + ///// Skip a string but still register it in the intern table if it meets the length threshold. + ///// + ///// Deserialization context + ///// Position before the type code was read + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //private static void SkipAndInternString(ref BinaryDeserializationContext context, int streamPosition) + //{ + // var byteLen = (int)context.ReadVarUInt(); + // if (byteLen == 0) return; - var str = context.ReadStringUtf8(byteLen); - if (str.Length >= context.MinStringInternLength) - { - context.RegisterInternedString(str, streamPosition); - } - } + // var str = context.ReadStringUtf8(byteLen); + // if (str.Length >= context.MinStringInternLength) + // { + // context.RegisterInternedString(str, streamPosition); + // } + //} private static void SkipObject(ref BinaryDeserializationContext context, BinaryDeserializeTypeMetadata metaData) { diff --git a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs index 78c3bc4..c594a3a 100644 --- a/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs +++ b/AyCode.Core/Serializers/Binaries/AcBinarySerializer.cs @@ -125,7 +125,7 @@ public static partial class AcBinarySerializer // Header sb.AppendLine("+==============================================================================+"); - sb.AppendLine($"| STRING INTERN ANALYSIS REPORT (Mode: {refMode,-12}) |"); + sb.AppendLine($"| STRING INTERN ANALYSIS REPORT (RefMode: {refMode,-12}) |"); sb.AppendLine("+==============================================================================+"); sb.AppendLine();