diff --git a/.gitignore b/.gitignore index 9fa727e..d6f6397 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ obj/ /packages/ riderModule.iml /_ReSharper.Caches/ -**/.idea/ \ No newline at end of file +.idea/* \ No newline at end of file diff --git a/SoulstormReplayReader.Benchmarks/Benchy.cs b/SoulstormReplayReader.Benchmarks/Benchy.cs new file mode 100644 index 0000000..a69c2ba --- /dev/null +++ b/SoulstormReplayReader.Benchmarks/Benchy.cs @@ -0,0 +1,63 @@ +using BenchmarkDotNet.Attributes; +using SoulstormReplayReader.Core; + +namespace SoulstormReplayReader.Benchmarks; + +// [SimpleJob(RuntimeMoniker.Net80)] +[MemoryDiagnoser] +public class Benchy +{ + private const string ReplaysDir = "./Replays/"; + + public SsReplayReader SsReplayReader { get; set; } + + [Params( + "1v1.rec" + , + "4p_withbots.rec" + , + "2v2v2.rec" + , + "4v4.rec" + )] + public string Replay; + + [Params( + false + , + true + )] + public bool SkipImages; + + [GlobalSetup] + public void Setup() + { + var memoryStream = new MemoryStream(File.ReadAllBytes(ReplaysDir + Replay), false); + + SsReplayReader = new SsReplayReader(memoryStream) { SkipImages = SkipImages }; + } + + // [Benchmark] + // public void ReadHeader() + // { + // SsReplayReader.ReadHeader(); + // } + // + // [Benchmark] + // public void ReadInfo() + // { + // SsReplayReader.ReadInfo(); + // } + + [Benchmark] + public void ReadFull() + { + SsReplayReader.ReadFull(); + } + + [GlobalCleanup] + public void Cleanup() + { + SsReplayReader.Dispose(); + } +} \ No newline at end of file diff --git a/SoulstormReplayReader.Benchmarks/BenchyRecords.txt b/SoulstormReplayReader.Benchmarks/BenchyRecords.txt new file mode 100644 index 0000000..95b6546 --- /dev/null +++ b/SoulstormReplayReader.Benchmarks/BenchyRecords.txt @@ -0,0 +1,178 @@ +/* REC FILE NAME: replay_2p.rec + * + * | Method | _skipImages | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | + * |---------- |------------ |----------:|----------:|----------:|---------:|-------:|----------:| + * | ReadFully | False | 839.91 us | 16.350 us | 16.058 us | 363.2813 | 1.9531 | 745.77 KB | + * | ReadFully | True | 95.93 us | 1.478 us | 1.154 us | 12.3291 | - | 25.42 KB | + * + * | Method | SkipImages | Mean | Error | StdDev | Median | Gen0 | Allocated | + * |---------- |----------- |----------:|----------:|----------:|----------:|---------:|----------:| + * | ReadFully | False | 643.48 us | 12.657 us | 20.076 us | 633.88 us | 358.3984 | 732.84 KB | + * | ReadFully | True | 76.54 us | 0.346 us | 0.323 us | 76.44 us | 6.1035 | 12.5 KB | + * + * | Method | SkipImages | Mean | Error | StdDev | Gen0 | Allocated | + * |---------- |----------- |----------:|---------:|---------:|--------:|----------:| + * | ReadFully | False | 187.55 us | 3.079 us | 2.880 us | 45.4102 | 93.52 KB | + * | ReadFully | True | 26.44 us | 0.335 us | 0.297 us | 6.4392 | 13.17 KB | + * + * | Method | SkipImages | Mean | Error | StdDev | Gen0 | Allocated | + * |---------- |----------- |----------:|---------:|---------:|--------:|----------:| + * | ReadFully | False | 217.73 us | 4.190 us | 4.483 us | 45.4102 | 93.9 KB | + * | ReadFully | True | 23.28 us | 0.391 us | 0.366 us | 6.1340 | 12.54 KB | + * + * | Method | SkipImages | Mean | Error | StdDev | Gen0 | Allocated | + * |---------- |----------- |----------:|---------:|---------:|--------:|----------:| + * | ReadFully | False | 211.93 us | 2.418 us | 2.144 us | 43.4570 | 89.84 KB | + * | ReadFully | True | 20.60 us | 0.296 us | 0.262 us | 4.3640 | 8.94 KB | + * + * | Method | SkipImages | UseFileStream | Mean | Error | StdDev | Median | Gen0 | Gen1 | Allocated | + * |---------- |----------- |-------------- |----------:|----------:|----------:|----------:|--------:|-------:|----------:| + * | ReadFully | False | False | 265.56 us | 13.334 us | 39.316 us | 258.33 us | 43.4570 | 0.4883 | 90.08 KB | + * | ReadFully | False | True | 599.75 us | 14.393 us | 39.644 us | 587.87 us | 42.9688 | 0.9766 | 90.08 KB | + * | ReadFully | True | False | 21.15 us | 0.385 us | 0.674 us | 21.01 us | 4.4861 | - | 9.17 KB | + * | ReadFully | True | True | 70.86 us | 1.392 us | 1.302 us | 70.92 us | 4.3945 | - | 9.17 KB | + * + * | Method | SkipImages | UseFileStream | Mean | Error | StdDev | Gen0 | Allocated | + * |---------- |----------- |-------------- |----------:|----------:|----------:|--------:|----------:| + * | ReadFully | False | False | 237.75 us | 4.725 us | 13.092 us | 43.4570 | 88.95 KB | + * | ReadFully | False | True | 600.06 us | 11.880 us | 14.142 us | 42.9688 | 88.95 KB | + * | ReadFully | True | False | 21.76 us | 0.424 us | 0.754 us | 3.8757 | 7.92 KB | + * | ReadFully | True | True | 73.07 us | 1.312 us | 1.707 us | 3.7842 | 7.92 KB | + * + * - DOTNET 7.0 + * | Method | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | + * |---------- |----------- |----------:|---------:|---------:|--------:|-------:|----------:| + * | ReadFully | False | 215.38 us | 3.819 us | 3.386 us | 42.9688 | 0.2441 | 88.72 KB | + * | ReadFully | True | 20.20 us | 0.392 us | 0.452 us | 3.7537 | - | 7.69 KB | + * + * - DOTNET 8.0 + * | Method | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | + * |---------- |----------- |----------:|---------:|---------:|--------:|-------:|----------:| + * | ReadFully | False | 144.14 us | 1.383 us | 1.155 us | 42.9688 | 0.2441 | 88.56 KB | + * | ReadFully | True | 16.52 us | 0.324 us | 0.332 us | 3.6621 | - | 7.53 KB | + */ + + ----- NEW BENCHMARK HISTORY ----- + +Net 8.0 +| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|----------:|---------:|-----------:| +| ReadFull | 1v1.rec | False | 150.42 us | 2.927 us | 3.908 us | 42.9688 | 0.2441 | - | 88.56 KB | +| ReadFull | 1v1.rec | True | 16.40 us | 0.324 us | 0.333 us | 3.6621 | - | - | 7.53 KB | +| ReadFull | 2v2v2.rec | False | 905.87 us | 14.511 us | 12.117 us | 134.7656 | 54.6875 | - | 531.55 KB | +| ReadFull | 2v2v2.rec | True | 539.74 us | 10.685 us | 13.122 us | 108.3984 | 27.3438 | - | 288.64 KB | +| ReadFull | 4p_withbots.rec | False | 19,229.97 us | 377.995 us | 719.176 us | 1125.0000 | 1000.0000 | 375.0000 | 6668.13 KB | +| ReadFull | 4p_withbots.rec | True | 20,528.20 us | 405.920 us | 907.897 us | 1093.7500 | 906.2500 | 343.7500 | 6627.92 KB | +| ReadFull | 4v4.rec | False | 1,276.80 us | 25.210 us | 24.760 us | 171.8750 | 113.2813 | - | 793.19 KB | +| ReadFull | 4v4.rec | True | 1,008.51 us | 14.857 us | 13.898 us | 142.5781 | 60.5469 | - | 590.75 KB | + +Init Replay.Actions list with capacity of Replay.TotalTicks / 2 +| Method | Replay | SkipImages | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |-------------:|-----------:|-----------:|-------------:|----------:|---------:|---------:|-----------:| +| ReadFull | 1v1.rec | False | 151.58 us | 2.997 us | 5.703 us | 150.79 us | 43.2129 | 0.2441 | - | 89.02 KB | +| ReadFull | 1v1.rec | True | 15.91 us | 0.317 us | 0.596 us | 15.76 us | 3.9063 | - | - | 7.98 KB | +| ReadFull | 2v2v2.rec | False | 812.33 us | 15.300 us | 38.664 us | 800.10 us | 127.9297 | 42.9688 | - | 476.98 KB | +| ReadFull | 2v2v2.rec | True | 412.04 us | 8.206 us | 17.838 us | 405.89 us | 74.7070 | 20.9961 | - | 234.07 KB | +| ReadFull | 4p_withbots.rec | False | 14,881.39 us | 294.093 us | 645.541 us | 14,856.17 us | 1031.2500 | 843.7500 | 375.0000 | 6493.13 KB | +| ReadFull | 4p_withbots.rec | True | 14,368.24 us | 286.450 us | 669.569 us | 14,319.52 us | 1000.0000 | 875.0000 | 343.7500 | 6452.25 KB | +| ReadFull | 4v4.rec | False | 1,417.59 us | 28.016 us | 23.395 us | 1,407.82 us | 97.6563 | 68.3594 | 27.3438 | 616.45 KB | +| ReadFull | 4v4.rec | True | 1,098.68 us | 21.912 us | 38.949 us | 1,087.36 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB | + +| Method | Replay | SkipImages | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated | +|----------- |---------------- |----------- |--------------:|------------:|------------:|--------------:|----------:|---------:|---------:|-----------:| +| ReadHeader | 1v1.rec | False | 1.572 us | 0.0294 us | 0.0275 us | 1.572 us | 0.6695 | - | - | 1.37 KB | +| ReadInfo | 1v1.rec | False | 141.536 us | 2.6164 us | 2.3194 us | 141.797 us | 42.4805 | - | - | 87.67 KB | +| ReadFull | 1v1.rec | False | 141.130 us | 1.8004 us | 1.4056 us | 141.015 us | 43.2129 | - | - | 89.06 KB | +| ReadHeader | 1v1.rec | True | 1.680 us | 0.0376 us | 0.1074 us | 1.646 us | 0.6695 | - | - | 1.37 KB | +| ReadInfo | 1v1.rec | True | 11.819 us | 0.2225 us | 0.3955 us | 11.720 us | 3.2501 | - | - | 6.64 KB | +| ReadFull | 1v1.rec | True | 14.857 us | 0.2916 us | 0.4088 us | 14.860 us | 3.9215 | - | - | 8.03 KB | +| ReadHeader | 4p_withbots.rec | False | 1.849 us | 0.0362 us | 0.0355 us | 1.844 us | 0.7801 | - | - | 1.59 KB | +| ReadInfo | 4p_withbots.rec | False | 75.350 us | 1.4535 us | 1.4927 us | 74.821 us | 22.2168 | - | - | 45.76 KB | +| ReadFull | 4p_withbots.rec | False | 15,368.249 us | 303.3914 us | 755.5498 us | 15,409.927 us | 1078.1250 | 890.6250 | 421.8750 | 6492.96 KB | +| ReadHeader | 4p_withbots.rec | True | 1.912 us | 0.0382 us | 0.1000 us | 1.884 us | 0.7782 | - | - | 1.59 KB | +| ReadInfo | 4p_withbots.rec | True | 11.963 us | 0.1580 us | 0.1401 us | 11.948 us | 2.5177 | - | - | 5.16 KB | +| ReadFull | 4p_withbots.rec | True | 14,558.302 us | 288.3673 us | 803.8528 us | 14,454.221 us | 1046.8750 | 906.2500 | 390.6250 | 6452.2 KB | + +| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:| +| ReadFull | 1v1.rec | False | 151.58 us | 2.997 us | 5.703 us | 43.2129 | 0.2441 | - | 89.02 KB | +| ReadFull | 1v1.rec | True | 15.91 us | 0.317 us | 0.596 us | 3.9063 | - | - | 7.98 KB | +| ReadFull | 2v2v2.rec | False | 812.33 us | 15.300 us | 38.664 us | 127.9297 | 42.9688 | - | 476.98 KB | +| ReadFull | 2v2v2.rec | True | 412.04 us | 8.206 us | 17.838 us | 74.7070 | 20.9961 | - | 234.07 KB | +| ReadFull | 4p_withbots.rec | False | 14,881.39 us | 294.093 us | 645.541 us | 1031.2500 | 843.7500 | 375.0000 | 6493.13 KB | +| ReadFull | 4p_withbots.rec | True | 14,368.24 us | 286.450 us | 669.569 us | 1000.0000 | 875.0000 | 343.7500 | 6452.25 KB | +| ReadFull | 4v4.rec | False | 1,417.59 us | 28.016 us | 23.395 us | 97.6563 | 68.3594 | 27.3438 | 616.45 KB | +| ReadFull | 4v4.rec | True | 1,098.68 us | 21.912 us | 38.949 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB | + +| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:| +| ReadFull | 1v1.rec | False | 100.62 us | 1.712 us | 2.038 us | 43.4570 | - | - | 89 KB | +| ReadFull | 1v1.rec | True | 14.78 us | 0.288 us | 0.384 us | 3.9215 | - | - | 8.03 KB | +| ReadFull | 2v2v2.rec | False | 714.68 us | 12.293 us | 10.265 us | 124.0234 | 41.9922 | - | 477.07 KB | +| ReadFull | 2v2v2.rec | True | 431.81 us | 8.517 us | 8.365 us | 75.6836 | 22.4609 | - | 234.35 KB | +| ReadFull | 4p_withbots.rec | False | 15,205.05 us | 301.357 us | 648.704 us | 1078.1250 | 906.2500 | 421.8750 | 6493.01 KB | +| ReadFull | 4p_withbots.rec | True | 14,478.10 us | 286.181 us | 651.779 us | 1062.5000 | 937.5000 | 406.2500 | 6452.1 KB | +| ReadFull | 4v4.rec | False | 1,444.44 us | 28.807 us | 48.130 us | 97.6563 | 68.3594 | 27.3438 | 616.81 KB | +| ReadFull | 4v4.rec | True | 1,082.37 us | 18.606 us | 19.107 us | 70.3125 | 54.6875 | 27.3438 | 414.53 KB | + +| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |-------------:|-----------:|-------------:|----------:|---------:|---------:|-----------:| +| ReadFull | 1v1.rec | False | 102.87 us | 1.552 us | 2.176 us | 43.4570 | - | - | 89 KB | +| ReadFull | 1v1.rec | True | 15.57 us | 0.310 us | 0.839 us | 3.9063 | - | - | 8.03 KB | +| ReadFull | 2v2v2.rec | False | 733.72 us | 14.360 us | 18.672 us | 124.0234 | 41.9922 | - | 477.07 KB | +| ReadFull | 2v2v2.rec | True | 461.28 us | 8.453 us | 15.877 us | 75.1953 | 23.4375 | - | 234.35 KB | +| ReadFull | 4p_withbots.rec | False | 17,231.75 us | 589.520 us | 1,738.212 us | 1031.2500 | 843.7500 | 375.0000 | 6493.06 KB | +| ReadFull | 4p_withbots.rec | True | 16,119.01 us | 320.763 us | 710.788 us | 1000.0000 | 875.0000 | 343.7500 | 6452.12 KB | +| ReadFull | 4v4.rec | False | 1,561.33 us | 29.811 us | 31.898 us | 97.6563 | 68.3594 | 27.3438 | 616.81 KB | +| ReadFull | 4v4.rec | True | 1,188.38 us | 23.616 us | 27.196 us | 70.3125 | 54.6875 | 27.3438 | 414.53 KB | + +// ascii string equals with stackalloc bytes sequenceEqual no ignore case +| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:| +| ReadFull | 1v1.rec | True | 13.17 us | 0.258 us | 0.265 us | 3.7079 | - | - | 7.6 KB | +| ReadFull | 2v2v2.rec | True | 400.75 us | 7.956 us | 12.848 us | 76.6602 | 20.9961 | - | 233.86 KB | +| ReadFull | 4p_withbots.rec | True | 14,380.66 us | 285.889 us | 768.022 us | 1062.5000 | 921.8750 | 406.2500 | 6451.56 KB | +| ReadFull | 4v4.rec | True | 1,069.55 us | 20.197 us | 20.741 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB | + +// ascii string equals with stackalloc bytes custom sequenceEqual with ignore case +| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:| +| ReadFull | 1v1.rec | True | 13.96 us | 0.273 us | 0.507 us | 3.7079 | - | - | 7.6 KB | +| ReadFull | 2v2v2.rec | True | 409.48 us | 7.863 us | 6.970 us | 77.1484 | 20.9961 | - | 233.86 KB | +| ReadFull | 4p_withbots.rec | True | 13,161.68 us | 238.163 us | 185.942 us | 1000.0000 | 875.0000 | 343.7500 | 6451.48 KB | +| ReadFull | 4v4.rec | True | 1,049.49 us | 20.183 us | 17.892 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB | + +// Extract of logic and cleaning ExBinaryReader +| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |-------------:|-----------:|-----------:|----------:|---------:|---------:|-----------:| +| ReadFull | 1v1.rec | False | 106.58 us | 1.815 us | 2.360 us | 42.9688 | - | - | 88.48 KB | +| ReadFull | 1v1.rec | True | 13.45 us | 0.156 us | 0.130 us | 3.7079 | - | - | 7.6 KB | +| ReadFull | 2v2v2.rec | False | 713.72 us | 8.824 us | 7.823 us | 127.9297 | 44.9219 | - | 476.3 KB | +| ReadFull | 2v2v2.rec | True | 429.58 us | 8.062 us | 6.732 us | 77.1484 | 20.9961 | - | 233.86 KB | +| ReadFull | 4p_withbots.rec | False | 15,263.44 us | 303.842 us | 570.690 us | 1078.1250 | 906.2500 | 421.8750 | 6492.22 KB | +| ReadFull | 4p_withbots.rec | True | 14,930.50 us | 327.931 us | 924.936 us | 1046.8750 | 906.2500 | 390.6250 | 6451.62 KB | +| ReadFull | 4v4.rec | False | 1,394.81 us | 27.690 us | 32.963 us | 93.7500 | 64.4531 | 27.3438 | 616.06 KB | +| ReadFull | 4v4.rec | True | 1,106.32 us | 21.747 us | 35.731 us | 70.3125 | 54.6875 | 27.3438 | 414.01 KB | + + +26.07.2024 +| Method | Replay | SkipImages | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |------------:|----------:|----------:|----------:|---------:|---------:|-----------:| +| ReadFull | 1v1.rec | False | 109.6 us | 1.65 us | 1.46 us | 42.4805 | 7.0801 | - | 88.45 KB | +| ReadFull | 2v2v2.rec | False | 735.7 us | 14.28 us | 12.66 us | 109.3750 | 58.5938 | - | 476.2 KB | +| ReadFull | 4p_withbots.rec | False | 14,794.2 us | 295.22 us | 635.50 us | 1078.1250 | 906.2500 | 421.8750 | 6492.21 KB | +| ReadFull | 4v4.rec | False | 1,352.9 us | 16.24 us | 14.40 us | 93.7500 | 64.4531 | 27.3438 | 615.98 KB | + + +27.07.2024 Images parsing is better +| Method | Replay | SkipImages | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated | +|--------- |---------------- |----------- |-------------:|-----------:|-------------:|-------------:|----------:|---------:|---------:|-----------:| +| ReadFull | 1v1.rec | False | 42.87 us | 0.473 us | 0.395 us | 42.80 us | 42.5415 | 7.0801 | - | 88.27 KB | +| ReadFull | 1v1.rec | True | 12.87 us | 0.095 us | 0.080 us | 12.87 us | 3.6926 | - | - | 7.55 KB | +| ReadFull | 2v2v2.rec | False | 551.09 us | 10.990 us | 23.656 us | 541.74 us | 107.4219 | 53.7109 | - | 475.77 KB | +| ReadFull | 2v2v2.rec | True | 427.14 us | 3.915 us | 3.269 us | 427.37 us | 74.7070 | 20.9961 | - | 233.8 KB | +| ReadFull | 4p_withbots.rec | False | 14,880.91 us | 294.969 us | 561.210 us | 15,118.21 us | 1078.1250 | 906.2500 | 421.8750 | 6492.25 KB | +| ReadFull | 4p_withbots.rec | True | 15,426.41 us | 403.218 us | 1,103.803 us | 15,222.37 us | 1046.8750 | 890.6250 | 390.6250 | 6451.52 KB | +| ReadFull | 4v4.rec | False | 1,227.00 us | 24.521 us | 28.238 us | 1,222.49 us | 93.7500 | 64.4531 | 27.3438 | 615.61 KB | +| ReadFull | 4v4.rec | True | 1,152.13 us | 26.309 us | 73.340 us | 1,117.18 us | 70.3125 | 54.6875 | 27.3438 | 413.96 KB | + diff --git a/SoulstormReplayReader.Benchmarks/Program.cs b/SoulstormReplayReader.Benchmarks/Program.cs new file mode 100644 index 0000000..7108b1f --- /dev/null +++ b/SoulstormReplayReader.Benchmarks/Program.cs @@ -0,0 +1,5 @@ +using BenchmarkDotNet.Running; +using SoulstormReplayReader.Benchmarks; + +BenchmarkRunner.Run(); +// BenchmarkRunner.Run(); \ No newline at end of file diff --git a/SoulstormReplayReader.Benchmarks/Replays/1v1.rec b/SoulstormReplayReader.Benchmarks/Replays/1v1.rec new file mode 100644 index 0000000..86afe28 Binary files /dev/null and b/SoulstormReplayReader.Benchmarks/Replays/1v1.rec differ diff --git a/SoulstormReplayReader.Benchmarks/Replays/2v2v2.rec b/SoulstormReplayReader.Benchmarks/Replays/2v2v2.rec new file mode 100644 index 0000000..7658bf5 Binary files /dev/null and b/SoulstormReplayReader.Benchmarks/Replays/2v2v2.rec differ diff --git a/SoulstormReplayReader.Benchmarks/Replays/4p_withbots.rec b/SoulstormReplayReader.Benchmarks/Replays/4p_withbots.rec new file mode 100644 index 0000000..da0e495 Binary files /dev/null and b/SoulstormReplayReader.Benchmarks/Replays/4p_withbots.rec differ diff --git a/SoulstormReplayReader.Benchmarks/Replays/4v4.rec b/SoulstormReplayReader.Benchmarks/Replays/4v4.rec new file mode 100644 index 0000000..2a6c682 Binary files /dev/null and b/SoulstormReplayReader.Benchmarks/Replays/4v4.rec differ diff --git a/SoulstormReplayReader.Benchmarks/SoulstormReplayReader.Benchmarks.csproj b/SoulstormReplayReader.Benchmarks/SoulstormReplayReader.Benchmarks.csproj new file mode 100644 index 0000000..694767b --- /dev/null +++ b/SoulstormReplayReader.Benchmarks/SoulstormReplayReader.Benchmarks.csproj @@ -0,0 +1,20 @@ + + + + Exe + net8.0 + enable + disable + + + + + + + + + + + + + diff --git a/SoulstormReplayReader.Benchmarks/TestBenchy.cs b/SoulstormReplayReader.Benchmarks/TestBenchy.cs new file mode 100644 index 0000000..92e48a5 --- /dev/null +++ b/SoulstormReplayReader.Benchmarks/TestBenchy.cs @@ -0,0 +1,31 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; + +namespace SoulstormReplayReader.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80)] +[MemoryDiagnoser] +public class TestBenchy +{ + [Params(0xff3246aa, 0xff536ecd)] + public uint TestUInt; + + [Benchmark] + public void ConvertBgraToArgbBits() + { + for (int i = 0; i < 1000; i++) + { + var u = (TestUInt & 0x000000FFU) << 24 | (TestUInt & 0x0000FF00U) << 8 | (TestUInt & 0x00FF0000U) >> 8 | (TestUInt & 0xFF000000U) >> 24; + } + } + + [Benchmark] + public void ConvertBgraToArgbRotate() + { + for (int i = 0; i < 1000; i++) + { + var u = BitOperations.RotateRight(TestUInt & 0x00FF00FFu, 8) + BitOperations.RotateLeft(TestUInt & 0xFF00FF00u, 8); + } + } +} \ No newline at end of file diff --git a/SoulstormReplayReader.Core/Models/ReplayImage.cs b/SoulstormReplayReader.Core/Models/ReplayImage.cs index 57908e3..961570e 100644 --- a/SoulstormReplayReader.Core/Models/ReplayImage.cs +++ b/SoulstormReplayReader.Core/Models/ReplayImage.cs @@ -17,8 +17,9 @@ public sealed class ReplayImage Bitmap = new ReplayColor[height * width]; } - public void SetPixel(int x, int y, ReplayColor color) + public Span GetLine(int y) { - Bitmap[y * Width + x] = color; + var start = y * Width; + return Bitmap.AsSpan(start, Width); } } \ No newline at end of file diff --git a/SoulstormReplayReader.Core/SsReplayReader.cs b/SoulstormReplayReader.Core/SsReplayReader.cs index 159cc56..44e5e2d 100644 --- a/SoulstormReplayReader.Core/SsReplayReader.cs +++ b/SoulstormReplayReader.Core/SsReplayReader.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using SoulstormReplayReader.Core.Domain; @@ -355,10 +356,13 @@ public sealed class SsReplayReader(Stream stream) : IDisposable playerImage.Init(width, height); for (var y = height - 1; y >= 0; y--) { - for (var x = 0; x < width; x++) - { - playerImage.SetPixel(x, y, _binaryReader.ReadBgraToArgb()); - } + var line = playerImage.GetLine(y); + + _binaryReader.ReadBytes(MemoryMarshal.AsBytes(line)); + var uintLine = MemoryMarshal.Cast(line); + + for (var x = 0; x < uintLine.Length; x++) + uintLine[x] = BinaryPrimitives.ReverseEndianness(uintLine[x]); } } } diff --git a/SoulstormReplayReader.Playground/Program.cs b/SoulstormReplayReader.Playground/Program.cs new file mode 100644 index 0000000..8c56172 --- /dev/null +++ b/SoulstormReplayReader.Playground/Program.cs @@ -0,0 +1,210 @@ +using SoulstormReplayReader.Core; + +string[] filePaths = +{ + // @"TestReplays\replay_6p_2v2v2_001.rec", + // @"TestReplays\gens_builds\ork_many_researches.rec", +// @"TestReplays\lag\lag3.rec", +// "TestReplays/buildTest/ork_all_things.rec", +// "TestReplays/buildTest/eldar_all_things.rec", +// "TestReplays/buildTest/necr_all_things.rec", +// "TestReplays/buildTest/sisters_all_things.rec", + // "TestReplays/buildTest/eldar_aspect.rec", +// "TestReplays\\Unfair\\Cheats\\deletedArmy_4.rec", + // "TestReplays\\Tests\\steam_skip_data_test_1.rec", + // "TestReplays\\Tests\\some_error.rec", + // "TestReplays\\790059-W-DE-L-CSM.rec" + "TestReplays\\4p_withBots.rec", + // "TestReplays\\unification_anon_2_races.rec" + // "TestReplays\\Unfair\\Bugs\\SoB_2commanders_1squad.rec", + // @"TestReplays\Unfair\Cheats\eresArchitect_delet.rec", + // @"TestReplays\Broken\#860994_empty_name.rec", + // "TestReplays\\Unfair\\Cheats\\mono_in_1_min.rec", +// @"TestReplays\old\BM.rec" +// "TestReplays\\Unfair\\Bugs\\necr_action_110.rec", +}; +string[] dirPaths = +{ + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Many", + // "TestReplays\\Long", + // "TestReplays\\Tests", + // "TestReplays\\Broken", + // "TestReplays\\gens_builds", + // "TestReplays\\Unfair\\Cheats", + // "TestReplays\\Unfair\\Bugs", +}; + +var replayPaths = filePaths.Concat(dirPaths.SelectMany(Directory.EnumerateFiles)).ToArray().AsSpan(); // .Select(path => "..\\..\\..\\" + path) + +foreach (var replayPath in replayPaths) +{ + var stream = new MemoryStream(File.ReadAllBytes(replayPath), false); // 8192 + +// #if DEBUGLOGGING + // Console.WriteLine(replayPath); +// #endif + + using var replayReader = new SsReplayReader(stream) + { + CheckForBugs = true, + SkipImages = true + }; + var replay = replayReader.ReadFull(); + + Console.WriteLine(string.Join(' ', replay.WinConditions)); + +// #if DEBUGLOGGING + // Console.WriteLine(replay.TotalTicks); + // Console.WriteLine(replay.Actions?.GroupBy(x => x.Tick).Count()); +// #endif + + // foreach (var player in replay.Players.Where(player => player.BugChecker != null)) + // { + // if (player.BugChecker.HasAccusations()) + // { + // Console.WriteLine($"\n{filePath}"); + // Console.WriteLine($"{player.Name} [{player.Race}]:\n {player.BugChecker.GetAccusationsList()}"); + // } + // } +} + + +// Console.WriteLine(@$" +// ---------GAME SETTINGS--------- +// world seed: {replay.WorldSeed} +// ai diff: {replay.GameSettings.AiDifficulty} +// start res: {replay.GameSettings.StartResources} +// lock teams: {replay.GameSettings.LockTeams} +// cheats on: {replay.GameSettings.CheatsOn} +// start location: {replay.GameSettings.StartingLocation} +// game speed: {replay.GameSettings.GameSpeed} +// res sharing: {replay.GameSettings.ResourceSharing} +// res rate: {replay.GameSettings.ResourceRate} +// glob: {replay.ConditionsString} +// "); +// } + +// foreach (var cmd in replayReader.Actions) +// { +// actions.Add(cmd); +// } +// foreach (var action in replay.Actions) +// { +// var player = replay.Players[action.PlayerId]; +// if (player.ResolvedRace == RaceEnum.Necrons) +// { +// +// } +// } +// TO WATCH +// #351207-W-ORK-L-NEC.rec +// #351190-W-ORK-L-NEC.rec +// #274008.rec + +// foreach (var cmd in actions.OrderBy(a => a)) +// { +// Console.Write($"{cmd} "); +// } + +// foreach (var cmd in actions.OrderBy(a => a.Key).GroupBy(a => a.Key)) +// { +// foreach (var a in cmd.DistinctBy(a => a.Value.Value).OrderBy(a => a.Value.Value)) +// { +// Console.WriteLine($"{a.Key} {a.Value.Key} {a.Value.Value}"); +// } +// } + +// var emptyAction = new EmptyAction(); +// IGameAction action = emptyAction; +// Console.WriteLine(0 == null); + +// var inStream = new BufferedStream(File.OpenRead(@"TestReplays/gameTest/12_actions.rec"), 65536); +// var outStream = new BufferedStream(File.OpenWrite(@"TestReplays/gameTest/12_actions_renamed.rec"), 65536); +// ReplayService.Rename(inStream, outStream, "12 acutiomos").Wait(); + +/* primary commands: + 117 - build building + 2 - destroy building + */ + +//1 1 e8 3 0 0 2 6 66 c3 0 0 0 54 1 61 0 1c 2 +//1 1 e8 3 0 0 2 6 64 c3 0 0 0 bc fd a0 0 2c 1 + + +/* +---------GAME SETTINGS--------- + ai diff: 1 + start res: 0 + lock teams: 1 + cheats on: 0 +start location: 2 + game speed: 0 + res sharing: 2 + res rate: 0 + */ + +// foreach (var kv in replayReader.ComPairs) +// { +// commands.Add(kv); +// } + + +// Console.WriteLine(@$". +// ------------GENERAL------------ +// name: {replay.IngameName} +// mod: {replay.ModName} +// version: {replay.Version} +// engine: {replay.EngineName} +// engine addon: {replay.EngineAddon} +// active players: {replay.ActivePlayersCount} +// teams: {replay.TeamsCount} +// total ticks: {replay.TotalTicks} +// . +// --------------LAG-------------- +// lag chunks: {replay.StreamInfo.LagChunks} +// . +// --------------MAP-------------- +// map name: {replay.Map.Name} +// map size: {replay.Map.Size} +// "); +// +// +// foreach (var msg in replay.ChatMessages) +// { +// Console.WriteLine(msg.ToString()); +// } +// } + +// Console.WriteLine(@$" +// ---------GAME SETTINGS--------- +// ai diff: {replay.GameSettings.AiDifficulty} +// start res: {replay.GameSettings.StartResources} +// lock teams: {replay.GameSettings.LockTeams} +// cheats on: {replay.GameSettings.CheatsOn} +// start location: {replay.GameSettings.StartingLocation} +// game speed: {replay.GameSettings.GameSpeed} +// res sharing: {replay.GameSettings.ResourceSharing} +// res rate: {replay.GameSettings.ResourceRate} +// "); +// diff --git a/SoulstormReplayReader.Playground/SoulstormReplayReader.Playground.csproj b/SoulstormReplayReader.Playground/SoulstormReplayReader.Playground.csproj new file mode 100644 index 0000000..3c21d8d --- /dev/null +++ b/SoulstormReplayReader.Playground/SoulstormReplayReader.Playground.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + disable + + + + + + + diff --git a/SoulstormReplayReader.sln b/SoulstormReplayReader.sln index be585f7..4c6899b 100644 --- a/SoulstormReplayReader.sln +++ b/SoulstormReplayReader.sln @@ -2,6 +2,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoulstormReplayReader.Core", "SoulstormReplayReader.Core\SoulstormReplayReader.Core.csproj", "{5DAD7ED6-C5A1-4489-9D17-57C752A5296A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoulstormReplayReader.Benchmarks", "SoulstormReplayReader.Benchmarks\SoulstormReplayReader.Benchmarks.csproj", "{7C787F7D-BA3D-47C3-88AD-F281A988E1B2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoulstormReplayReader.Playground", "SoulstormReplayReader.Playground\SoulstormReplayReader.Playground.csproj", "{0ACC5A97-39BC-412F-871F-4AE88DDE0808}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +16,13 @@ Global {5DAD7ED6-C5A1-4489-9D17-57C752A5296A}.Debug|Any CPU.Build.0 = Debug|Any CPU {5DAD7ED6-C5A1-4489-9D17-57C752A5296A}.Release|Any CPU.ActiveCfg = Release|Any CPU {5DAD7ED6-C5A1-4489-9D17-57C752A5296A}.Release|Any CPU.Build.0 = Release|Any CPU + {7C787F7D-BA3D-47C3-88AD-F281A988E1B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C787F7D-BA3D-47C3-88AD-F281A988E1B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C787F7D-BA3D-47C3-88AD-F281A988E1B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C787F7D-BA3D-47C3-88AD-F281A988E1B2}.Release|Any CPU.Build.0 = Release|Any CPU + {0ACC5A97-39BC-412F-871F-4AE88DDE0808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0ACC5A97-39BC-412F-871F-4AE88DDE0808}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0ACC5A97-39BC-412F-871F-4AE88DDE0808}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0ACC5A97-39BC-412F-871F-4AE88DDE0808}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal