Skip to content

SequenceReader performance; elide bounds checks and book-keeping - #82765

Closed
mgravell wants to merge 23 commits into
mainfrom
marc/sequencereader
Closed

SequenceReader performance; elide bounds checks and book-keeping#82765
mgravell wants to merge 23 commits into
mainfrom
marc/sequencereader

Conversation

@mgravell

@mgravellmgravell commented Feb 28, 2023

Copy link
Copy Markdown
Contributor

context: https://twitter.com/marcgravell/status/1629955240895078405 (read back for @davidfowl mention)

no direct side-by-side benchmark (what's a good way to arrange that here? I'm all ears), but in a proof-of-concept benchmark (see link above), achieved ~0.7 vs existing (note: that number was in-part based on the now removed ref T changes)

purpose: improved performance in SequenceReader

approach:

  • reduced book-keeping by usually only manipulating a single index
  • remove bounds-checking (since we're checking the bounds ourselves) via Unsafe.Add(ref T, int)
  • when possible (net7+) store the current span's ref T directly; otherwise, store the ReadOnlySpan<T> like before, and use JIT-friendly accessor to expose the same API to the implementation code
  • deconstruct SequencePosition for packing purposes
  • elide bounds checks in search by slicing for [0,len) loop
  • simplify position calculation
  • in search loop, avoid copying out the T value (of unknown size); use the ref instead
  • don't store/maintain "next" position; refactor next buffer fetch (removes need to retain "next")
  • reduce size to 72 bytes from 96 bytes (note: in theory we don't actually need _currentPositionInteger (by obtaining the outer span and initializing _currentSpanIndex accordingly), but because of padding that wouldn't help unless we were able to split out the _currentSpan into int plus ref T via unsafe add etc, which would allow us to drop to 64 bytes)
  • remove bounds checks in tryget/trypeek via hoist/uint check
  • use non-short-circuiting & in Advance, since we expect both sides to pass in the happy path (and both are safe)

benchmarks are here

(all payloads are 1024 bytes, split into chunks via SegmentLength; if this is negative, it is a vector, rather than a multi-segment)

typical results: https://raw.githubusercontent.com/mgravell/SequenceReaderBench/main/results.md

some similar benchmarks with ref T + int usage (unsafe, etc) don't show significant improvements in any cases

@ghostghost assigned mgravellFeb 28, 2023
@ghost

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-memory
See info in area-owners.md if you want to be subscribed.

Issue Details

context: https://twitter.com/marcgravell/status/1629955240895078405 (read back for @davidfowl mention)

purpose: improved performance in SequenceReader

approach:

  • reduced book-keeping by usually only manipulating a single index
  • remove bounds-checking (since we're checking the bounds ourselves) via Unsafe.AddRef
  • when possible (net7+) store the current span's ref T directly
Author:mgravell
Assignees:mgravell
Labels:

area-System.Memory

Milestone:-

Comment threadsrc/libraries/System.Memory/src/System/Buffers/SequenceReader.Search.cs Outdated
@mgravell

Copy link
Copy Markdown
ContributorAuthor

@stephentoub well, I won't pretend not to be disappointed - however, thoughts on just reducing the book-keeping etc?

@stephentoub

Copy link
Copy Markdown
Member

thoughts on just reducing the book-keeping etc?

If there are safe ways of making it faster/smaller/etc., that's great, let's do it. And as I noted, I expect there are ways of writing the code differently such that bounds checks can be eliminated even without using Unsafe.

@mgravellmgravell changed the title SequenceReader performance; elide bounds checks and book-keeping by using ref-oriented approachSequenceReader performance; elide bounds checks and book-keepingMar 1, 2023
@mgravell

Copy link
Copy Markdown
ContributorAuthor

@stephentoub fair enough; I have switched back from the unsafe to indexed, but improved a bunch of other related things :)

@mgravell
mgravellforce-pushed the marc/sequencereader branch from f748b4e to ed64ca8CompareMarch 1, 2023 13:48
@mgravell

Copy link
Copy Markdown
ContributorAuthor

disclosure: I'm not currently able to run the tests locally (although it builds cleanly):

Failed to launch testhost with error: System.IO.FileNotFoundException: Path does not exist: C:\Code\runtime\sequencereader\artifacts\bin\testhost\net8.0-windows-Debug-x64\dotnet.exe
File name: 'C:\Code\runtime\sequencereader\artifacts\bin\testhost\net8.0-windows-Debug-x64\dotnet.exe'

I've tried about 30 things, but I can't get that working right now, and I can't find an idiot-proof guide to making it happy.

@stephentoub

Copy link
Copy Markdown
Member

disclosure: I'm not currently able to run the tests locally (although it builds cleanly):

How are you building and running tests?

@mgravell

Copy link
Copy Markdown
ContributorAuthor

locally, with .net8 preview installed; at command-line, dotnet build in ...\src\libraries\System.Memory\src and and ...\src\libraries\System.Memory\tests - and dotnet test in ...\src\libraries\System.Memory\tests - fails as above; also using IDE via \src\libraries\System.Memory\System.Memory.sln, but fails with same error in output window

in root, build.cmd works in my main branch, but does not generate the missing artifacts; oddly build.cmddoes not work in my branch, failing with a build.ninja failure about a missing C compiler; which is weird because it works everywhere else, including the coreclr build

@stephentoub

Copy link
Copy Markdown
Member

Make sure you've done a clean build of the repo from root first, e.g.

git clean -xdf
build clr+libs -rc release

and only then try to iterate on a specific library

cd src\libraries\System.Memory\src
dotnet build
cd ..\tests
dotnet build /t:tests

Note as well that using dotnet like that in the subdirectory is going to use whatever sdk you have installed, whereas the root build via the build script will use a local sdk that's downloaded for the repo. If you want to use that one in the subdirectories as well, you can refer to it directly via the dotnet.cmd/sh that's in the repo's root directory.

@mgravell

Copy link
Copy Markdown
ContributorAuthor

done all that (using /t:test - tests wasn't found); no change - it is still trying to use dotnet from ...\artifacts\bin\testhost\net8.0-windows-Debug-x64, which is empty, regardless of dotnet vs dotnet.cmd; the dotnet.cmd from the root seems to be using the program-files version of preview1, so... yeah, no idea what is going on; just:

'"C:\Code\runtime\sequencereader\artifacts\bin\testhost\net8.0-windows-Debug-x64\dotnet.exe"' is not recognized as an internal or external command, operable program or batch file.

(because it does not exist)

@mgravell

mgravell commented Mar 2, 2023

Copy link
Copy Markdown
ContributorAuthor

interestingly, TryPeek seems to suffer a weird regression only on net7 (fine on net6 and net8); I propose to ignore net7, and I'll re-run the benchmarks only on net8 since this doesn't feel like a backport thing (I'm very intrigued if anyone knows what this regression may be - presumably a JIT change around branches):

 [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool TryPeek(out T value) {
AssertValidPosition(); // note: [Conditional("DEBUG")]
if (_currentSpanIndex == _currentSpan.Length) // only true at EOF due to eager read
{
value = default;
return false;
}
value = _currentSpan[_currentSpanIndex];
return true;
}

results:

| Method | Job | Runtime | SegmentLength | Mean | Error | StdDev ||-------------- |--------- |--------- |-------------- |-----------:|---------:|---------:|| SystemTryPeek | .NET 6.0 | .NET 6.0 | -1024 | 747.2 ns | 14.42 ns | 14.81 ns || CustomTryPeek | .NET 6.0 | .NET 6.0 | -1024 | 687.9 ns | 9.98 ns | 9.33 ns || SystemTryPeek | .NET 6.0 | .NET 6.0 | 4 | 749.9 ns | 11.27 ns | 9.99 ns || CustomTryPeek | .NET 6.0 | .NET 6.0 | 4 | 692.4 ns | 11.89 ns | 10.54 ns || SystemTryPeek | .NET 6.0 | .NET 6.0 | 128 | 742.4 ns | 10.89 ns | 10.19 ns || CustomTryPeek | .NET 6.0 | .NET 6.0 | 128 | 690.3 ns | 9.60 ns | 8.98 ns || SystemTryPeek | .NET 6.0 | .NET 6.0 | 1024 | 743.5 ns | 5.95 ns | 5.56 ns || CustomTryPeek | .NET 6.0 | .NET 6.0 | 1024 | 682.0 ns | 3.82 ns | 3.57 ns || | | | | | | || SystemTryPeek | .NET 7.0 | .NET 7.0 | -1024 | 679.6 ns | 8.15 ns | 7.23 ns || CustomTryPeek | .NET 7.0 | .NET 7.0 | -1024 | 1,121.2 ns | 12.48 ns | 9.74 ns || SystemTryPeek | .NET 7.0 | .NET 7.0 | 4 | 683.3 ns | 11.10 ns | 10.38 ns || CustomTryPeek | .NET 7.0 | .NET 7.0 | 4 | 1,123.8 ns | 9.98 ns | 8.33 ns || SystemTryPeek | .NET 7.0 | .NET 7.0 | 128 | 685.8 ns | 9.32 ns | 8.72 ns || CustomTryPeek | .NET 7.0 | .NET 7.0 | 128 | 1,118.5 ns | 10.64 ns | 9.95 ns || SystemTryPeek | .NET 7.0 | .NET 7.0 | 1024 | 679.0 ns | 6.24 ns | 5.84 ns || CustomTryPeek | .NET 7.0 | .NET 7.0 | 1024 | 1,116.6 ns | 6.57 ns | 5.49 ns || | | | | | | || SystemTryPeek | .NET 8.0 | .NET 8.0 | -1024 | 704.7 ns | 13.94 ns | 25.50 ns || CustomTryPeek | .NET 8.0 | .NET 8.0 | -1024 | 630.5 ns | 8.47 ns | 7.92 ns || SystemTryPeek | .NET 8.0 | .NET 8.0 | 4 | 690.5 ns | 9.48 ns | 8.86 ns || CustomTryPeek | .NET 8.0 | .NET 8.0 | 4 | 625.4 ns | 5.49 ns | 4.59 ns || SystemTryPeek | .NET 8.0 | .NET 8.0 | 128 | 686.4 ns | 8.86 ns | 8.29 ns || CustomTryPeek | .NET 8.0 | .NET 8.0 | 128 | 624.5 ns | 5.68 ns | 5.31 ns || SystemTryPeek | .NET 8.0 | .NET 8.0 | 1024 | 696.3 ns | 12.07 ns | 10.70 ns || CustomTryPeek | .NET 8.0 | .NET 8.0 | 1024 | 631.1 ns | 8.28 ns | 7.74 ns |

(I also yanked the _currentSpan.Length into a field, to see if it was not inlining the .Length, but: no difference)

@stephentoub

Copy link
Copy Markdown
Member

I'm not sure why there's a .NET 7 regression, but regardless of target, I suggest trying pulling the fields into locals prior to doing the comparison; you can do it in a way that'll result in smaller code and no bounds check:
SharpLab

@mgravell

mgravell commented Mar 2, 2023

Copy link
Copy Markdown
ContributorAuthor

@stephentoub ironically despite the demonstrably better opcode, that tweak actually seems to react negatively in tests, on net7 and net8 - again like I'm hitting some JIT regression; if you look at net6, it looks great! but:

  • on net7, both approaches regress vs baseline, the hoist+uint regressing less (i.e. the better of the two)
  • on net8, my pre-hoist version improves, and the hoist+uint regresses significantly

should I flag these somewhere else? I can put a fully runnable version somewhere

| Method | Job | Runtime | SegmentLength | Mean | Error | StdDev | Ratio | RatioSD ||--------------- |--------- |--------- |-------------- |-----------:|---------:|---------:|------:|--------:|| SystemTryPeek | .NET 6.0 | .NET 6.0 | -1024 | 743.3 ns | 11.42 ns | 10.69 ns | 1.00 | 0.00 || CustomTryPeek | .NET 6.0 | .NET 6.0 | -1024 | 675.4 ns | 1.86 ns | 1.55 ns | 0.91 | 0.01 || CustomTryPeek2 | .NET 6.0 | .NET 6.0 | -1024 | 622.8 ns | 5.77 ns | 5.12 ns | 0.84 | 0.01 || | | | | | | | | || SystemTryPeek | .NET 6.0 | .NET 6.0 | 4 | 734.8 ns | 8.49 ns | 7.53 ns | 1.00 | 0.00 || CustomTryPeek | .NET 6.0 | .NET 6.0 | 4 | 673.0 ns | 1.06 ns | 0.99 ns | 0.92 | 0.01 || CustomTryPeek2 | .NET 6.0 | .NET 6.0 | 4 | 620.6 ns | 5.98 ns | 5.00 ns | 0.84 | 0.01 || | | | | | | | | || SystemTryPeek | .NET 6.0 | .NET 6.0 | 128 | 744.8 ns | 10.64 ns | 9.95 ns | 1.00 | 0.00 || CustomTryPeek | .NET 6.0 | .NET 6.0 | 128 | 681.8 ns | 8.88 ns | 8.31 ns | 0.92 | 0.02 || CustomTryPeek2 | .NET 6.0 | .NET 6.0 | 128 | 629.0 ns | 9.74 ns | 9.11 ns | 0.84 | 0.01 || | | | | | | | | || SystemTryPeek | .NET 6.0 | .NET 6.0 | 1024 | 732.0 ns | 3.34 ns | 2.96 ns | 1.00 | 0.00 || CustomTryPeek | .NET 6.0 | .NET 6.0 | 1024 | 678.1 ns | 4.33 ns | 4.05 ns | 0.93 | 0.01 || CustomTryPeek2 | .NET 6.0 | .NET 6.0 | 1024 | 618.9 ns | 2.27 ns | 1.89 ns | 0.85 | 0.00 || | | | | | | | | || SystemTryPeek | .NET 7.0 | .NET 7.0 | -1024 | 672.4 ns | 2.22 ns | 1.97 ns | 1.00 | 0.00 || CustomTryPeek | .NET 7.0 | .NET 7.0 | -1024 | 1,112.8 ns | 9.43 ns | 8.82 ns | 1.65 | 0.01 || CustomTryPeek2 | .NET 7.0 | .NET 7.0 | -1024 | 891.5 ns | 2.13 ns | 1.89 ns | 1.33 | 0.01 || | | | | | | | | || SystemTryPeek | .NET 7.0 | .NET 7.0 | 4 | 673.8 ns | 3.40 ns | 2.84 ns | 1.00 | 0.00 || CustomTryPeek | .NET 7.0 | .NET 7.0 | 4 | 1,143.1 ns | 22.19 ns | 24.66 ns | 1.69 | 0.03 || CustomTryPeek2 | .NET 7.0 | .NET 7.0 | 4 | 895.9 ns | 10.14 ns | 8.99 ns | 1.33 | 0.01 || | | | | | | | | || SystemTryPeek | .NET 7.0 | .NET 7.0 | 128 | 676.0 ns | 4.09 ns | 3.63 ns | 1.00 | 0.00 || CustomTryPeek | .NET 7.0 | .NET 7.0 | 128 | 1,123.0 ns | 17.91 ns | 16.76 ns | 1.66 | 0.03 || CustomTryPeek2 | .NET 7.0 | .NET 7.0 | 128 | 895.3 ns | 6.10 ns | 5.41 ns | 1.32 | 0.01 || | | | | | | | | || SystemTryPeek | .NET 7.0 | .NET 7.0 | 1024 | 682.5 ns | 8.38 ns | 7.84 ns | 1.00 | 0.00 || CustomTryPeek | .NET 7.0 | .NET 7.0 | 1024 | 1,125.3 ns | 18.56 ns | 17.36 ns | 1.65 | 0.03 || CustomTryPeek2 | .NET 7.0 | .NET 7.0 | 1024 | 899.3 ns | 9.17 ns | 8.57 ns | 1.32 | 0.02 || | | | | | | | | || SystemTryPeek | .NET 8.0 | .NET 8.0 | -1024 | 670.7 ns | 1.06 ns | 0.94 ns | 1.00 | 0.00 || CustomTryPeek | .NET 8.0 | .NET 8.0 | -1024 | 619.8 ns | 4.84 ns | 4.29 ns | 0.92 | 0.01 || CustomTryPeek2 | .NET 8.0 | .NET 8.0 | -1024 | 1,116.0 ns | 13.13 ns | 10.96 ns | 1.66 | 0.02 || | | | | | | | | || SystemTryPeek | .NET 8.0 | .NET 8.0 | 4 | 673.0 ns | 1.18 ns | 0.98 ns | 1.00 | 0.00 || CustomTryPeek | .NET 8.0 | .NET 8.0 | 4 | 625.0 ns | 7.32 ns | 6.85 ns | 0.93 | 0.01 || CustomTryPeek2 | .NET 8.0 | .NET 8.0 | 4 | 1,131.7 ns | 11.41 ns | 10.67 ns | 1.68 | 0.01 || | | | | | | | | || SystemTryPeek | .NET 8.0 | .NET 8.0 | 128 | 683.7 ns | 6.76 ns | 6.32 ns | 1.00 | 0.00 || CustomTryPeek | .NET 8.0 | .NET 8.0 | 128 | 633.2 ns | 9.56 ns | 8.94 ns | 0.93 | 0.01 || CustomTryPeek2 | .NET 8.0 | .NET 8.0 | 128 | 1,132.7 ns | 17.05 ns | 15.11 ns | 1.66 | 0.03 || | | | | | | | | || SystemTryPeek | .NET 8.0 | .NET 8.0 | 1024 | 683.4 ns | 10.02 ns | 9.37 ns | 1.00 | 0.00 || CustomTryPeek | .NET 8.0 | .NET 8.0 | 1024 | 629.9 ns | 10.97 ns | 10.26 ns | 0.92 | 0.02 || CustomTryPeek2 | .NET 8.0 | .NET 8.0 | 1024 | 1,136.0 ns | 21.21 ns | 20.84 ns | 1.66 | 0.03 |

fortunately "read" is fine with the hoist everywhere:

| Method | Job | Runtime | SegmentLength | Mean | Error | StdDev | Ratio | RatioSD ||--------------- |--------- |--------- |-------------- |---------:|----------:|----------:|------:|--------:|| SystemTryRead | .NET 6.0 | .NET 6.0 | -1024 | 1.596 us | 0.0177 us | 0.0166 us | 1.00 | 0.00 || CustomTryRead | .NET 6.0 | .NET 6.0 | -1024 | 1.557 us | 0.0088 us | 0.0083 us | 0.98 | 0.01 || CustomTryRead2 | .NET 6.0 | .NET 6.0 | -1024 | 1.344 us | 0.0014 us | 0.0011 us | 0.84 | 0.01 || | | | | | | | | || SystemTryRead | .NET 6.0 | .NET 6.0 | 4 | 4.028 us | 0.0630 us | 0.0590 us | 1.00 | 0.00 || CustomTryRead | .NET 6.0 | .NET 6.0 | 4 | 2.628 us | 0.0301 us | 0.0281 us | 0.65 | 0.01 || CustomTryRead2 | .NET 6.0 | .NET 6.0 | 4 | 2.552 us | 0.0234 us | 0.0219 us | 0.63 | 0.01 || | | | | | | | | || SystemTryRead | .NET 6.0 | .NET 6.0 | 128 | 1.697 us | 0.0128 us | 0.0120 us | 1.00 | 0.00 || CustomTryRead | .NET 6.0 | .NET 6.0 | 128 | 1.870 us | 0.0169 us | 0.0150 us | 1.10 | 0.01 || CustomTryRead2 | .NET 6.0 | .NET 6.0 | 128 | 1.454 us | 0.0031 us | 0.0026 us | 0.86 | 0.01 || | | | | | | | | || SystemTryRead | .NET 6.0 | .NET 6.0 | 1024 | 1.480 us | 0.0132 us | 0.0124 us | 1.00 | 0.00 || CustomTryRead | .NET 6.0 | .NET 6.0 | 1024 | 1.657 us | 0.0063 us | 0.0056 us | 1.12 | 0.01 || CustomTryRead2 | .NET 6.0 | .NET 6.0 | 1024 | 1.345 us | 0.0074 us | 0.0062 us | 0.91 | 0.01 || | | | | | | | | || SystemTryRead | .NET 7.0 | .NET 7.0 | -1024 | 1.560 us | 0.0111 us | 0.0099 us | 1.00 | 0.00 || CustomTryRead | .NET 7.0 | .NET 7.0 | -1024 | 1.574 us | 0.0051 us | 0.0042 us | 1.01 | 0.01 || CustomTryRead2 | .NET 7.0 | .NET 7.0 | -1024 | 1.147 us | 0.0227 us | 0.0212 us | 0.74 | 0.02 || | | | | | | | | || SystemTryRead | .NET 7.0 | .NET 7.0 | 4 | 3.971 us | 0.0777 us | 0.0864 us | 1.00 | 0.00 || CustomTryRead | .NET 7.0 | .NET 7.0 | 4 | 2.501 us | 0.0488 us | 0.0432 us | 0.63 | 0.02 || CustomTryRead2 | .NET 7.0 | .NET 7.0 | 4 | 2.628 us | 0.0496 us | 0.0509 us | 0.66 | 0.02 || | | | | | | | | || SystemTryRead | .NET 7.0 | .NET 7.0 | 128 | 1.681 us | 0.0065 us | 0.0060 us | 1.00 | 0.00 || CustomTryRead | .NET 7.0 | .NET 7.0 | 128 | 1.734 us | 0.0033 us | 0.0026 us | 1.03 | 0.00 || CustomTryRead2 | .NET 7.0 | .NET 7.0 | 128 | 1.232 us | 0.0029 us | 0.0026 us | 0.73 | 0.00 || | | | | | | | | || SystemTryRead | .NET 7.0 | .NET 7.0 | 1024 | 1.566 us | 0.0080 us | 0.0071 us | 1.00 | 0.00 || CustomTryRead | .NET 7.0 | .NET 7.0 | 1024 | 1.722 us | 0.0031 us | 0.0027 us | 1.10 | 0.01 || CustomTryRead2 | .NET 7.0 | .NET 7.0 | 1024 | 1.138 us | 0.0183 us | 0.0172 us | 0.73 | 0.01 || | | | | | | | | || SystemTryRead | .NET 8.0 | .NET 8.0 | -1024 | 1.385 us | 0.0260 us | 0.0319 us | 1.00 | 0.00 || CustomTryRead | .NET 8.0 | .NET 8.0 | -1024 | 1.365 us | 0.0217 us | 0.0203 us | 0.98 | 0.02 || CustomTryRead2 | .NET 8.0 | .NET 8.0 | -1024 | 1.135 us | 0.0056 us | 0.0052 us | 0.81 | 0.02 || | | | | | | | | || SystemTryRead | .NET 8.0 | .NET 8.0 | 4 | 3.519 us | 0.0283 us | 0.0251 us | 1.00 | 0.00 || CustomTryRead | .NET 8.0 | .NET 8.0 | 4 | 2.917 us | 0.0302 us | 0.0267 us | 0.83 | 0.01 || CustomTryRead2 | .NET 8.0 | .NET 8.0 | 4 | 2.295 us | 0.0203 us | 0.0180 us | 0.65 | 0.01 || | | | | | | | | || SystemTryRead | .NET 8.0 | .NET 8.0 | 128 | 1.482 us | 0.0193 us | 0.0181 us | 1.00 | 0.00 || CustomTryRead | .NET 8.0 | .NET 8.0 | 128 | 1.453 us | 0.0068 us | 0.0060 us | 0.98 | 0.01 || CustomTryRead2 | .NET 8.0 | .NET 8.0 | 128 | 1.256 us | 0.0039 us | 0.0033 us | 0.85 | 0.01 || | | | | | | | | || SystemTryRead | .NET 8.0 | .NET 8.0 | 1024 | 1.348 us | 0.0107 us | 0.0095 us | 1.00 | 0.00 || CustomTryRead | .NET 8.0 | .NET 8.0 | 1024 | 1.350 us | 0.0058 us | 0.0055 us | 1.00 | 0.01 || CustomTryRead2 | .NET 8.0 | .NET 8.0 | 1024 | 1.131 us | 0.0027 us | 0.0026 us | 0.84 | 0.01 |

@adamsitnik

Copy link
Copy Markdown
Member

@mgravell Is my understanding correct that it improves perf on .NET 6 and 7, but for unknown reason regresses on 8? Is there any way I could help you with getting this PR merged (beside the code review ofc)? I could run it on my hardware for .NET 8 main vs PR and get the trace files/disassembly if needed.

@adamsitnikadamsitnik self-assigned this May 22, 2023
@adamsitnikadamsitnik added the tenet-performance Performance related issue label May 22, 2023
@jozkee

jozkee commented Aug 14, 2023

Copy link
Copy Markdown
Member

I managed to rebase this PR on top of main and compared the results against main. There are very nice improvements with the exception of TryRead and TryReadTo which shows a regression, especially on the small segment length case. We may want to hold-off this change until 9.0.

Results
BenchmarkDotNet v0.13.7-nightly.20230717.35, Windows 11 (10.0.22621.2134/22H2/2022Update/SunValley2)
AMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores
.NET SDK 8.0.100-preview.7.23376.3
[Host] : .NET 8.0.0 (8.0.23.37506), X64 RyuJIT AVX2
Job-VDEYSP : .NET 8.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-JFPICV : .NET 8.0.0 (42.42.42.42424), X64 RyuJIT AVX2
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:EnableUnsafeBinaryFormatterSerialization=true IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 | Method | Job | Toolchain | SegmentLength | Mean | Error | StdDev | Median | Min | Max | Ratio | MannWhitney(3ms) | RatioSD | Allocated | Alloc Ratio |
|--------------------- |----------- |----------- |-------------- |------------:|----------:|----------:|------------:|------------:|------------:|------:|----------------- |--------:|----------:|------------:|
| SystemAdvancePastAny | Job-VDEYSP | feature | -1024 | 235.90 ns | 2.218 ns | 1.852 ns | 236.46 ns | 232.99 ns | 238.75 ns | 0.55 | Same | 0.01 | - | NA |
| SystemAdvancePastAny | Job-JFPICV | main | -1024 | 432.11 ns | 2.496 ns | 2.335 ns | 432.02 ns | 428.73 ns | 436.98 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemAdvancePastAny | Job-VDEYSP | feature | 4 | 1,675.89 ns | 4.426 ns | 3.923 ns | 1,674.76 ns | 1,671.46 ns | 1,684.04 ns | 0.94 | Same | 0.00 | - | NA |
| SystemAdvancePastAny | Job-JFPICV | main | 4 | 1,785.57 ns | 7.547 ns | 6.690 ns | 1,785.14 ns | 1,775.78 ns | 1,798.89 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemAdvancePastAny | Job-VDEYSP | feature | 128 | 313.67 ns | 3.434 ns | 3.212 ns | 313.32 ns | 310.05 ns | 320.11 ns | 0.62 | Same | 0.01 | - | NA |
| SystemAdvancePastAny | Job-JFPICV | main | 128 | 507.31 ns | 1.234 ns | 1.093 ns | 507.05 ns | 506.04 ns | 509.61 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemAdvancePastAny | Job-VDEYSP | feature | 1024 | 233.57 ns | 1.798 ns | 1.682 ns | 232.56 ns | 232.03 ns | 237.22 ns | 0.54 | Same | 0.00 | - | NA |
| SystemAdvancePastAny | Job-JFPICV | main | 1024 | 435.17 ns | 0.879 ns | 0.822 ns | 434.92 ns | 434.27 ns | 436.68 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemAdvance | Job-VDEYSP | feature | -1024 | 442.82 ns | 6.444 ns | 6.027 ns | 441.80 ns | 436.09 ns | 453.74 ns | 1.00 | Same | 0.01 | - | NA |
| SystemAdvance | Job-JFPICV | main | -1024 | 440.75 ns | 1.499 ns | 1.171 ns | 440.77 ns | 438.91 ns | 442.45 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemAdvance | Job-VDEYSP | feature | 4 | 1,939.14 ns | 6.711 ns | 6.278 ns | 1,938.32 ns | 1,930.90 ns | 1,949.90 ns | 0.97 | Same | 0.01 | - | NA |
| SystemAdvance | Job-JFPICV | main | 4 | 1,992.90 ns | 6.847 ns | 6.070 ns | 1,993.70 ns | 1,985.12 ns | 2,006.13 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemAdvance | Job-VDEYSP | feature | 128 | 514.84 ns | 1.342 ns | 1.190 ns | 514.68 ns | 513.30 ns | 517.49 ns | 0.96 | Same | 0.00 | - | NA |
| SystemAdvance | Job-JFPICV | main | 128 | 537.23 ns | 1.004 ns | 0.839 ns | 537.03 ns | 536.11 ns | 538.77 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemAdvance | Job-VDEYSP | feature | 1024 | 439.22 ns | 1.397 ns | 1.238 ns | 438.82 ns | 437.83 ns | 441.31 ns | 0.99 | Same | 0.00 | - | NA |
| SystemAdvance | Job-JFPICV | main | 1024 | 443.68 ns | 1.115 ns | 1.043 ns | 443.44 ns | 442.62 ns | 445.81 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemPosition | Job-VDEYSP | feature | -1024 | 224.71 ns | 0.797 ns | 0.746 ns | 224.38 ns | 224.01 ns | 226.05 ns | 0.33 | Same | 0.00 | - | NA |
| SystemPosition | Job-JFPICV | main | -1024 | 681.10 ns | 2.445 ns | 2.287 ns | 681.14 ns | 677.98 ns | 686.19 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemPosition | Job-VDEYSP | feature | 4 | 450.55 ns | 1.117 ns | 1.045 ns | 450.19 ns | 449.03 ns | 452.32 ns | 0.25 | Same | 0.00 | - | NA |
| SystemPosition | Job-JFPICV | main | 4 | 1,772.73 ns | 14.692 ns | 13.743 ns | 1,779.07 ns | 1,745.56 ns | 1,790.50 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemPosition | Job-VDEYSP | feature | 128 | 227.25 ns | 0.687 ns | 0.643 ns | 226.90 ns | 226.65 ns | 228.37 ns | 0.13 | Same | 0.00 | - | NA |
| SystemPosition | Job-JFPICV | main | 128 | 1,728.64 ns | 3.115 ns | 2.914 ns | 1,728.06 ns | 1,725.46 ns | 1,734.62 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemPosition | Job-VDEYSP | feature | 1024 | 227.99 ns | 0.721 ns | 0.675 ns | 227.68 ns | 227.40 ns | 229.30 ns | 0.33 | Same | 0.00 | - | NA |
| SystemPosition | Job-JFPICV | main | 1024 | 682.82 ns | 2.620 ns | 2.451 ns | 682.41 ns | 679.96 ns | 687.86 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryPeek | Job-VDEYSP | feature | -1024 | 434.19 ns | 0.631 ns | 0.527 ns | 434.01 ns | 433.71 ns | 435.55 ns | 0.95 | Same | 0.01 | - | NA |
| SystemTryPeek | Job-JFPICV | main | -1024 | 456.50 ns | 8.852 ns | 8.694 ns | 454.83 ns | 448.30 ns | 478.09 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryPeek | Job-VDEYSP | feature | 4 | 443.23 ns | 2.940 ns | 2.606 ns | 442.49 ns | 440.53 ns | 448.91 ns | 0.99 | Same | 0.02 | - | NA |
| SystemTryPeek | Job-JFPICV | main | 4 | 449.71 ns | 7.907 ns | 7.396 ns | 447.17 ns | 440.26 ns | 462.55 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryPeek | Job-VDEYSP | feature | 128 | 444.70 ns | 2.642 ns | 2.206 ns | 444.63 ns | 441.52 ns | 448.45 ns | 0.98 | Same | 0.01 | - | NA |
| SystemTryPeek | Job-JFPICV | main | 128 | 452.82 ns | 5.928 ns | 5.545 ns | 453.32 ns | 442.99 ns | 462.30 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryPeek | Job-VDEYSP | feature | 1024 | 438.19 ns | 1.099 ns | 1.028 ns | 438.31 ns | 436.65 ns | 439.89 ns | 0.94 | Same | 0.01 | - | NA |
| SystemTryPeek | Job-JFPICV | main | 1024 | 468.28 ns | 7.365 ns | 6.889 ns | 471.56 ns | 459.28 ns | 478.39 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryReadTo | Job-VDEYSP | feature | -1024 | 85.16 ns | 0.258 ns | 0.241 ns | 85.06 ns | 84.88 ns | 85.60 ns | 1.04 | Same | 0.00 | - | NA |
| SystemTryReadTo | Job-JFPICV | main | -1024 | 82.04 ns | 0.482 ns | 0.451 ns | 82.10 ns | 80.68 ns | 82.57 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryReadTo | Job-VDEYSP | feature | 4 | 3,482.61 ns | 12.050 ns | 11.272 ns | 3,481.28 ns | 3,461.31 ns | 3,504.64 ns | 1.82 | Same | 0.00 | - | NA |
| SystemTryReadTo | Job-JFPICV | main | 4 | 1,913.75 ns | 4.098 ns | 3.633 ns | 1,911.78 ns | 1,910.57 ns | 1,921.80 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryReadTo | Job-VDEYSP | feature | 128 | 168.54 ns | 0.508 ns | 0.450 ns | 168.56 ns | 167.83 ns | 169.15 ns | 1.33 | Same | 0.02 | - | NA |
| SystemTryReadTo | Job-JFPICV | main | 128 | 126.69 ns | 1.687 ns | 1.578 ns | 126.93 ns | 123.68 ns | 129.13 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryReadTo | Job-VDEYSP | feature | 1024 | 87.80 ns | 0.252 ns | 0.236 ns | 87.79 ns | 87.50 ns | 88.17 ns | 1.36 | Same | 0.01 | - | NA |
| SystemTryReadTo | Job-JFPICV | main | 1024 | 64.73 ns | 0.420 ns | 0.392 ns | 64.69 ns | 64.24 ns | 65.47 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryRead | Job-VDEYSP | feature | -1024 | 446.80 ns | 1.273 ns | 1.128 ns | 446.39 ns | 445.46 ns | 448.88 ns | 0.49 | Same | 0.00 | - | NA |
| SystemTryRead | Job-JFPICV | main | -1024 | 906.08 ns | 2.883 ns | 2.556 ns | 905.21 ns | 903.20 ns | 912.33 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryRead | Job-VDEYSP | feature | 4 | 1,739.64 ns | 4.863 ns | 4.311 ns | 1,739.64 ns | 1,732.15 ns | 1,747.22 ns | 1.08 | Same | 0.00 | - | NA |
| SystemTryRead | Job-JFPICV | main | 4 | 1,609.45 ns | 4.496 ns | 3.986 ns | 1,609.09 ns | 1,603.75 ns | 1,615.48 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryRead | Job-VDEYSP | feature | 128 | 535.58 ns | 8.214 ns | 6.859 ns | 539.07 ns | 520.98 ns | 543.78 ns | 0.56 | Same | 0.01 | - | NA |
| SystemTryRead | Job-JFPICV | main | 128 | 962.53 ns | 2.895 ns | 2.708 ns | 962.00 ns | 958.22 ns | 967.11 ns | 1.00 | Base | 0.00 | - | NA |
| | | | | | | | | | | | | | | |
| SystemTryRead | Job-VDEYSP | feature | 1024 | 457.04 ns | 3.138 ns | 2.620 ns | 457.66 ns | 453.96 ns | 462.61 ns | 0.50 | Same | 0.01 | - | NA |
| SystemTryRead | Job-JFPICV | main | 1024 | 911.87 ns | 14.463 ns | 13.529 ns | 906.81 ns | 897.46 ns | 936.71 ns | 1.00 | Base | 0.00 | - | NA |

@jozkeejozkee modified the milestones: 8.0.0, 9.0.0Aug 14, 2023
@jozkee
jozkeeforce-pushed the marc/sequencereader branch from f32fd66 to e825679CompareAugust 17, 2023 15:14
@jozkee

Copy link
Copy Markdown
Member

@EgorBo@tannergooding I'm trying to understand the reason of a regression (modified in this PR) on SequenceReader.TryRead when the segments are small i.e: the longer the loop, the bigger the regression. Any advice you could give here of which may be causing it?

The benchmark in question:

[Benchmark]publicintSystemTryRead(){varreader=newSystem.Buffers.SequenceReader<int>(payload);intcount=0;while(reader.TryRead(out_)){count++;}returncount;}

New TryRead (inlined in SystemTryRead benchmark)

Disassembly:
; System.Memory.SequenceReaderBenchmark.SystemTryRead()pushr15pushr14pushr13pushrdipushrsipushrbppushrbxsubrsp,0A0vzeroupper vxorps xmm4,xmm4,xmm4 vmovdqa xmmword ptr [rsp+20],xmm4 vmovdqa xmmword ptr [rsp+30],xmm4movrax,0FFFFFFFFFFFFFFA0M00_L00: vmovdqa xmmword ptr [rsp+rax+0A0],xmm4 vmovdqa xmmword ptr [rsp+rax+0B0],xmm4 vmovdqa xmmword ptr [rsp+rax+0C0],xmm4addrax,30jne short M00_L00 vmovdqu xmm0,xmmword ptr [rcx+10] vmovdqu xmmword ptr [rsp+40],xmm0movrdx,[rcx+20]mov[rsp+50],rdx vmovdqu xmm0,xmmword ptr [rsp+40] vmovdqu xmmword ptr [rsp+78],xmm0movrcx,[rsp+50]mov[rsp+88],rcxmovrcx,[rsp+40]movedx,[rsp+50]andedx,7FFFFFFFmov[rsp+58],rcxmov[rsp+70],edxmovr8,[rsp+40]testr8,r8je near ptr M00_L17movebx,[rsp+50]movesi,[rsp+54]xoredi,edicmpr8,[rsp+48] setne dilmovecx,ebxorecx,esijl near ptr M00_L15movrax,r8movrcx,offset MT_System.Buffers.ReadOnlySequenceSegment`1[[System.Int32, System.Private.CoreLib]]cmp[rax],rcxjne near ptr M00_L10M00_L01:movrcx,[rax+18]movebp,[rax+20]movr14d,[rax+24]xorr15d,r15dxorr13d,r13dtestrcx,rcxje short M00_L03movrax,[rcx]test dword ptr [rax],80000000je near ptr M00_L11lear15,[rcx+10]movr13d,[rcx+8]M00_L02:andebp,7FFFFFFFmovecx,ebpmoveax,r14daddrax,rcxmovedx,r13dcmprax,rdxja near ptr M00_L13lear15,[r15+rcx*4]movr13d,r14dM00_L03:testedi,edije near ptr M00_L12cmpebx,r13dja near ptr M00_L13movecx,ebxlearcx,[r15+rcx*4]subr13d,ebxmov[rsp+30],rcxmov[rsp+38],r13dM00_L04: vmovdqu xmm0,xmmword ptr [rsp+30] vmovdqu xmmword ptr [rsp+90],xmm0xorecx,ecxmov[rsp+74],ecxmov[rsp+68],rcxmovrcx,[rsp+40]cmprcx,[rsp+48]je near ptr M00_L18mov qword ptr [rsp+60],0FFFFFFFFFFFFFFFFcmp dword ptr [rsp+98],0je near ptr M00_L16M00_L05:xoredi,ediM00_L06:movecx,[rsp+74]movrax,[rsp+90]movedx,[rsp+98]cmpecx,edxjae short M00_L08movr8d,ecxmoveax,[rax+r8*4]incecxmov[rsp+74],ecxcmpecx,edxje short M00_L09M00_L07:incedijmp short M00_L06M00_L08:moveax,ediaddrsp,0A0poprbxpoprbppoprsipoprdipopr13popr14popr15retM00_L09:learcx,[rsp+58]call qword ptr [7FFB2D246100]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].TryGetNextSpan()jmp short M00_L07M00_L10:movrdx,r8call qword ptr [7FFB2CD343F0]; System.Runtime.CompilerServices.CastHelpers.ChkCastClassSpecial(Void*, System.Object)jmp near ptr M00_L01M00_L11:leardx,[rsp+20]movrax,[rcx]movrax,[rax+40]call qword ptr [rax+28]movr15,[rsp+20]movr13d,[rsp+28]jmp near ptr M00_L02M00_L12:subesi,ebxmoveax,ebxmovecx,esiaddrax,rcxmovecx,r13dcmprax,rcxjbe short M00_L14M00_L13:call qword ptr [7FFB2D0657B8]int3M00_L14:movecx,ebxlearcx,[r15+rcx*4]mov[rsp+30],rcxmov[rsp+38],esijmp near ptr M00_L04M00_L15:learcx,[rsp+40]leardx,[rsp+30]movr9d,edicall qword ptr [7FFB2D0E4300]jmp near ptr M00_L04M00_L16:learcx,[rsp+58]call qword ptr [7FFB2D246100]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].TryGetNextSpan()jmp near ptr M00_L05M00_L17: vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+30],xmm0jmp near ptr M00_L04M00_L18:movsxdrcx,dword ptr [rsp+98]mov[rsp+60],rcxjmp near ptr M00_L05; Total bytes of code 632
; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].TryGetNextSpan()pushr15pushr14pushr13pushr12pushrdipushrsipushrbppushrbxsubrsp,38xoreax,eaxmov[rsp+28],raxmovrbx,rcxmovesi,[rbx+40]leardx,[rbx+20]movrcx,rbxleardi,[rcx+38]movrbp,[rdx+8]movr14d,[rdx+14]andr14d,7FFFFFFFmovr15d,1movrdx,[rcx]movrcx,offset MT_System.Buffers.ReadOnlySequenceSegment`1[[System.Int32, System.Private.CoreLib]]call qword ptr [7FFB2CD34360]; System.Runtime.CompilerServices.CastHelpers.IsInstanceOfClass(Void*, System.Object)movr13,raxtestr13,r13je short M01_L04M01_L00:cmpr13,rbpje short M01_L04movr13,[r13+8]movrcx,rbxmovrdx,r13call CORINFO_HELP_CHECKED_ASSIGN_REFtestr13,r13je short M01_L04movrcx,[r13+18]movr12d,[r13+20]movr15d,[r13+24]xoreax,eaxxoredx,edxtestrcx,rcxje short M01_L02movrax,[rcx]test dword ptr [rax],80000000je near ptr M01_L11learax,[rcx+10]movedx,[rcx+8]M01_L01:andr12d,7FFFFFFFmovecx,r12dmovr8d,r15daddr8,rcxmovedx,edxcmpr8,rdxja near ptr M01_L09learax,[rax+rcx*4]movedx,r15dM01_L02:mov[rdi],raxmov[rdi+8],edxcmpr13,rbpje short M01_L07M01_L03:cmp dword ptr [rdi+8],0je short M01_L08xorr15d,r15dM01_L04:testr15d,r15djne short M01_L05xoreax,eaxmov[rbx+1C],eaxmov[rbx+18],eaxmovsxdrax,esiadd[rbx+10],raxmoveax,1addrsp,38poprbxpoprbppoprsipoprdipopr12popr13popr14popr15retM01_L05:cmpr15d,2je short M01_L10mov[rbx+1C],esiM01_L06:xoreax,eaxaddrsp,38poprbxpoprbppoprsipoprdipopr12popr13popr14popr15retM01_L07:moveax,r14dcmpeax,[rdi+8]ja short M01_L09movrax,[rdi]mov[rdi],raxmov[rdi+8],r14djmp short M01_L03M01_L08:movr15d,2jmp near ptr M01_L00M01_L09:call qword ptr [7FFB2D0657B8]int3M01_L10:xoreax,eaxmov[rbx+1C],eaxmov[rbx+18],eaxmovsxdrax,esiadd[rbx+10],raxjmp short M01_L06M01_L11:leardx,[rsp+28]movrax,[rcx]movrax,[rax+40]call qword ptr [rax+28]movrax,[rsp+28]movedx,[rsp+30]jmp near ptr M01_L01; Total bytes of code 364

Old TryRead

Disassembly:
; System.Memory.SequenceReaderBenchmark.SystemTryRead()pushr15pushr14pushr13pushr12pushrdipushrsipushrbppushrbxsubrsp,0D8vzeroupperxoreax,eaxmov[rsp+38],rax vxorps xmm4,xmm4,xmm4movrax,0FFFFFFFFFFFFFF70M00_L00: vmovdqa xmmword ptr [rsp+rax+0D0],xmm4 vmovdqa xmmword ptr [rsp+rax+0E0],xmm4 vmovdqa xmmword ptr [rsp+rax+0F0],xmm4addrax,30jne short M00_L00mov[rsp+0D0],raxmovrbx,[rcx+10]movrsi,[rcx+18]movedi,[rcx+20]movebp,[rcx+24]mov[rsp+0B0],rbxmov[rsp+0B8],rsimov[rsp+0C0],edimov[rsp+0C4],ebpmovecx,ediandecx,7FFFFFFFmov[rsp+90],rbxmov[rsp+98],ecxmov qword ptr [rsp+78],0FFFFFFFFFFFFFFFF vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+0A0],xmm0movrcx,rbxtestrcx,rcxje near ptr M00_L16xorr14d,r14dcmprbx,rsi setne r14btestedi,edijl near ptr M00_L26testebp,ebpjl near ptr M00_L25movrcx,rbxmovrdx,offset MT_System.Buffers.ReadOnlySequenceSegment`1[[System.Int32, System.Private.CoreLib]]cmp[rcx],rdxjne near ptr M00_L04M00_L01:movrcx,rbxmovrdx,[rcx+18]movr15d,[rcx+20]movr13d,[rcx+24]xorr12d,r12dxorecx,ecxmov[rsp+30],rdxtestrdx,rdxje short M00_L03movrcx,[rdx]test dword ptr [rcx],80000000je near ptr M00_L23lear12,[rdx+10]movecx,[rdx+8]M00_L02:andr15d,7FFFFFFFmovedx,r15dmoveax,r13daddrax,rdxmovecx,ecxcmprax,rcxja near ptr M00_L21lear12,[r12+rdx*4]movecx,r13dM00_L03:mov[rsp+68],r12mov[rsp+70],ecxtestr14d,r14dje near ptr M00_L24cmpedi,[rsp+70]ja near ptr M00_L21movrcx,[rsp+68]movedx,edilearcx,[rcx+rdx*4]movedx,[rsp+70]subedx,edimov[rsp+68],rcxmov[rsp+70],edxmovrcx,[rbx+8]mov[rsp+0A0],rcxxorecx,ecxmov[rsp+0A8],ecxjmp near ptr M00_L16M00_L04:movrcx,rdxmovrdx,rbxcall qword ptr [7FFB2CD543F0]; System.Runtime.CompilerServices.CastHelpers.ChkCastClassSpecial(Void*, System.Object)jmp near ptr M00_L01M00_L05:incr15dM00_L06:cmp byte ptr [rsp+8C],0je near ptr M00_L22movrcx,[rsp+0C8]movedx,[rsp+0D0]moveax,[rsp+88]movr8d,eaxcmpr8d,edxjae near ptr M00_L34movedx,r8dmovecx,[rcx+rdx*4]inceaxmov[rsp+88],eaxmovrcx,[rsp+80]incrcxmov[rsp+80],rcxmovecx,[rsp+88]movedx,[rsp+0D0]cmpecx,edxjl short M00_L05movrcx,[rsp+0B0]movr13,[rsp+0B8]cmprcx,r13je near ptr M00_L18jmp short M00_L09M00_L07:moveax,1M00_L08:mov[rsp+0A0],rbpxoredx,edxmov[rsp+0A8],edxtesteax,eaxje near ptr M00_L18jmp near ptr M00_L14M00_L09:movr12,[rsp+0A0]movesi,[rsp+0A8]movebx,esimovr13,[rsp+0B8]movecx,[rsp+0C0]movedi,[rsp+0C4]movrdx,[rsp+0A0]xorebp,ebptestrdx,rdxje near ptr M00_L28sarecx,1Fmoveax,edisareax,1Fleaecx,[rax+rcx*2]moveax,ecxnegeaxandedi,7FFFFFFFtesteax,eaxjne near ptr M00_L30movrax,rdxmovrcx,offset MT_System.Buffers.ReadOnlySequenceSegment`1[[System.Int32, System.Private.CoreLib]]cmp[rax],rcxjne near ptr M00_L15M00_L10:cmprax,r13je near ptr M00_L29movrbp,[rax+8]testrbp,rbpje near ptr M00_L20movrcx,[rax+18]movr13d,[rax+20]movr14d,[rax+24]cmpesi,r14dja near ptr M00_L19addr13d,esisubr14d,esijmp near ptr M00_L07M00_L11:xoredx,edxxoreax,eaxtestrcx,rcxje short M00_L13movrdx,[rcx]test dword ptr [rdx],80000000je near ptr M00_L33leardx,[rcx+10]moveax,[rcx+8]M00_L12:andr13d,7FFFFFFFmovecx,r13dmovr8d,r14daddr8,rcxmoveax,eaxcmpr8,raxja near ptr M00_L21leardx,[rdx+rcx*4]moveax,r14dM00_L13:mov[rsp+0C8],rdxmov[rsp+0D0],eaxxoredx,edxmov[rsp+88],edxjmp near ptr M00_L05M00_L14:mov[rsp+90],r12mov[rsp+98],ebxtestr14d,r14djle near ptr M00_L32jmp short M00_L11M00_L15:call qword ptr [7FFB2CD543F0]; System.Runtime.CompilerServices.CastHelpers.ChkCastClassSpecial(Void*, System.Object)jmp near ptr M00_L10M00_L16: vmovdqu xmm0,xmmword ptr [rsp+68] vmovdqu xmmword ptr [rsp+0C8],xmm0xorecx,ecxcmp dword ptr [rsp+70],0 setg clmov[rsp+8C],clcmp byte ptr [rsp+8C],0je near ptr M00_L27M00_L17:xorr15d,r15djmp near ptr M00_L06M00_L18:mov byte ptr [rsp+8C],0jmp near ptr M00_L05M00_L19:movecx,21call qword ptr [7FFB2D085B18]int3M00_L20:call qword ptr [7FFB2D264F18]int3M00_L21:call qword ptr [7FFB2D0857B8]int3M00_L22:moveax,r15daddrsp,0D8poprbxpoprbppoprsipoprdipopr12popr13popr14popr15retM00_L23:leardx,[rsp+58]movrcx,[rsp+30]movrax,[rcx]movrax,[rax+40]call qword ptr [rax+28]movr12,[rsp+58]movecx,[rsp+60]jmp near ptr M00_L02M00_L24:subebp,edimovedx,edimovecx,ebpaddrdx,rcxmovecx,[rsp+70]cmprdx,rcxja short M00_L21movrdx,[rsp+68]movecx,edileardx,[rdx+rcx*4]mov[rsp+68],rdxmov[rsp+70],ebpjmp near ptr M00_L16M00_L25:testr14d,r14djne short M00_L20movrdx,rbxmovrcx,offset MT_System.Int32[]call qword ptr [7FFB2CD54390]movecx,ebpandecx,7FFFFFFFsubecx,edimovedx,edimovr8d,ecxaddrdx,r8movr8d,[rbx+8]cmprdx,r8ja near ptr M00_L21movedx,edileardx,[rbx+rdx*4+10]mov[rsp+68],rdxmov[rsp+70],ecxjmp near ptr M00_L16M00_L26:mov[rsp+20],r14dlearcx,[rsp+68]movrdx,rbxmovr8d,edimovr9d,ebpcall qword ptr [7FFB2D1044B0]jmp near ptr M00_L16M00_L27:cmprbx,rsije near ptr M00_L17mov byte ptr [rsp+8C],1learcx,[rsp+78]call qword ptr [7FFB2D266250]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].GetNextSpan()jmp near ptr M00_L17M00_L28:xorecx,ecxxorr13d,r13dxorr14d,r14dxoreax,eaxjmp near ptr M00_L08M00_L29:movrcx,[rax+18]movr13d,[rax+20]movr14d,[rax+24]subedi,esimovedx,esimoveax,ediaddrdx,raxmoveax,r14dcmprdx,raxja near ptr M00_L19addesi,r13dmovr13d,esimovr14d,edijmp near ptr M00_L07M00_L30:cmprdx,r13jne near ptr M00_L20cmpeax,1jne short M00_L31movrcx,offset MT_System.Int32[]call qword ptr [7FFB2CD54390]movr14d,edisubr14d,esimovedx,esimovecx,r14daddrdx,rcxmovecx,[rax+8]cmprdx,rcxja near ptr M00_L21movrcx,raxmovr13d,esijmp near ptr M00_L07M00_L31:movrcx,offset MT_System.Buffers.MemoryManager`1[[System.Int32, System.Private.CoreLib]]call qword ptr [7FFB2CD543D8]; System.Runtime.CompilerServices.CastHelpers.ChkCastClass(Void*, System.Object)movrcx,raxleardx,[rsp+48]movrax,[rax]movrax,[rax+40]call qword ptr [rax+20]movr14d,edisubr14d,esimovedx,esimovecx,r14daddrdx,rcxmovecx,[rsp+54]cmprdx,rcxja near ptr M00_L21movrcx,[rsp+48]movr13d,esiaddr13d,[rsp+50]jmp near ptr M00_L07M00_L32: vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+0C8],xmm0xoredx,edxmov[rsp+88],edxjmp near ptr M00_L09M00_L33:leardx,[rsp+38]movrax,[rcx]movrax,[rax+40]call qword ptr [rax+28]movrdx,[rsp+38]moveax,[rsp+40]jmp near ptr M00_L12M00_L34:call CORINFO_HELP_RNGCHKFAILint3; Total bytes of code 1477
; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].GetNextSpan()pushr15pushr14pushr13pushr12pushrdipushrsipushrbppushrbxsubrsp,48 vxorps xmm4,xmm4,xmm4 vmovdqa xmmword ptr [rsp+20],xmm4 vmovdqa xmmword ptr [rsp+30],xmm4movrbx,rcxmovrcx,[rbx+38]movrsi,[rbx+40]cmprcx,rsije near ptr M02_L07M02_L00:movrdx,[rbx+28]movrdi,rdxmovebp,[rbx+30]movrsi,[rbx+40]movecx,[rbx+48]movr14d,[rbx+4C]lear15,[rbx+28]xorr13d,r13dtestrdx,rdxje near ptr M02_L08sarecx,1Fmoveax,r14dsareax,1Fleaecx,[rax+rcx*2]movr12d,ecxnegr12dmoveax,[r15+8]mov[rsp+40],eaxandr14d,7FFFFFFFtestr12d,r12djne near ptr M02_L11movr12,rdxmovrcx,offset MT_System.Buffers.ReadOnlySequenceSegment`1[[System.Int32, System.Private.CoreLib]]cmp[r12],rcxjne near ptr M02_L06M02_L01:cmpr12,rsije near ptr M02_L10movr13,[r12+8]testr13,r13je near ptr M02_L14movrsi,[r12+18]movr14d,[r12+20]movr12d,[r12+24]moveax,[rsp+40]cmpeax,r12dja near ptr M02_L13addr14d,eaxsubr12d,eaxM02_L02:mov dword ptr [rsp+44],1M02_L03:movrcx,r15movrdx,r13call CORINFO_HELP_CHECKED_ASSIGN_REFxorecx,ecxmov[r15+8],ecxcmp dword ptr [rsp+44],0je near ptr M02_L07learcx,[rbx+18]movrdx,rdicall CORINFO_HELP_CHECKED_ASSIGN_REFmov[rbx+20],ebptestr12d,r12djle near ptr M02_L09xoredx,edxxorecx,ecxtestrsi,rsije short M02_L05movrdx,[rsi]test dword ptr [rdx],80000000je near ptr M02_L15leardx,[rsi+10]movecx,[rsi+8]M02_L04:andr14d,7FFFFFFFmoveax,r14dmovr8d,r12daddr8,raxmovecx,ecxcmpr8,rcxja near ptr M02_L16leardx,[rdx+rax*4]movecx,r12dM02_L05:mov[rbx+50],rdxmov[rbx+58],ecxxoredx,edxmov[rbx+10],edxaddrsp,48poprbxpoprbppoprsipoprdipopr12popr13popr14popr15retM02_L06:call qword ptr [7FFB2CD543F0]; System.Runtime.CompilerServices.CastHelpers.ChkCastClassSpecial(Void*, System.Object)movr12,raxjmp near ptr M02_L01M02_L07:mov byte ptr [rbx+14],0addrsp,48poprbxpoprbppoprsipoprdipopr12popr13popr14popr15retM02_L08:xoresi,esixorr14d,r14dxorr12d,r12dxoreax,eaxxoredx,edxmov[rsp+44],edxjmp near ptr M02_L03M02_L09:xorecx,ecxmov[rbx+50],rcxmov[rbx+58],rcxmov[rbx+10],ecxjmp near ptr M02_L00M02_L10:movrsi,[r12+18]movedx,[r12+20]movr12d,[r12+24]moveax,[rsp+40]subr14d,eaxmovecx,eaxmovr8d,r14daddrcx,r8movr8d,r12dcmprcx,r8ja near ptr M02_L13addeax,edxmovr12d,r14dmovr14d,eaxjmp near ptr M02_L02M02_L11:cmprdx,rsijne near ptr M02_L14cmpr12d,1jne short M02_L12movrcx,offset MT_System.Int32[]call qword ptr [7FFB2CD54390]movesi,[rsp+40]movr12d,r14dsubr12d,esimovedx,esimovecx,r12daddrdx,rcxmovecx,[rax+8]cmprdx,rcxja near ptr M02_L16movr14d,esimovrsi,raxjmp near ptr M02_L02M02_L12:movrcx,offset MT_System.Buffers.MemoryManager`1[[System.Int32, System.Private.CoreLib]]call qword ptr [7FFB2CD543D8]; System.Runtime.CompilerServices.CastHelpers.ChkCastClass(Void*, System.Object)movrcx,raxleardx,[rsp+30]movrax,[rax]movrax,[rax+40]call qword ptr [rax+20]movesi,[rsp+40]movr12d,r14dsubr12d,esimovecx,esimoveax,r12daddrcx,raxmoveax,[rsp+3C]cmprcx,raxja short M02_L16movrcx,[rsp+30]movr14d,esiaddr14d,[rsp+38]movrsi,rcxjmp near ptr M02_L02M02_L13:movecx,21call qword ptr [7FFB2D085B18]int3M02_L14:call qword ptr [7FFB2D264F18]int3M02_L15:leardx,[rsp+20]movrcx,rsimovrax,[rsi]movrax,[rax+40]call qword ptr [rax+28]movrdx,[rsp+20]movecx,[rsp+28]jmp near ptr M02_L04M02_L16:call qword ptr [7FFB2D0857B8]int3; Total bytes of code 706

@jozkee

Copy link
Copy Markdown
Member

TryReadTo is also affected with a 1.8x slowness.

[Benchmark]publicvoidSystemTryReadTo(){varreader=newSystem.Buffers.SequenceReader<int>(payload);if(reader.TryReadTo(outReadOnlySpan<int>_,255))Throw();}

New TryReadTo

Disassembly:
; System.Memory.SequenceReaderBenchmark.SystemTryReadTo()pushr15pushr14pushr13pushr12pushrdipushrsipushrbppushrbxsubrsp,198vzeroupperxoreax,eaxmov[rsp+28],rax vxorps xmm4,xmm4,xmm4 vmovdqa xmmword ptr [rsp+30],xmm4movrax,0FFFFFFFFFFFFFEB0M00_L00: vmovdqa xmmword ptr [rsp+rax+190],xmm4 vmovdqa xmmword ptr [rsp+rax+1A0],xmm4 vmovdqa xmmword ptr [rsp+rax+1B0],xmm4addrax,30jne short M00_L00mov[rsp+190],rax vmovdqu xmm0,xmmword ptr [rcx+10] vmovdqu xmmword ptr [rsp+128],xmm0movrdx,[rcx+20]mov[rsp+138],rdx vmovdqu xmm0,xmmword ptr [rsp+128] vmovdqu xmmword ptr [rsp+170],xmm0movrcx,[rsp+138]mov[rsp+180],rcxmovrcx,[rsp+128]movedx,[rsp+138]andedx,7FFFFFFFmov[rsp+150],rcxmov[rsp+168],edxmovr8,[rsp+128]testr8,r8je near ptr M00_L26movebx,[rsp+138]movebp,[rsp+13C]xorr14d,r14dcmpr8,[rsp+130] setne r14bmovecx,ebxorecx,ebpjl near ptr M00_L14movrax,r8movrcx,offset MT_System.Buffers.ReadOnlySequenceSegment`1[[System.Int32, System.Private.CoreLib]]cmp[rax],rcxjne near ptr M00_L11M00_L01:movrcx,[rax+18]movr15d,[rax+20]movr13d,[rax+24]xorr12d,r12dxorr8d,r8dtestrcx,rcxje short M00_L03movr8,[rcx]test dword ptr [r8],80000000je near ptr M00_L12lear12,[rcx+10]movr8d,[rcx+8]M00_L02:andr15d,7FFFFFFFmovecx,r15dmovedx,r13daddrdx,rcxmovr8d,r8dcmprdx,r8ja near ptr M00_L17lear12,[r12+rcx*4]movr8d,r13dM00_L03:testr14d,r14dje near ptr M00_L13cmpebx,r8dja near ptr M00_L17movecx,ebxlearcx,[r12+rcx*4]subr8d,ebxmov[rsp+118],rcxmov[rsp+120],r8dM00_L04: vmovdqu xmm0,xmmword ptr [rsp+118] vmovdqu xmmword ptr [rsp+188],xmm0xorecx,ecxmov[rsp+16C],ecxmov[rsp+160],rcxmovrcx,[rsp+128]cmprcx,[rsp+130]je near ptr M00_L27mov qword ptr [rsp+158],0FFFFFFFFFFFFFFFFcmp dword ptr [rsp+190],0je near ptr M00_L15M00_L05:movecx,[rsp+16C]movr15d,[rsp+190]cmpecx,r15dja near ptr M00_L17movr8,[rsp+188]movedx,ecxlear13,[r8+rdx*4]subr15d,ecxmovrcx,r13movr8d,r15dmovedx,0FFcall qword ptr [7FFB2D27CF18]; System.SpanHelpers.NonPackedIndexOfValueType[[System.Int32, System.Private.CoreLib],[System.SpanHelpers+DontNegate`1[[System.Int32, System.Private.CoreLib]], System.Private.CoreLib]](Int32 ByRef, Int32, Int32)cmpeax,0FFFFFFFFjne near ptr M00_L16movr14d,[rsp+190]subr14d,[rsp+16C]leardi,[rsp+88]learsi,[rsp+150]movecx,9repmovsqtestr14d,r14djle short M00_L06movsxdrdx,r14dlearcx,[rsp+150]call qword ptr [7FFB2D276FD0]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].Advance(Int64)M00_L06:learcx,[rsp+188]leardx,[rsp+78]movr8d,[rsp+16C]call qword ptr [7FFB2CD5FA98]; System.ReadOnlySpan`1[[System.Int32, System.Private.CoreLib]].Slice(Int32)learcx,[rsp+150]call qword ptr [7FFB2D276E20]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].get_End()testeax,eaxjne short M00_L08M00_L07: vmovdqu xmm0,xmmword ptr [rsp+78] vmovdqu xmmword ptr [rsp+48],xmm0learcx,[rsp+48]movedx,0FFcall qword ptr [7FFB2D27C300]; System.MemoryExtensions.IndexOf[[System.Int32, System.Private.CoreLib]](System.ReadOnlySpan`1<Int32>, Int32)cmpeax,0FFFFFFFFjne near ptr M00_L21movedx,[rsp+80]learcx,[rsp+150]call qword ptr [7FFB2D276FE8]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].AdvanceCurrentSpan(Int64) vmovdqu xmm0,xmmword ptr [rsp+188] vmovdqu xmmword ptr [rsp+78],xmm0learcx,[rsp+150]call qword ptr [7FFB2D276E20]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].get_End()testeax,eaxje short M00_L07M00_L08:leardi,[rsp+150]learsi,[rsp+88]movecx,9repmovsq vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+0F0],xmm0 vmovdqu xmmword ptr [rsp+0F8],xmm0 vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+140],xmm0xoresi,esiM00_L09:testesi,esijne near ptr M00_L25M00_L10:addrsp,198poprbxpoprbppoprsipoprdipopr12popr13popr14popr15retM00_L11:movrdx,r8call qword ptr [7FFB2CD543F0]; System.Runtime.CompilerServices.CastHelpers.ChkCastClassSpecial(Void*, System.Object)jmp near ptr M00_L01M00_L12:leardx,[rsp+108]movrax,[rcx]movrax,[rax+40]call qword ptr [rax+28]movr12,[rsp+108]movr8d,[rsp+110]jmp near ptr M00_L02M00_L13:subebp,ebxmovecx,ebxmovedx,ebpaddrcx,rdxmovedx,r8dcmprcx,rdxja short M00_L17movecx,ebxlearcx,[r12+rcx*4]mov[rsp+118],rcxmov[rsp+120],ebpjmp near ptr M00_L04M00_L14:learcx,[rsp+128]leardx,[rsp+118]movr9d,r14dcall qword ptr [7FFB2D0C50B0]jmp near ptr M00_L04M00_L15:learcx,[rsp+150]call qword ptr [7FFB2D276FA0]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].TryGetNextSpan()jmp near ptr M00_L05M00_L16:learbx,[rsp+140]testeax,eaxje short M00_L19cmpeax,r15djbe short M00_L18M00_L17:call qword ptr [7FFB2D0857B8]int3M00_L18:movecx,eaxjmp short M00_L20M00_L19:xorr13d,r13dxorecx,ecxM00_L20:mov[rbx],r13mov[rbx+8],ecxmovecx,[rsp+16C]leaecx,[rcx+rax+1]mov[rsp+16C],ecxmovecx,[rsp+16C]cmpecx,[rsp+190]jne near ptr M00_L25learcx,[rsp+150]call qword ptr [7FFB2D276FA0]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].TryGetNextSpan()jmp near ptr M00_L25M00_L21:testeax,eaxjle short M00_L22movsxdrdx,eaxlearcx,[rsp+150]call qword ptr [7FFB2D276FE8]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].AdvanceCurrentSpan(Int64)M00_L22:movrbx,[rsp+88]movesi,[rsp+0A0]addesi,[rsp+0A4]learcx,[rsp+150]leardx,[rsp+68]call qword ptr [7FFB2D276E68]mov[rsp+38],rbxmov[rsp+40],esi vmovdqu xmm0,xmmword ptr [rsp+68] vmovdqu xmmword ptr [rsp+28],xmm0lear8,[rsp+38]lear9,[rsp+28]learcx,[rsp+170]leardx,[rsp+0F0]call qword ptr [7FFB2D0C4F60]learcx,[rsp+150]movedx,1call qword ptr [7FFB2D276FD0]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].Advance(Int64)learbx,[rsp+140]movrcx,[rsp+0F0]cmprcx,[rsp+0F8]je short M00_L23learcx,[rsp+0F0]call qword ptr [7FFB2D27DE78]movrdx,rax vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+58],xmm0learcx,[rsp+58]call qword ptr [7FFB2CD5F870] vmovdqu xmm0,xmmword ptr [rsp+58] vmovdqu xmmword ptr [rsp+0D0],xmm0jmp short M00_L24M00_L23:learcx,[rsp+0F0]leardx,[rsp+0E0]call qword ptr [7FFB2D0C5068]learcx,[rsp+0E0]leardx,[rsp+0D0]call qword ptr [7FFB2D275B48]; System.ReadOnlyMemory`1[[System.Int32, System.Private.CoreLib]].get_Span()M00_L24:movrax,[rsp+0D0]mov[rbx],raxmoveax,[rsp+0D8]mov[rbx+8],eaxmovesi,1jmp near ptr M00_L09M00_L25:call qword ptr [7FFB2D0C5368]jmp near ptr M00_L10M00_L26: vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+118],xmm0jmp near ptr M00_L04M00_L27:movsxdrcx,dword ptr [rsp+190]mov[rsp+158],rcxjmp near ptr M00_L05; Total bytes of code 1407
; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].TryGetNextSpan()pushr15pushr14pushr13pushr12pushrdipushrsipushrbppushrbxsubrsp,38xoreax,eaxmov[rsp+28],raxmovrbx,rcxmovesi,[rbx+40]leardx,[rbx+20]movrcx,rbxleardi,[rcx+38]movrbp,[rdx+8]movr14d,[rdx+14]andr14d,7FFFFFFFmovr15d,1movrdx,[rcx]movrcx,offset MT_System.Buffers.ReadOnlySequenceSegment`1[[System.Int32, System.Private.CoreLib]]call qword ptr [7FFB2CD54360]; System.Runtime.CompilerServices.CastHelpers.IsInstanceOfClass(Void*, System.Object)movr13,raxtestr13,r13je short M08_L04M08_L00:cmpr13,rbpje short M08_L04movr13,[r13+8]movrcx,rbxmovrdx,r13call CORINFO_HELP_CHECKED_ASSIGN_REFtestr13,r13je short M08_L04movrcx,[r13+18]movr12d,[r13+20]movr15d,[r13+24]xoreax,eaxxoredx,edxtestrcx,rcxje short M08_L02movrax,[rcx]test dword ptr [rax],80000000je near ptr M08_L08learax,[rcx+10]movedx,[rcx+8]M08_L01:andr12d,7FFFFFFFmovecx,r12dmovr8d,r15daddr8,rcxmovedx,edxcmpr8,rdxja near ptr M08_L10learax,[rax+rcx*4]movedx,r15dM08_L02:mov[rdi],raxmov[rdi+8],edxcmpr13,rbpje short M08_L05M08_L03:cmp dword ptr [rdi+8],0je short M08_L09xorr15d,r15dM08_L04:testr15d,r15djne short M08_L06xoreax,eaxmov[rbx+1C],eaxmov[rbx+18],eaxmovsxdrax,esiadd[rbx+10],raxmoveax,1addrsp,38poprbxpoprbppoprsipoprdipopr12popr13popr14popr15retM08_L05:moveax,r14dcmpeax,[rdi+8]ja short M08_L10movrax,[rdi]mov[rdi],raxmov[rdi+8],r14djmp short M08_L03M08_L06:cmpr15d,2je short M08_L11mov[rbx+1C],esiM08_L07:xoreax,eaxaddrsp,38poprbxpoprbppoprsipoprdipopr12popr13popr14popr15retM08_L08:leardx,[rsp+28]movrax,[rcx]movrax,[rax+40]call qword ptr [rax+28]movrax,[rsp+28]movedx,[rsp+30]jmp near ptr M08_L01M08_L09:movr15d,2jmp near ptr M08_L00M08_L10:call qword ptr [7FFB2D0857B8]int3M08_L11:xoreax,eaxmov[rbx+1C],eaxmov[rbx+18],eaxmovsxdrax,esiadd[rbx+10],raxjmp short M08_L07; Total bytes of code 364

Old TryReadTo

Disassembly:
; System.Memory.SequenceReaderBenchmark.SystemTryReadTo()pushr15pushr14pushr13pushr12pushrdipushrsipushrbppushrbxsubrsp,208vzeroupper vxorps xmm4,xmm4,xmm4 vmovdqa xmmword ptr [rsp+30],xmm4 vmovdqa xmmword ptr [rsp+40],xmm4movrax,0FFFFFFFFFFFFFE50M00_L00: vmovdqa xmmword ptr [rsp+rax+200],xmm4 vmovdqa xmmword ptr [rsp+rax+210],xmm4 vmovdqa xmmword ptr [rsp+rax+220],xmm4addrax,30jne short M00_L00mov[rsp+200],raxmovrbx,[rcx+10]movrbp,[rcx+18]movr14d,[rcx+20]movr15d,[rcx+24]mov[rsp+1E0],rbxmov[rsp+1E8],rbpmov[rsp+1F0],r14dmov[rsp+1F4],r15dmovecx,r14dandecx,7FFFFFFFmov[rsp+1C0],rbxmov[rsp+1C8],ecxmov qword ptr [rsp+1A8],0FFFFFFFFFFFFFFFF vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+1D0],xmm0movrcx,rbxtestrcx,rcxje near ptr M00_L10xorr13d,r13dcmprbx,rbp setne r13btestr14d,r14djl near ptr M00_L33testr15d,r15djl near ptr M00_L31movrcx,rbxmovrdx,offset MT_System.Buffers.ReadOnlySequenceSegment`1[[System.Int32, System.Private.CoreLib]]cmp[rcx],rdxjne near ptr M00_L04M00_L01:movrcx,rbxmovr8,[rcx+18]movr12d,[rcx+20]movesi,[rcx+24]xorecx,ecxxoredx,edxtestr8,r8je short M00_L03movrcx,[r8]test dword ptr [rcx],80000000je near ptr M00_L29learcx,[r8+10]movedx,[r8+8]M00_L02:andr12d,7FFFFFFFmovr8d,r12dmoveax,esiaddrax,r8movedx,edxcmprax,rdxja near ptr M00_L21learcx,[rcx+r8*4]movedx,esiM00_L03:mov[rsp+188],rcxmov[rsp+190],edxtestr13d,r13dje near ptr M00_L30cmpr14d,[rsp+190]ja near ptr M00_L21movrcx,[rsp+188]movr8d,r14dlearcx,[rcx+r8*4]movr8d,[rsp+190]subr8d,r14dmov[rsp+188],rcxmov[rsp+190],r8dmovrcx,[rbx+8]mov[rsp+1D0],rcxxorecx,ecxmov[rsp+1D8],ecxjmp near ptr M00_L10M00_L04:movrcx,rdxmovrdx,rbxcall qword ptr [7FFB2CD243F0]; System.Runtime.CompilerServices.CastHelpers.ChkCastClassSpecial(Void*, System.Object)jmp near ptr M00_L01M00_L05:movedx,r8dlearcx,[rcx+rdx*4]subesi,r8dcmp byte ptr [rsp+1BC],0je short M00_L07M00_L06:movr8d,esimovedx,0FFcall qword ptr [7FFB2D24D7A0]; System.SpanHelpers.NonPackedIndexOfValueType[[System.Int32, System.Private.CoreLib],[System.SpanHelpers+DontNegate`1[[System.Int32, System.Private.CoreLib]], System.Private.CoreLib]](Int32 ByRef, Int32, Int32)cmpeax,0FFFFFFFFjne near ptr M00_L22movedx,esilearcx,[rsp+1A8]call qword ptr [7FFB2D247120]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].AdvanceCurrentSpan(Int64)movrcx,[rsp+1F8]movesi,[rsp+200]cmp byte ptr [rsp+1BC],0jne short M00_L06M00_L07:leardi,[rsp+1A8]learsi,[rsp+0E0]movecx,0Crepmovsq vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+160],xmm0 vmovdqu xmmword ptr [rsp+168],xmm0 vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+198],xmm0xoresi,esiM00_L08:testesi,esijne near ptr M00_L28M00_L09:addrsp,208poprbxpoprbppoprsipoprdipopr12popr13popr14popr15retM00_L10: vmovdqu xmm0,xmmword ptr [rsp+188] vmovdqu xmmword ptr [rsp+1F8],xmm0xorecx,ecxcmp dword ptr [rsp+190],0 setg clmov[rsp+1BC],clcmp byte ptr [rsp+1BC],0je near ptr M00_L34M00_L11:movrcx,[rsp+1F8]movr12d,[rsp+200]movr8d,[rsp+1B8]cmpr8d,r12dja near ptr M00_L21movedx,r8dlearsi,[rcx+rdx*4]subr12d,r8dmovrcx,rsimovr8d,r12dmovedx,0FFcall qword ptr [7FFB2D24D7A0]; System.SpanHelpers.NonPackedIndexOfValueType[[System.Int32, System.Private.CoreLib],[System.SpanHelpers+DontNegate`1[[System.Int32, System.Private.CoreLib]], System.Private.CoreLib]](Int32 ByRef, Int32, Int32)cmpeax,0FFFFFFFFjne near ptr M00_L35movr13d,[rsp+200]subr13d,[rsp+1B8]leardi,[rsp+0E0]learsi,[rsp+1A8]movecx,0Crepmovsqtestr13d,r13djle near ptr M00_L17movsxdr13,r13dtestr13,0FFFFFFFF80000000jne short M00_L12movecx,[rsp+200]subecx,[rsp+1B8]cmpecx,r13djg near ptr M00_L38M00_L12:testr13,r13jl near ptr M00_L39movrcx,r13addrcx,[rsp+1B0]mov[rsp+1B0],rcxcmp byte ptr [rsp+1BC],0je near ptr M00_L16M00_L13:movecx,[rsp+200]subecx,[rsp+1B8]movsxdrdx,ecxcmprdx,r13jg near ptr M00_L19movedx,ecxaddedx,[rsp+1B8]mov[rsp+1B8],edxmovsxdrcx,ecxsubr13,rcx vmovdqu xmm0,xmmword ptr [rsp+1E0] vmovdqu xmmword ptr [rsp+90],xmm0movrcx,[rsp+1F0]mov[rsp+0A0],rcxmovrcx,[rsp+90]cmprcx,[rsp+98]je near ptr M00_L18M00_L14:movrsi,[rsp+1D0]movedi,[rsp+1D8] vmovdqu xmm0,xmmword ptr [rsp+1E0] vmovdqu xmmword ptr [rsp+90],xmm0movrcx,[rsp+1F0]mov[rsp+0A0],rcxlearcx,[rsp+90]leardx,[rsp+1D0]lear8,[rsp+80]lear9,[rsp+60]call qword ptr [7FFB2D095050]; System.Buffers.ReadOnlySequence`1[[System.Int32, System.Private.CoreLib]].TryGetBuffer(System.SequencePosition ByRef, System.ReadOnlyMemory`1<Int32> ByRef, System.SequencePosition ByRef) vmovdqu xmm0,xmmword ptr [rsp+60] vmovdqu xmmword ptr [rsp+1D0],xmm0testeax,eaxje near ptr M00_L18mov[rsp+1C0],rsimov[rsp+1C8],edicmp dword ptr [rsp+8C],0jle near ptr M00_L40learcx,[rsp+80]leardx,[rsp+70]call qword ptr [7FFB2D245B48]; System.ReadOnlyMemory`1[[System.Int32, System.Private.CoreLib]].get_Span() vmovdqu xmm0,xmmword ptr [rsp+70] vmovdqu xmmword ptr [rsp+1F8],xmm0xorecx,ecxmov[rsp+1B8],ecxM00_L15:testr13,r13je short M00_L16cmp byte ptr [rsp+1BC],0jne near ptr M00_L13M00_L16:testr13,r13jne short M00_L20M00_L17:movrcx,[rsp+1F8]movesi,[rsp+200]movr8d,[rsp+1B8]cmpr8d,esija short M00_L21jmp near ptr M00_L05M00_L18:mov byte ptr [rsp+1BC],0jmp short M00_L15M00_L19:movecx,r13daddecx,[rsp+1B8]mov[rsp+1B8],ecxjmp short M00_L17M00_L20:movrcx,[rsp+1B0]subrcx,r13mov[rsp+1B0],rcxmovecx,0Fcall qword ptr [7FFB2D245D58]int3M00_L21:call qword ptr [7FFB2D0557B8]int3M00_L22:testeax,eaxjle short M00_L23movsxdrcx,eaxmovrax,rcxaddrax,[rsp+1B0]mov[rsp+1B0],raxaddecx,[rsp+1B8]mov[rsp+1B8],ecxmovecx,[rsp+1B8]moveax,[rsp+200]cmpecx,eaxjl short M00_L23learcx,[rsp+1A8]call qword ptr [7FFB2D2470F0]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].GetNextSpan()M00_L23: vmovdqu xmm0,xmmword ptr [rsp+1E0] vmovdqu xmmword ptr [rsp+0C8],xmm0movrcx,[rsp+1F0]mov[rsp+0D8],rcxlearcx,[rsp+0E0]leardx,[rsp+0B8]call qword ptr [7FFB2D246F70]learcx,[rsp+1A8]leardx,[rsp+0A8]call qword ptr [7FFB2D246F70] vmovdqu xmm0,xmmword ptr [rsp+0B8] vmovdqu xmmword ptr [rsp+40],xmm0 vmovdqu xmm0,xmmword ptr [rsp+0A8] vmovdqu xmmword ptr [rsp+30],xmm0lear8,[rsp+40]lear9,[rsp+30]learcx,[rsp+0C8]leardx,[rsp+160]call qword ptr [7FFB2D094F60]movecx,[rsp+200]subecx,[rsp+1B8]cmpecx,1jle short M00_L24movecx,[rsp+1B8]incecxmov[rsp+1B8],ecxmovrcx,[rsp+1B0]incrcxmov[rsp+1B0],rcxjmp short M00_L25M00_L24:learcx,[rsp+1A8]movedx,1call qword ptr [7FFB2D247138]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].AdvanceToNextSpan(Int64)M00_L25:learbx,[rsp+198]movrcx,[rsp+160]cmprcx,[rsp+168]je short M00_L26learcx,[rsp+160]call qword ptr [7FFB2D24E700]movrdx,rax vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+50],xmm0learcx,[rsp+50]call qword ptr [7FFB2CD2F870] vmovdqu xmm0,xmmword ptr [rsp+50] vmovdqu xmmword ptr [rsp+140],xmm0jmp short M00_L27M00_L26:learcx,[rsp+160]leardx,[rsp+150]call qword ptr [7FFB2D095068]learcx,[rsp+150]leardx,[rsp+140]call qword ptr [7FFB2D245B48]; System.ReadOnlyMemory`1[[System.Int32, System.Private.CoreLib]].get_Span()M00_L27:movrax,[rsp+140]mov[rbx],raxmoveax,[rsp+148]mov[rbx+8],eaxmovesi,1jmp near ptr M00_L08M00_L28:call qword ptr [7FFB2D095368]jmp near ptr M00_L09M00_L29:leardx,[rsp+178]movrcx,r8movrax,[r8]movrax,[rax+40]call qword ptr [rax+28]movrcx,[rsp+178]movedx,[rsp+180]jmp near ptr M00_L02M00_L30:subr15d,r14dmoveax,r14dmovecx,r15daddrax,rcxmovecx,[rsp+190]cmprax,rcxja near ptr M00_L21movrax,[rsp+188]movecx,r14dlearax,[rax+rcx*4]mov[rsp+188],raxmov[rsp+190],r15djmp near ptr M00_L10M00_L31:testr13d,r13dje short M00_L32call qword ptr [7FFB2D245DB8]int3M00_L32:movrdx,rbxmovrcx,offset MT_System.Int32[]call qword ptr [7FFB2CD24390]movecx,r15dandecx,7FFFFFFFsubecx,r14dmovedx,r14dmovr8d,ecxaddrdx,r8movr8d,[rbx+8]cmprdx,r8ja near ptr M00_L21movedx,r14dleardx,[rbx+rdx*4+10]mov[rsp+188],rdxmov[rsp+190],ecxjmp near ptr M00_L10M00_L33:mov[rsp+20],r13dlearcx,[rsp+188]movrdx,rbxmovr8d,r14dmovr9d,r15dcall qword ptr [7FFB2D095260]jmp near ptr M00_L10M00_L34:cmprbx,rbpje near ptr M00_L11mov byte ptr [rsp+1BC],1learcx,[rsp+1A8]call qword ptr [7FFB2D2470F0]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].GetNextSpan()jmp near ptr M00_L11M00_L35:learcx,[rsp+198]testeax,eaxje short M00_L36cmpeax,r12dja near ptr M00_L21movedx,eaxjmp short M00_L37M00_L36:xoresi,esixoredx,edxM00_L37:mov[rcx],rsimov[rcx+8],edxinceaxmovsxdrcx,eaxmovrax,rcxaddrax,[rsp+1B0]mov[rsp+1B0],raxaddecx,[rsp+1B8]mov[rsp+1B8],ecxmovecx,[rsp+1B8]moveax,[rsp+200]cmpecx,eaxjl near ptr M00_L28learcx,[rsp+1A8]call qword ptr [7FFB2D2470F0]; System.Buffers.SequenceReader`1[[System.Int32, System.Private.CoreLib]].GetNextSpan()jmp near ptr M00_L28M00_L38:movecx,r13daddecx,[rsp+1B8]mov[rsp+1B8],ecxmovrcx,r13addrcx,[rsp+1B0]mov[rsp+1B0],rcxjmp near ptr M00_L17M00_L39:movecx,0Fcall qword ptr [7FFB2D245D58]int3M00_L40: vxorps xmm0,xmm0,xmm0 vmovdqu xmmword ptr [rsp+1F8],xmm0xorecx,ecxmov[rsp+1B8],ecxjmp near ptr M00_L14; Total bytes of code 2191
; System.Buffers.ReadOnlySequence`1[[System.Int32, System.Private.CoreLib]].TryGetBuffer(System.SequencePosition ByRef, System.ReadOnlyMemory`1<Int32> ByRef, System.SequencePosition ByRef)pushr15pushr14pushrdipushrsipushrbppushrbxsubrsp,38xoreax,eaxmov[rsp+28],raxmovrbx,r8movrsi,r9movr8,[rdx]xoreax,eaxmov[rsi],raxmov[rsi+8],raxtestr8,r8je near ptr M04_L03moveax,[rcx+10]sareax,1Fmovedi,[rcx+14]movr10d,edisarr10d,1Fleaeax,[r10+rax*2]movebp,eaxnegebpmovr14,[rcx+8]movr15d,[rdx+8]andedi,7FFFFFFFtestebp,ebpjne near ptr M04_L05movrbp,r8movrcx,offset MT_System.Buffers.ReadOnlySequenceSegment`1[[System.Int32, System.Private.CoreLib]]cmp[rbp],rcxjne short M04_L02M04_L00:cmprbp,r14je short M04_L04movrdx,[rbp+8]testrdx,rdxje near ptr M04_L06movrcx,rsicall CORINFO_HELP_CHECKED_ASSIGN_REFxorecx,ecxmov[rsi+8],ecxmovrdx,[rbp+18]movr14d,[rbp+20]movesi,[rbp+24]cmpr15d,esija near ptr M04_L11addr14d,r15dsubesi,r15dmovrcx,rbxcall CORINFO_HELP_CHECKED_ASSIGN_REFmov[rbx+8],r14dmov[rbx+0C],esiM04_L01:moveax,1addrsp,38poprbxpoprbppoprsipoprdipopr14popr15retM04_L02:movrdx,r8call qword ptr [7FFB2CD243F0]; System.Runtime.CompilerServices.CastHelpers.ChkCastClassSpecial(Void*, System.Object)movrbp,raxjmp short M04_L00M04_L03:xoreax,eaxmov[rbx],raxmov[rbx+8],raxaddrsp,38poprbxpoprbppoprsipoprdipopr14popr15retM04_L04:movrdx,[rbp+18]movr14d,[rbp+20]movesi,[rbp+24]subedi,r15dmovecx,r15dmoveax,ediaddrcx,raxmoveax,esicmprcx,raxja near ptr M04_L11addr15d,r14dmovrcx,rbxcall CORINFO_HELP_CHECKED_ASSIGN_REFmov[rbx+8],r15dmov[rbx+0C],edijmp short M04_L01M04_L05:cmpr8,r14je short M04_L07M04_L06:call qword ptr [7FFB2D245DB8]int3M04_L07:cmpebp,1jne short M04_L08movrdx,r8movrcx,offset MT_System.Int32[]call qword ptr [7FFB2CD24390]movrdx,raxsubedi,r15dmovecx,r15dmoveax,ediaddrcx,raxmoveax,[rdx+8]cmprcx,raxja short M04_L09movrcx,rbxcall CORINFO_HELP_CHECKED_ASSIGN_REFmov[rbx+8],r15dmov[rbx+0C],edijmp near ptr M04_L01M04_L08:movrdx,r8movrcx,offset MT_System.Buffers.MemoryManager`1[[System.Int32, System.Private.CoreLib]]call qword ptr [7FFB2CD243D8]; System.Runtime.CompilerServices.CastHelpers.ChkCastClass(Void*, System.Object)movrcx,raxleardx,[rsp+28]movrax,[rax]movrax,[rax+40]call qword ptr [rax+20]movesi,edisubesi,r15dmoveax,r15dmovecx,esiaddrax,rcxmovecx,[rsp+34]cmprax,rcxjbe short M04_L10M04_L09:call qword ptr [7FFB2D0557B8]int3M04_L10:movrdx,[rsp+28]movedi,r15daddedi,[rsp+30]movrcx,rbxcall CORINFO_HELP_CHECKED_ASSIGN_REFmov[rbx+8],edimov[rbx+0C],esijmp near ptr M04_L01M04_L11:movecx,21call qword ptr [7FFB2D055B18]int3; Total bytes of code 477

@stephentoub

Copy link
Copy Markdown
Member

@jozkee, @mgravell, what are the next steps for this PR? Thanks.

@jozkee

Copy link
Copy Markdown
Member

@stephentoub we need to check if there's something that can be made about the TryRead and TryReadTo regressions; if not, evaluate if this a valuable trade-off.

@stephentoub

Copy link
Copy Markdown
Member

@jozkee, @mgravell, what's the plan for this? It's been sitting here for a long time. Are we going to take it or not?

@mgravell

Copy link
Copy Markdown
ContributorAuthor

I would suggest that if there's still scope for it, that we bump it to net10 - a bit late in the cycle for nuanced reader things; nobody seems massively stoked either way, though, so honestly: I'm also fine with just burning it. I can work with local similar types when needed

@stephentoubstephentoub modified the milestones: 9.0.0, 10.0.0Jul 22, 2024
@stephentoub

Copy link
Copy Markdown
Member

Ok, let's try to either land it or close it early in 10. Thanks.

@stephentoub

Copy link
Copy Markdown
Member

@mgravell, do you plan to continue working on this?

@jkotas
jkotas deleted the marc/sequencereader branch May 31, 2025 07:16
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 30, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Memorytenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@mgravell@stephentoub@adamsitnik@jozkee@tannergooding