diff --git a/DiffPlex.Benchmarks/DiffPlex.Benchmarks.csproj b/DiffPlex.Benchmarks/DiffPlex.Benchmarks.csproj new file mode 100644 index 00000000..9e720ff2 --- /dev/null +++ b/DiffPlex.Benchmarks/DiffPlex.Benchmarks.csproj @@ -0,0 +1,18 @@ + + + + + + + + + + + + Exe + net8.0 + enable + enable + + + diff --git a/DiffPlex.Benchmarks/Program.cs b/DiffPlex.Benchmarks/Program.cs new file mode 100644 index 00000000..0d1b4cbd --- /dev/null +++ b/DiffPlex.Benchmarks/Program.cs @@ -0,0 +1,227 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Order; +using BenchmarkDotNet.Running; +using DiffPlex.Chunkers; +using DiffPlex.DiffBuilder; +using DiffPlex.DiffBuilder.Model; +using DiffPlex.Model; + +namespace DiffPlex.Benchmarks; + +internal static class Program +{ + public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); +} + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 3)] +[Orderer(SummaryOrderPolicy.FastestToSlowest)] +public class CoreDiffBenchmarks +{ + private static readonly char[] WordSeparators = { ' ', '\t', '.', '(', ')', '{', '}', ',', '!', '?', ';' }; + + private readonly Differ differ = new(); + private readonly InlineDiffBuilder inlineDiffBuilder; + private readonly SideBySideDiffBuilder sideBySideDiffBuilder; + private BenchmarkInput input = null!; + + public CoreDiffBenchmarks() + { + inlineDiffBuilder = new InlineDiffBuilder(differ); + sideBySideDiffBuilder = new SideBySideDiffBuilder(differ); + } + + [Params(InputScenario.Identical, InputScenario.ManySmallChanges, InputScenario.FewLargeChanges, InputScenario.CompletelyDifferent, InputScenario.LongCommonSubsequence)] + public InputScenario Scenario { get; set; } + + [Params(InputSize.Small, InputSize.Large)] + public InputSize Size { get; set; } + + [GlobalSetup] + public void GlobalSetup() + { + input = BenchmarkInput.Create(Scenario, Size); + } + + [Benchmark(Baseline = true)] + public DiffResult CreateDiffs_LineChunker() + { + return differ.CreateDiffs(input.OldText, input.NewText, ignoreWhiteSpace: false, ignoreCase: false, LineChunker.Instance); + } + + [Benchmark] + public DiffResult CreateLineDiffs() + { + return differ.CreateLineDiffs(input.OldText, input.NewText, ignoreWhitespace: false); + } + + [Benchmark] + public DiffResult CreateWordDiffs() + { + return differ.CreateWordDiffs(input.OldText, input.NewText, ignoreWhitespace: false, WordSeparators); + } + + [Benchmark] + public DiffResult CreateCharacterDiffs() + { + return differ.CreateCharacterDiffs(input.CharacterOldText, input.CharacterNewText, ignoreWhitespace: false); + } + + [Benchmark] + public DiffPaneModel BuildInlineDiffModel() + { + return inlineDiffBuilder.BuildDiffModel(input.OldText, input.NewText, ignoreWhitespace: false, ignoreCase: false, LineChunker.Instance); + } + + [Benchmark] + public SideBySideDiffModel BuildSideBySideDiffModel() + { + return sideBySideDiffBuilder.BuildDiffModel(input.OldText, input.NewText, ignoreWhitespace: false); + } +} + +public enum InputScenario +{ + Identical, + ManySmallChanges, + FewLargeChanges, + CompletelyDifferent, + LongCommonSubsequence +} + +public enum InputSize +{ + Small, + Large +} + +internal sealed record BenchmarkInput(string OldText, string NewText, string CharacterOldText, string CharacterNewText) +{ + public static BenchmarkInput Create(InputScenario scenario, InputSize size) + { + int lineCount = size == InputSize.Small ? 25 : 1_500; + int characterCount = size == InputSize.Small ? 250 : 1_000; + + var oldLines = CreateLines(lineCount, "old"); + var newLines = scenario switch + { + InputScenario.Identical => oldLines.ToArray(), + InputScenario.ManySmallChanges => ManySmallChanges(oldLines), + InputScenario.FewLargeChanges => FewLargeChanges(oldLines), + InputScenario.CompletelyDifferent => CreateLines(lineCount, "new"), + InputScenario.LongCommonSubsequence => LongCommonSubsequence(oldLines), + _ => throw new ArgumentOutOfRangeException(nameof(scenario), scenario, null) + }; + + string oldText = string.Join('\n', oldLines); + string newText = string.Join('\n', newLines); + + string characterOldText = CreateCharacterText(characterCount, "abcdefghij"); + string characterNewText = scenario switch + { + InputScenario.Identical => characterOldText, + InputScenario.ManySmallChanges => ReplaceEvery(characterOldText, every: 19, 'Z'), + InputScenario.FewLargeChanges => ReplaceRange(characterOldText, characterCount / 3, characterCount / 5, 'X'), + InputScenario.CompletelyDifferent => CreateCharacterText(characterCount, "ZYXWVUTSRQ"), + // Keep the character variant bounded while still preserving a long common subsequence around an insertion. + InputScenario.LongCommonSubsequence => characterOldText.Insert(characterCount / 2, CreateCharacterText(characterCount / 10, "lcs")), + _ => throw new ArgumentOutOfRangeException(nameof(scenario), scenario, null) + }; + + return new BenchmarkInput(oldText, newText, characterOldText, characterNewText); + } + + private static string[] CreateLines(int lineCount, string prefix) + { + var lines = new string[lineCount]; + for (int i = 0; i < lines.Length; i++) + { + lines[i] = $"{prefix} line {i:D5}: common-token-{i % 17} value-{(i * 31) % 997} words for diffing"; + } + + return lines; + } + + private static string[] ManySmallChanges(string[] oldLines) + { + var newLines = oldLines.ToArray(); + for (int i = 4; i < newLines.Length; i += 10) + { + newLines[i] = newLines[i] + " changed"; + } + + return newLines; + } + + private static string[] FewLargeChanges(string[] oldLines) + { + var newLines = oldLines.ToArray(); + int blockLength = Math.Max(3, oldLines.Length / 5); + int start = Math.Max(0, (oldLines.Length - blockLength) / 2); + + for (int i = 0; i < blockLength; i++) + { + newLines[start + i] = $"replacement block line {i:D5}: updated content with different tokens {i % 11}"; + } + + return newLines; + } + + private static string[] LongCommonSubsequence(string[] oldLines) + { + var newLines = new List(oldLines.Length + Math.Max(1, oldLines.Length / 20)); + for (int i = 0; i < oldLines.Length; i++) + { + if (i % 50 == 0) + { + newLines.Add($"inserted anchor line {i:D5}: preserves a long common subsequence around edits"); + } + + if (i % 75 != 0) + { + newLines.Add(oldLines[i]); + } + } + + return newLines.ToArray(); + } + + private static string CreateCharacterText(int characterCount, string pattern) + { + if (characterCount <= 0) + { + return string.Empty; + } + + var builder = new System.Text.StringBuilder(characterCount); + while (builder.Length < characterCount) + { + builder.Append(pattern); + } + + return builder.ToString(0, characterCount); + } + + private static string ReplaceEvery(string text, int every, char replacement) + { + var chars = text.ToCharArray(); + for (int i = every - 1; i < chars.Length; i += every) + { + chars[i] = replacement; + } + + return new string(chars); + } + + private static string ReplaceRange(string text, int start, int length, char replacement) + { + var chars = text.ToCharArray(); + for (int i = start; i < Math.Min(chars.Length, start + length); i++) + { + chars[i] = replacement; + } + + return new string(chars); + } +} diff --git a/DiffPlex.Benchmarks/README.md b/DiffPlex.Benchmarks/README.md new file mode 100644 index 00000000..a304f793 --- /dev/null +++ b/DiffPlex.Benchmarks/README.md @@ -0,0 +1,18 @@ +# DiffPlex benchmarks + +This project contains BenchmarkDotNet benchmarks for the core DiffPlex diffing APIs and builders. The scenarios cover identical inputs, many small edits, a few large edits, completely different inputs, and inputs with long common subsequences at small and large sizes. + +Run the full suite from this directory: + +```console +dotnet run -c Release +``` + +Useful shorter commands while iterating: + +```console +dotnet run -c Release -- --filter "*CreateLineDiffs*" +dotnet run -c Release -- --filter "*Large*" --exporters github csv +``` + +BenchmarkDotNet writes detailed reports under `BenchmarkDotNet.Artifacts/results` by default. Committed before/after summaries for this optimization effort are tracked in `../benchmarks/RESULTS.md`. diff --git a/DiffPlex.sln b/DiffPlex.sln index b9b251b4..53d0a75f 100644 --- a/DiffPlex.sln +++ b/DiffPlex.sln @@ -42,6 +42,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiffPlex.App", "DiffPlex.Ap EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiffPlex.Blazor", "DiffPlex.Blazor\DiffPlex.Blazor.csproj", "{B1234567-89AB-CDEF-0123-456789ABCDEF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiffPlex.Benchmarks", "DiffPlex.Benchmarks\DiffPlex.Benchmarks.csproj", "{65C36D68-4F3F-43E9-A98A-C37F9634358B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -196,6 +198,22 @@ Global {B1234567-89AB-CDEF-0123-456789ABCDEF}.Release|arm64.ActiveCfg = Release|Any CPU {B1234567-89AB-CDEF-0123-456789ABCDEF}.Release|x64.ActiveCfg = Release|Any CPU {B1234567-89AB-CDEF-0123-456789ABCDEF}.Release|x86.ActiveCfg = Release|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Debug|arm64.ActiveCfg = Debug|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Debug|arm64.Build.0 = Debug|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Debug|x64.ActiveCfg = Debug|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Debug|x64.Build.0 = Debug|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Debug|x86.ActiveCfg = Debug|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Debug|x86.Build.0 = Debug|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Release|Any CPU.Build.0 = Release|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Release|arm64.ActiveCfg = Release|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Release|arm64.Build.0 = Release|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Release|x64.ActiveCfg = Release|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Release|x64.Build.0 = Release|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Release|x86.ActiveCfg = Release|Any CPU + {65C36D68-4F3F-43E9-A98A-C37F9634358B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/DiffPlex/DiffBuilder/InlineDiffBuilder.cs b/DiffPlex/DiffBuilder/InlineDiffBuilder.cs index 730341c5..144e6455 100644 --- a/DiffPlex/DiffBuilder/InlineDiffBuilder.cs +++ b/DiffPlex/DiffBuilder/InlineDiffBuilder.cs @@ -25,8 +25,7 @@ public DiffPaneModel BuildDiffModel(string oldText, string newText) public DiffPaneModel BuildDiffModel(string oldText, string newText, bool ignoreWhitespace) { - var chunker = new LineChunker(); - return BuildDiffModel(oldText, newText, ignoreWhitespace, false, chunker); + return BuildDiffModel(oldText, newText, ignoreWhitespace, false, LineChunker.Instance); } public DiffPaneModel BuildDiffModel(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase, IChunker chunker) diff --git a/DiffPlex/DiffBuilder/Model/DiffPaneModel.cs b/DiffPlex/DiffBuilder/Model/DiffPaneModel.cs index f7a19831..bc4e86f5 100644 --- a/DiffPlex/DiffBuilder/Model/DiffPaneModel.cs +++ b/DiffPlex/DiffBuilder/Model/DiffPaneModel.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; namespace DiffPlex.DiffBuilder.Model { @@ -9,7 +8,18 @@ public class DiffPaneModel public bool HasDifferences { - get { return Lines.Any(x => x.Type != ChangeType.Unchanged); } + get + { + for (int i = 0; i < Lines.Count; i++) + { + if (Lines[i].Type != ChangeType.Unchanged) + { + return true; + } + } + + return false; + } } public DiffPaneModel() diff --git a/DiffPlex/DiffBuilder/SideBySideDiffBuilder.cs b/DiffPlex/DiffBuilder/SideBySideDiffBuilder.cs index 251d04e5..d43951e1 100644 --- a/DiffPlex/DiffBuilder/SideBySideDiffBuilder.cs +++ b/DiffPlex/DiffBuilder/SideBySideDiffBuilder.cs @@ -1,6 +1,5 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; using DiffPlex.Chunkers; using DiffPlex.DiffBuilder.Model; using DiffPlex.Model; @@ -127,6 +126,18 @@ private ChangeType BuildWordDiffPieces(string oldText, string newText, List oldPieces, List newPieces, PieceBuilder subPieceBuilder, bool ignoreWhiteSpace, bool ignoreCase) { + int capacity = Math.Max(diffResult.PiecesOld.Count, diffResult.PiecesNew.Count); + if (oldPieces.Capacity < capacity) + { + oldPieces.Capacity = capacity; + } + + if (newPieces.Capacity < capacity) + { + newPieces.Capacity = capacity; + } + + bool hasChanges = false; int aPos = 0; int bPos = 0; @@ -152,6 +163,8 @@ private static ChangeType BuildDiffPieces(DiffResult diffResult, List newPiece.Type = oldPiece.Type = subChangeSummary; } + hasChanges |= oldPiece.Type is ChangeType.Modified or ChangeType.Inserted or ChangeType.Deleted + || newPiece.Type is ChangeType.Modified or ChangeType.Inserted or ChangeType.Deleted; oldPieces.Add(oldPiece); newPieces.Add(newPiece); aPos++; @@ -164,6 +177,7 @@ private static ChangeType BuildDiffPieces(DiffResult diffResult, List { oldPieces.Add(new DiffPiece(diffResult.PiecesOld[i + diffBlock.DeleteStartA], ChangeType.Deleted, aPos + 1)); newPieces.Add(new DiffPiece()); + hasChanges = true; aPos++; } } @@ -173,6 +187,7 @@ private static ChangeType BuildDiffPieces(DiffResult diffResult, List { newPieces.Add(new DiffPiece(diffResult.PiecesNew[i + diffBlock.InsertStartB], ChangeType.Inserted, bPos + 1)); oldPieces.Add(new DiffPiece()); + hasChanges = true; bPos++; } } @@ -187,17 +202,7 @@ private static ChangeType BuildDiffPieces(DiffResult diffResult, List } // Consider the whole diff as "modified" if we found any change, otherwise we consider it unchanged - if(oldPieces.Any(x => x.Type is ChangeType.Modified or ChangeType.Inserted or ChangeType.Deleted)) - { - return ChangeType.Modified; - } - - if (newPieces.Any(x => x.Type is ChangeType.Modified or ChangeType.Inserted or ChangeType.Deleted)) - { - return ChangeType.Modified; - } - - return ChangeType.Unchanged; + return hasChanges ? ChangeType.Modified : ChangeType.Unchanged; } } } \ No newline at end of file diff --git a/DiffPlex/Differ.cs b/DiffPlex/Differ.cs index 281ee96c..d47a6eff 100644 --- a/DiffPlex/Differ.cs +++ b/DiffPlex/Differ.cs @@ -14,22 +14,22 @@ public class Differ : IDiffer public DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace) { - return CreateDiffs(oldText, newText, ignoreWhitespace, false, new LineChunker()); + return CreateDiffs(oldText, newText, ignoreWhitespace, false, LineChunker.Instance); } public DiffResult CreateLineDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase) { - return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, new LineChunker()); + return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, LineChunker.Instance); } public DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace) { - return CreateDiffs(oldText, newText, ignoreWhitespace, false, new CharacterChunker()); + return CreateDiffs(oldText, newText, ignoreWhitespace, false, CharacterChunker.Instance); } public DiffResult CreateCharacterDiffs(string oldText, string newText, bool ignoreWhitespace, bool ignoreCase) { - return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, new CharacterChunker()); + return CreateDiffs(oldText, newText, ignoreWhitespace, ignoreCase, CharacterChunker.Instance); } public DiffResult CreateWordDiffs(string oldText, string newText, bool ignoreWhitespace, char[] separators) diff --git a/benchmarks/RESULTS.md b/benchmarks/RESULTS.md new file mode 100644 index 00000000..62b6d03d --- /dev/null +++ b/benchmarks/RESULTS.md @@ -0,0 +1,216 @@ +# DiffPlex benchmark results + +Benchmark runs use the `DiffPlex.Benchmarks` project: + +```console +dotnet run -c Release --project DiffPlex.Benchmarks -- --filter "*" --exporters github csv +``` + +The harness uses BenchmarkDotNet with `MemoryDiagnoser`, one launch, one warmup iteration, and three measured iterations on .NET 8. Results below were captured on the GitHub-hosted coding-agent runner for this PR, so they are intended for relative before/after comparisons rather than absolute machine-independent numbers. + +## Baseline + +Captured before library optimizations in this PR. + +``` + +BenchmarkDotNet v0.13.12, Ubuntu 24.04.4 LTS (Noble Numbat) +AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores +.NET SDK 9.0.317 + [Host] : .NET 8.0.30 (8.0.3026.36720), X64 RyuJIT AVX2 + Job-ZSKIMK : .NET 8.0.30 (8.0.3026.36720), X64 RyuJIT AVX2 + +Runtime=.NET 8.0 IterationCount=3 LaunchCount=1 +WarmupCount=1 + +``` +| Method | Scenario | Size | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | +|------------------------- |--------------------- |------ |-------------:|-------------:|-----------:|------:|--------:|---------:|---------:|---------:|------------:|------------:| +| CreateLineDiffs | Identical | Small | 13.62 μs | 3.023 μs | 0.166 μs | 1.00 | 0.01 | 0.6409 | 0.0153 | - | 10.48 KB | 1.01 | +| CreateDiffs_LineChunker | Identical | Small | 13.67 μs | 2.448 μs | 0.134 μs | 1.00 | 0.00 | 0.6256 | 0.0153 | - | 10.41 KB | 1.00 | +| BuildInlineDiffModel | Identical | Small | 14.19 μs | 0.803 μs | 0.044 μs | 1.04 | 0.01 | 0.7935 | 0.0305 | - | 12.98 KB | 1.25 | +| CreateCharacterDiffs | Identical | Small | 14.39 μs | 1.013 μs | 0.056 μs | 1.05 | 0.01 | 1.4191 | 0.0916 | - | 23.34 KB | 2.24 | +| BuildSideBySideDiffModel | Identical | Small | 14.42 μs | 1.535 μs | 0.084 μs | 1.05 | 0.01 | 0.9613 | 0.0458 | - | 15.73 KB | 1.51 | +| CreateWordDiffs | Identical | Small | 39.12 μs | 8.579 μs | 0.470 μs | 2.86 | 0.03 | 3.1738 | 0.3662 | - | 52.08 KB | 5.00 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | Identical | Large | 55.88 μs | 9.384 μs | 0.514 μs | 0.07 | 0.00 | 5.4321 | 1.0376 | - | 89.25 KB | 0.15 | +| CreateLineDiffs | Identical | Large | 768.69 μs | 55.490 μs | 3.042 μs | 0.94 | 0.00 | 35.1563 | 24.4141 | - | 582.18 KB | 1.00 | +| BuildInlineDiffModel | Identical | Large | 811.80 μs | 41.993 μs | 2.302 μs | 0.99 | 0.01 | 43.9453 | 28.3203 | - | 731.56 KB | 1.26 | +| CreateDiffs_LineChunker | Identical | Large | 821.73 μs | 131.944 μs | 7.232 μs | 1.00 | 0.00 | 35.1563 | 22.4609 | - | 582.11 KB | 1.00 | +| BuildSideBySideDiffModel | Identical | Large | 874.99 μs | 133.118 μs | 7.297 μs | 1.06 | 0.00 | 53.7109 | 36.1328 | - | 881.17 KB | 1.51 | +| CreateWordDiffs | Identical | Large | 2,980.90 μs | 394.108 μs | 21.602 μs | 3.63 | 0.01 | 371.0938 | 371.0938 | 371.0938 | 3081.63 KB | 5.29 | +| | | | | | | | | | | | | | +| CreateDiffs_LineChunker | ManySmallChanges | Small | 13.11 μs | 2.671 μs | 0.146 μs | 1.00 | 0.00 | 0.6561 | 0.0153 | - | 10.77 KB | 1.00 | +| BuildInlineDiffModel | ManySmallChanges | Small | 13.92 μs | 4.420 μs | 0.242 μs | 1.06 | 0.01 | 0.8240 | 0.0305 | - | 13.61 KB | 1.26 | +| CreateLineDiffs | ManySmallChanges | Small | 14.02 μs | 2.769 μs | 0.152 μs | 1.07 | 0.01 | 0.6561 | 0.0153 | - | 10.84 KB | 1.01 | +| CreateCharacterDiffs | ManySmallChanges | Small | 17.99 μs | 1.444 μs | 0.079 μs | 1.37 | 0.01 | 1.4954 | 0.0916 | - | 24.7 KB | 2.29 | +| BuildSideBySideDiffModel | ManySmallChanges | Small | 25.92 μs | 2.840 μs | 0.156 μs | 1.98 | 0.01 | 2.3499 | 0.2136 | - | 38.86 KB | 3.61 | +| CreateWordDiffs | ManySmallChanges | Small | 40.82 μs | 6.619 μs | 0.363 μs | 3.11 | 0.06 | 3.1738 | 0.3662 | - | 52.72 KB | 4.90 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | ManySmallChanges | Large | 88.80 μs | 16.386 μs | 0.898 μs | 0.09 | 0.00 | 5.7373 | 1.3428 | - | 94.62 KB | 0.16 | +| CreateDiffs_LineChunker | ManySmallChanges | Large | 974.73 μs | 184.194 μs | 10.096 μs | 1.00 | 0.00 | 35.1563 | 25.3906 | - | 600.35 KB | 1.00 | +| CreateLineDiffs | ManySmallChanges | Large | 1,034.34 μs | 295.396 μs | 16.192 μs | 1.06 | 0.01 | 35.1563 | 25.3906 | - | 600.42 KB | 1.00 | +| BuildInlineDiffModel | ManySmallChanges | Large | 1,080.94 μs | 239.085 μs | 13.105 μs | 1.11 | 0.00 | 44.9219 | 29.2969 | - | 761.55 KB | 1.27 | +| BuildSideBySideDiffModel | ManySmallChanges | Large | 1,939.46 μs | 240.490 μs | 13.182 μs | 1.99 | 0.01 | 123.0469 | 111.3281 | - | 2038.47 KB | 3.40 | +| CreateWordDiffs | ManySmallChanges | Large | 4,079.09 μs | 497.673 μs | 27.279 μs | 4.18 | 0.02 | 398.4375 | 398.4375 | 398.4375 | 3119.17 KB | 5.20 | +| | | | | | | | | | | | | | +| CreateDiffs_LineChunker | FewLargeChanges | Small | 12.97 μs | 0.893 μs | 0.049 μs | 1.00 | 0.00 | 0.6409 | 0.0153 | - | 10.62 KB | 1.00 | +| BuildInlineDiffModel | FewLargeChanges | Small | 13.90 μs | 1.315 μs | 0.072 μs | 1.07 | 0.00 | 0.8240 | 0.0305 | - | 13.62 KB | 1.28 | +| CreateLineDiffs | FewLargeChanges | Small | 16.83 μs | 3.251 μs | 0.178 μs | 1.30 | 0.01 | 0.6409 | - | - | 10.69 KB | 1.01 | +| CreateCharacterDiffs | FewLargeChanges | Small | 23.14 μs | 4.491 μs | 0.246 μs | 1.78 | 0.01 | 1.4343 | 0.0916 | - | 23.47 KB | 2.21 | +| BuildSideBySideDiffModel | FewLargeChanges | Small | 39.35 μs | 5.839 μs | 0.320 μs | 3.03 | 0.01 | 3.9063 | 0.4272 | - | 63.86 KB | 6.01 | +| CreateWordDiffs | FewLargeChanges | Small | 57.58 μs | 4.386 μs | 0.240 μs | 4.44 | 0.00 | 3.4180 | 0.4272 | - | 56.72 KB | 5.34 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | FewLargeChanges | Large | 192.87 μs | 24.754 μs | 1.357 μs | 0.17 | 0.00 | 5.3711 | 1.2207 | - | 89.38 KB | 0.15 | +| CreateLineDiffs | FewLargeChanges | Large | 1,134.74 μs | 201.686 μs | 11.055 μs | 1.00 | 0.01 | 35.1563 | 23.4375 | - | 587.34 KB | 1.00 | +| BuildInlineDiffModel | FewLargeChanges | Large | 1,135.70 μs | 335.185 μs | 18.373 μs | 1.00 | 0.01 | 44.9219 | 42.9688 | - | 760.19 KB | 1.29 | +| CreateDiffs_LineChunker | FewLargeChanges | Large | 1,136.63 μs | 202.828 μs | 11.118 μs | 1.00 | 0.00 | 35.1563 | 21.4844 | - | 587.27 KB | 1.00 | +| BuildSideBySideDiffModel | FewLargeChanges | Large | 2,986.63 μs | 248.855 μs | 13.641 μs | 2.63 | 0.01 | 226.5625 | 203.1250 | - | 3762.32 KB | 6.41 | +| CreateWordDiffs | FewLargeChanges | Large | 46,639.30 μs | 7,007.730 μs | 384.117 μs | 41.03 | 0.27 | 363.6364 | 363.6364 | 363.6364 | 3328.73 KB | 5.67 | +| | | | | | | | | | | | | | +| CreateDiffs_LineChunker | CompletelyDifferent | Small | 15.31 μs | 1.913 μs | 0.105 μs | 1.00 | 0.00 | 0.7935 | 0.0305 | - | 13.02 KB | 1.00 | +| CreateLineDiffs | CompletelyDifferent | Small | 15.65 μs | 5.080 μs | 0.278 μs | 1.02 | 0.02 | 0.7935 | 0.0305 | - | 13.09 KB | 1.01 | +| BuildInlineDiffModel | CompletelyDifferent | Small | 17.71 μs | 2.514 μs | 0.138 μs | 1.16 | 0.00 | 1.0986 | 0.0305 | - | 18.11 KB | 1.39 | +| CreateWordDiffs | CompletelyDifferent | Small | 48.12 μs | 7.304 μs | 0.400 μs | 3.14 | 0.00 | 3.2959 | 0.4272 | - | 54.71 KB | 4.20 | +| BuildSideBySideDiffModel | CompletelyDifferent | Small | 98.67 μs | 33.563 μs | 1.840 μs | 6.45 | 0.12 | 10.8643 | 3.0518 | - | 177.52 KB | 13.63 | +| CreateCharacterDiffs | CompletelyDifferent | Small | 224.24 μs | 109.025 μs | 5.976 μs | 14.65 | 0.49 | 1.4648 | - | - | 24.53 KB | 1.88 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | CompletelyDifferent | Large | 3,216.54 μs | 344.145 μs | 18.864 μs | 0.40 | 0.00 | 3.9063 | - | - | 90.44 KB | 0.13 | +| CreateDiffs_LineChunker | CompletelyDifferent | Large | 8,070.43 μs | 1,648.049 μs | 90.335 μs | 1.00 | 0.00 | 46.8750 | 31.2500 | 15.6250 | 693.03 KB | 1.00 | +| BuildInlineDiffModel | CompletelyDifferent | Large | 8,223.16 μs | 1,241.614 μs | 68.057 μs | 1.02 | 0.00 | 46.8750 | 31.2500 | 15.6250 | 991.73 KB | 1.43 | +| CreateLineDiffs | CompletelyDifferent | Large | 8,276.27 μs | 2,924.963 μs | 160.327 μs | 1.03 | 0.02 | 46.8750 | 31.2500 | 15.6250 | 693.1 KB | 1.00 | +| BuildSideBySideDiffModel | CompletelyDifferent | Large | 17,702.56 μs | 1,681.268 μs | 92.156 μs | 2.19 | 0.02 | 625.0000 | 500.0000 | 156.2500 | 10543.03 KB | 15.21 | +| CreateWordDiffs | CompletelyDifferent | Large | 19,389.63 μs | 1,586.972 μs | 86.987 μs | 2.40 | 0.02 | 343.7500 | 343.7500 | 343.7500 | 3237.9 KB | 4.67 | +| | | | | | | | | | | | | | +| CreateLineDiffs | LongC(...)uence [21] | Small | 13.20 μs | 1.397 μs | 0.077 μs | 0.78 | 0.01 | 0.6409 | 0.0153 | - | 10.65 KB | 1.01 | +| BuildInlineDiffModel | LongC(...)uence [21] | Small | 14.69 μs | 2.380 μs | 0.130 μs | 0.87 | 0.01 | 0.8087 | 0.0305 | - | 13.27 KB | 1.25 | +| CreateCharacterDiffs | LongC(...)uence [21] | Small | 16.86 μs | 2.796 μs | 0.153 μs | 1.00 | 0.00 | 1.4954 | 0.0916 | - | 24.54 KB | 2.32 | +| CreateDiffs_LineChunker | LongC(...)uence [21] | Small | 16.90 μs | 3.875 μs | 0.212 μs | 1.00 | 0.00 | 0.6409 | - | - | 10.58 KB | 1.00 | +| BuildSideBySideDiffModel | LongC(...)uence [21] | Small | 20.58 μs | 2.823 μs | 0.155 μs | 1.22 | 0.01 | 1.5564 | 0.0916 | - | 25.8 KB | 2.44 | +| CreateWordDiffs | LongC(...)uence [21] | Small | 42.93 μs | 6.288 μs | 0.345 μs | 2.54 | 0.02 | 3.2349 | 0.3662 | - | 52.93 KB | 5.00 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | LongC(...)uence [21] | Large | 64.77 μs | 16.499 μs | 0.904 μs | 0.07 | 0.00 | 5.7373 | 1.3428 | - | 93.73 KB | 0.16 | +| CreateLineDiffs | LongC(...)uence [21] | Large | 806.75 μs | 221.576 μs | 12.145 μs | 0.93 | 0.01 | 35.1563 | 26.3672 | - | 588.32 KB | 1.00 | +| BuildInlineDiffModel | LongC(...)uence [21] | Large | 866.57 μs | 93.162 μs | 5.107 μs | 1.00 | 0.01 | 44.9219 | 33.2031 | - | 740.08 KB | 1.26 | +| CreateDiffs_LineChunker | LongC(...)uence [21] | Large | 870.23 μs | 172.279 μs | 9.443 μs | 1.00 | 0.00 | 35.1563 | 25.3906 | - | 588.25 KB | 1.00 | +| BuildSideBySideDiffModel | LongC(...)uence [21] | Large | 995.12 μs | 218.820 μs | 11.994 μs | 1.14 | 0.00 | 60.5469 | 29.2969 | - | 989.58 KB | 1.68 | +| CreateWordDiffs | LongC(...)uence [21] | Large | 4,967.33 μs | 386.605 μs | 21.191 μs | 5.71 | 0.05 | 398.4375 | 398.4375 | 398.4375 | 3110.18 KB | 5.29 | + +## Before/after summary + +Positive percentages below indicate improvement. The largest retained gains are in side-by-side builder allocation usage, from pre-sizing output lists and avoiding a final LINQ rescan for the change summary. Some unchanged core/word-diff benchmarks moved in both directions between runs, so those timing-only differences are treated as benchmark noise rather than optimization impact. + +| Method | Scenario | Size | Baseline Mean | Final Mean | Time Change | Baseline Allocated | Final Allocated | Allocation Change | +|---|---|---:|---:|---:|---:|---:|---:|---:| +| CreateLineDiffs | Identical | Small | 13.62 μs | 13.59 μs | 0.2% | 10.48 KB | 10.41 KB | 0.7% | +| CreateCharacterDiffs | Identical | Small | 14.39 μs | 14.60 μs | -1.5% | 23.34 KB | 23.32 KB | 0.1% | +| BuildSideBySideDiffModel | Identical | Large | 874.99 μs | 869.97 μs | 0.6% | 881.17 KB | 840.17 KB | 4.7% | +| BuildSideBySideDiffModel | ManySmallChanges | Small | 25.92 μs | 25.55 μs | 1.4% | 38.86 KB | 35.46 KB | 8.7% | +| BuildSideBySideDiffModel | ManySmallChanges | Large | 1,939.46 μs | 1,712.49 μs | 11.7% | 2038.47 KB | 1863.92 KB | 8.6% | +| BuildSideBySideDiffModel | FewLargeChanges | Large | 2,986.63 μs | 2,878.48 μs | 3.6% | 3762.32 KB | 3475.27 KB | 7.6% | +| BuildSideBySideDiffModel | CompletelyDifferent | Large | 17,702.56 μs | 16,772.30 μs | 5.3% | 10543.03 KB | 9998.19 KB | 5.2% | +| BuildSideBySideDiffModel | Long common subsequence | Large | 995.12 μs | 1,023.03 μs | -2.8% | 989.58 KB | 988.12 KB | 0.1% | +| CreateWordDiffs | ManySmallChanges | Large | 4,079.09 μs | 4,149.08 μs | -1.7% | 3119.17 KB | 3119.17 KB | 0.0% | + +## Optimization notes and ROI conclusion + +Retained changes: + +- `Differ.CreateLineDiffs` and `Differ.CreateCharacterDiffs` now reuse stateless chunker singleton instances, removing one small per-call allocation while preserving the public API and output behavior. +- `SideBySideDiffBuilder` pre-sizes output line lists and tracks whether changes were emitted while building pieces, avoiding a post-build LINQ rescan. +- `DiffPaneModel.HasDifferences` now uses a simple indexed loop instead of LINQ. + +Measured but not retained: + +- Replacing delimiter `Array.IndexOf` with a `HashSet` increased `CreateWordDiffs` allocation and did not provide consistent timing wins for the default small delimiter set. +- Pre-sizing inline builder output reduced a small amount of allocation but added an extra pass over diff blocks and produced mixed timing results. + +Further likely improvements would require higher-risk algorithmic or data-structure changes in the Myers/LCS implementation or chunk representation. Given the mixed timing noise and the clear-but-modest allocation wins from the retained low-risk changes, this pass stops here as the next changes have low expected ROI relative to compatibility and behavior risk. + +## Final + +Captured after the retained optimization pass in this PR. + +``` + +BenchmarkDotNet v0.13.12, Ubuntu 24.04.4 LTS (Noble Numbat) +AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores +.NET SDK 9.0.317 + [Host] : .NET 8.0.30 (8.0.3026.36720), X64 RyuJIT AVX2 + Job-ZBOZAN : .NET 8.0.30 (8.0.3026.36720), X64 RyuJIT AVX2 + +Runtime=.NET 8.0 IterationCount=3 LaunchCount=1 +WarmupCount=1 + +``` +| Method | Scenario | Size | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | +|------------------------- |--------------------- |------ |-------------:|-------------:|-----------:|------:|--------:|---------:|---------:|---------:|-----------:|------------:| +| CreateLineDiffs | Identical | Small | 13.59 μs | 1.376 μs | 0.075 μs | 0.46 | 0.13 | 0.6256 | 0.0153 | - | 10.41 KB | 1.00 | +| BuildInlineDiffModel | Identical | Small | 13.71 μs | 6.050 μs | 0.332 μs | 0.46 | 0.13 | 0.7935 | 0.0305 | - | 12.98 KB | 1.25 | +| CreateCharacterDiffs | Identical | Small | 14.60 μs | 6.931 μs | 0.380 μs | 0.49 | 0.13 | 1.4191 | 0.0916 | - | 23.32 KB | 2.24 | +| BuildSideBySideDiffModel | Identical | Small | 15.29 μs | 2.122 μs | 0.116 μs | 0.52 | 0.14 | 0.9155 | 0.0305 | - | 14.96 KB | 1.44 | +| CreateDiffs_LineChunker | Identical | Small | 31.37 μs | 186.786 μs | 10.238 μs | 1.00 | 0.00 | 0.6104 | - | - | 10.41 KB | 1.00 | +| CreateWordDiffs | Identical | Small | 40.11 μs | 16.704 μs | 0.916 μs | 1.36 | 0.38 | 3.1738 | 0.3662 | - | 52.08 KB | 5.00 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | Identical | Large | 58.36 μs | 1.954 μs | 0.107 μs | 0.07 | 0.00 | 5.4321 | 1.0376 | - | 89.23 KB | 0.15 | +| CreateDiffs_LineChunker | Identical | Large | 783.15 μs | 113.850 μs | 6.241 μs | 1.00 | 0.00 | 35.1563 | 22.4609 | - | 582.11 KB | 1.00 | +| CreateLineDiffs | Identical | Large | 840.63 μs | 172.137 μs | 9.435 μs | 1.07 | 0.00 | 35.1563 | 22.4609 | - | 582.11 KB | 1.00 | +| BuildInlineDiffModel | Identical | Large | 841.13 μs | 44.147 μs | 2.420 μs | 1.07 | 0.01 | 43.9453 | 28.3203 | - | 731.56 KB | 1.26 | +| BuildSideBySideDiffModel | Identical | Large | 869.97 μs | 83.672 μs | 4.586 μs | 1.11 | 0.01 | 50.7813 | 34.1797 | - | 840.17 KB | 1.44 | +| CreateWordDiffs | Identical | Large | 2,997.95 μs | 566.837 μs | 31.070 μs | 3.83 | 0.06 | 371.0938 | 371.0938 | 371.0938 | 3081.63 KB | 5.29 | +| | | | | | | | | | | | | | +| CreateDiffs_LineChunker | ManySmallChanges | Small | 13.07 μs | 1.536 μs | 0.084 μs | 1.00 | 0.00 | 0.6561 | 0.0153 | - | 10.77 KB | 1.00 | +| BuildInlineDiffModel | ManySmallChanges | Small | 14.05 μs | 2.628 μs | 0.144 μs | 1.07 | 0.01 | 0.8240 | 0.0305 | - | 13.61 KB | 1.26 | +| CreateLineDiffs | ManySmallChanges | Small | 14.25 μs | 1.418 μs | 0.078 μs | 1.09 | 0.00 | 0.6561 | 0.0153 | - | 10.77 KB | 1.00 | +| CreateCharacterDiffs | ManySmallChanges | Small | 18.05 μs | 6.905 μs | 0.378 μs | 1.38 | 0.02 | 1.4954 | 0.0916 | - | 24.68 KB | 2.29 | +| BuildSideBySideDiffModel | ManySmallChanges | Small | 25.55 μs | 18.594 μs | 1.019 μs | 1.95 | 0.09 | 2.1667 | 0.1831 | - | 35.46 KB | 3.29 | +| CreateWordDiffs | ManySmallChanges | Small | 43.60 μs | 11.351 μs | 0.622 μs | 3.33 | 0.03 | 3.1738 | 0.3662 | - | 52.72 KB | 4.90 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | ManySmallChanges | Large | 90.79 μs | 17.780 μs | 0.975 μs | 0.09 | 0.00 | 5.7373 | 1.2207 | - | 94.59 KB | 0.16 | +| CreateLineDiffs | ManySmallChanges | Large | 979.16 μs | 130.092 μs | 7.131 μs | 0.94 | 0.01 | 35.1563 | 25.3906 | - | 600.35 KB | 1.00 | +| CreateDiffs_LineChunker | ManySmallChanges | Large | 1,041.50 μs | 267.067 μs | 14.639 μs | 1.00 | 0.00 | 35.1563 | 25.3906 | - | 600.35 KB | 1.00 | +| BuildInlineDiffModel | ManySmallChanges | Large | 1,111.84 μs | 289.028 μs | 15.843 μs | 1.07 | 0.00 | 44.9219 | 29.2969 | - | 761.55 KB | 1.27 | +| BuildSideBySideDiffModel | ManySmallChanges | Large | 1,712.49 μs | 425.442 μs | 23.320 μs | 1.64 | 0.03 | 113.2813 | 93.7500 | - | 1863.92 KB | 3.10 | +| CreateWordDiffs | ManySmallChanges | Large | 4,149.08 μs | 330.977 μs | 18.142 μs | 3.98 | 0.05 | 398.4375 | 398.4375 | 398.4375 | 3119.17 KB | 5.20 | +| | | | | | | | | | | | | | +| CreateLineDiffs | FewLargeChanges | Small | 13.16 μs | 0.866 μs | 0.047 μs | 0.94 | 0.01 | 0.6409 | 0.0153 | - | 10.62 KB | 1.00 | +| CreateDiffs_LineChunker | FewLargeChanges | Small | 13.98 μs | 1.465 μs | 0.080 μs | 1.00 | 0.00 | 0.6409 | 0.0153 | - | 10.62 KB | 1.00 | +| BuildInlineDiffModel | FewLargeChanges | Small | 17.79 μs | 4.537 μs | 0.249 μs | 1.27 | 0.01 | 0.8240 | 0.0305 | - | 13.62 KB | 1.28 | +| CreateCharacterDiffs | FewLargeChanges | Small | 23.05 μs | 4.014 μs | 0.220 μs | 1.65 | 0.01 | 1.4343 | 0.0916 | - | 23.45 KB | 2.21 | +| BuildSideBySideDiffModel | FewLargeChanges | Small | 38.88 μs | 14.848 μs | 0.814 μs | 2.78 | 0.04 | 3.6011 | 0.4272 | - | 59.03 KB | 5.56 | +| CreateWordDiffs | FewLargeChanges | Small | 58.26 μs | 8.588 μs | 0.471 μs | 4.17 | 0.01 | 3.4180 | 0.4272 | - | 56.72 KB | 5.34 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | FewLargeChanges | Large | 187.61 μs | 24.022 μs | 1.317 μs | 0.17 | 0.00 | 5.3711 | 1.2207 | - | 89.35 KB | 0.15 | +| CreateDiffs_LineChunker | FewLargeChanges | Large | 1,094.80 μs | 62.034 μs | 3.400 μs | 1.00 | 0.00 | 35.1563 | 21.4844 | - | 587.29 KB | 1.00 | +| CreateLineDiffs | FewLargeChanges | Large | 1,145.38 μs | 224.340 μs | 12.297 μs | 1.05 | 0.01 | 35.1563 | 21.4844 | - | 587.27 KB | 1.00 | +| BuildInlineDiffModel | FewLargeChanges | Large | 1,213.08 μs | 61.797 μs | 3.387 μs | 1.11 | 0.00 | 44.9219 | 42.9688 | - | 760.19 KB | 1.29 | +| BuildSideBySideDiffModel | FewLargeChanges | Large | 2,878.48 μs | 1,053.643 μs | 57.754 μs | 2.63 | 0.04 | 210.9375 | 187.5000 | - | 3475.27 KB | 5.92 | +| CreateWordDiffs | FewLargeChanges | Large | 46,643.44 μs | 3,771.753 μs | 206.743 μs | 42.60 | 0.07 | 363.6364 | 363.6364 | 363.6364 | 3328.73 KB | 5.67 | +| | | | | | | | | | | | | | +| CreateLineDiffs | CompletelyDifferent | Small | 15.30 μs | 1.534 μs | 0.084 μs | 0.99 | 0.01 | 0.7935 | 0.0305 | - | 13.02 KB | 1.00 | +| CreateDiffs_LineChunker | CompletelyDifferent | Small | 15.47 μs | 2.659 μs | 0.146 μs | 1.00 | 0.00 | 0.7935 | 0.0305 | - | 13.02 KB | 1.00 | +| BuildInlineDiffModel | CompletelyDifferent | Small | 17.74 μs | 14.397 μs | 0.789 μs | 1.15 | 0.04 | 1.0986 | 0.0305 | - | 18.11 KB | 1.39 | +| CreateWordDiffs | CompletelyDifferent | Small | 47.82 μs | 11.050 μs | 0.606 μs | 3.09 | 0.05 | 3.2959 | 0.4272 | - | 54.71 KB | 4.20 | +| BuildSideBySideDiffModel | CompletelyDifferent | Small | 91.63 μs | 12.982 μs | 0.712 μs | 5.92 | 0.07 | 10.2539 | 2.9297 | - | 168.39 KB | 12.93 | +| CreateCharacterDiffs | CompletelyDifferent | Small | 215.45 μs | 28.647 μs | 1.570 μs | 13.93 | 0.05 | 1.4648 | - | - | 24.51 KB | 1.88 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | CompletelyDifferent | Large | 3,380.31 μs | 147.912 μs | 8.108 μs | 0.40 | 0.00 | 3.9063 | - | - | 90.42 KB | 0.13 | +| BuildInlineDiffModel | CompletelyDifferent | Large | 8,259.31 μs | 1,129.393 μs | 61.906 μs | 0.97 | 0.00 | 46.8750 | 31.2500 | 15.6250 | 991.73 KB | 1.43 | +| CreateLineDiffs | CompletelyDifferent | Large | 8,476.78 μs | 811.258 μs | 44.468 μs | 1.00 | 0.00 | 46.8750 | 31.2500 | 15.6250 | 693.03 KB | 1.00 | +| CreateDiffs_LineChunker | CompletelyDifferent | Large | 8,490.00 μs | 743.915 μs | 40.776 μs | 1.00 | 0.00 | 46.8750 | 31.2500 | 15.6250 | 693.03 KB | 1.00 | +| BuildSideBySideDiffModel | CompletelyDifferent | Large | 16,772.30 μs | 1,104.411 μs | 60.537 μs | 1.98 | 0.01 | 593.7500 | 500.0000 | 187.5000 | 9998.19 KB | 14.43 | +| CreateWordDiffs | CompletelyDifferent | Large | 19,650.84 μs | 71.024 μs | 3.893 μs | 2.31 | 0.01 | 343.7500 | 343.7500 | 343.7500 | 3237.9 KB | 4.67 | +| | | | | | | | | | | | | | +| BuildInlineDiffModel | LongC(...)uence [21] | Small | 13.51 μs | 1.857 μs | 0.102 μs | 0.98 | 0.01 | 0.8087 | 0.0305 | - | 13.27 KB | 1.25 | +| CreateLineDiffs | LongC(...)uence [21] | Small | 13.74 μs | 2.298 μs | 0.126 μs | 1.00 | 0.01 | 0.6409 | 0.0153 | - | 10.58 KB | 1.00 | +| CreateDiffs_LineChunker | LongC(...)uence [21] | Small | 13.78 μs | 1.691 μs | 0.093 μs | 1.00 | 0.00 | 0.6409 | 0.0153 | - | 10.58 KB | 1.00 | +| CreateCharacterDiffs | LongC(...)uence [21] | Small | 15.25 μs | 0.181 μs | 0.010 μs | 1.11 | 0.01 | 1.4954 | 0.1068 | - | 24.52 KB | 2.32 | +| BuildSideBySideDiffModel | LongC(...)uence [21] | Small | 22.72 μs | 5.309 μs | 0.291 μs | 1.65 | 0.03 | 1.4648 | 0.0916 | - | 24.29 KB | 2.30 | +| CreateWordDiffs | LongC(...)uence [21] | Small | 39.98 μs | 6.527 μs | 0.358 μs | 2.90 | 0.04 | 3.2349 | 0.3662 | - | 52.93 KB | 5.00 | +| | | | | | | | | | | | | | +| CreateCharacterDiffs | LongC(...)uence [21] | Large | 59.73 μs | 6.903 μs | 0.378 μs | 0.08 | 0.00 | 5.7373 | 1.4038 | - | 93.71 KB | 0.16 | +| CreateDiffs_LineChunker | LongC(...)uence [21] | Large | 788.26 μs | 120.861 μs | 6.625 μs | 1.00 | 0.00 | 35.1563 | 25.3906 | - | 588.25 KB | 1.00 | +| CreateLineDiffs | LongC(...)uence [21] | Large | 793.99 μs | 102.032 μs | 5.593 μs | 1.01 | 0.01 | 35.1563 | 25.3906 | - | 588.25 KB | 1.00 | +| BuildInlineDiffModel | LongC(...)uence [21] | Large | 838.26 μs | 30.790 μs | 1.688 μs | 1.06 | 0.01 | 44.9219 | 33.2031 | - | 740.08 KB | 1.26 | +| BuildSideBySideDiffModel | LongC(...)uence [21] | Large | 1,023.03 μs | 110.408 μs | 6.052 μs | 1.30 | 0.00 | 58.5938 | 42.9688 | - | 988.12 KB | 1.68 | +| CreateWordDiffs | LongC(...)uence [21] | Large | 4,663.38 μs | 252.355 μs | 13.832 μs | 5.92 | 0.06 | 398.4375 | 398.4375 | 398.4375 | 3110.18 KB | 5.29 |