- Notifications
You must be signed in to change notification settings - Fork5.2k
Reduce asm size of CollectionsMarshal.AsSpan#96773
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Conversation
Using the span constructor brings with it some branches and code we don't need due to knowledge of `List<T>`'s implementation, in particular that a) it always has an array (even if empty), and b) its array is invariant.Erroneous use of AsSpan while the list is being mutated could have also led to an ArgumentOutOfRangeException, which doesn't make much sense in this context. I've changed it to be an InvalidOperationException about the invalid concurrent use.
ghost commentedJan 10, 2024
Tagging subscribers to this area: @dotnet/interop-contrib Issue DetailsUsing the span constructor brings with it some branches and code we don't need due to knowledge of Erroneous use of AsSpan while the list is being mutated could have also led to an ArgumentOutOfRangeException from the span's constructor, which doesn't make much sense in this context. I've changed it to be an InvalidOperationException about the invalid concurrent use.
usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingSystem.Runtime.InteropServices;BenchmarkSwitcher.FromAssembly(typeof(Tests).Assembly).Run(args);[DisassemblyDiagnoser]publicclassTests{privatereadonlyList<string>_list=new(){"a","b","c"};[Benchmark]publicSpan<string>AsSpan()=>CollectionsMarshal.AsSpan(_list);[Benchmark]publicintSum(){inttotal=0;foreach(stringsinCollectionsMarshal.AsSpan(_list)){total+=s.Length;}returntotal;}}
|
jkotas commentedJan 10, 2024
Why is the JIT not able to optimize the original code well? cc @dotnet/jit-contrib |
stephentoub commentedJan 10, 2024
For starters I assume because it doesn't know that it can elide the null check on the array and elide the variance check on the array, since it's not privvy to the information about how the list was constructed. |
EgorBo commentedJan 10, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
For the variance check, minimal repro: publicclassMyList<T>{publicT[]arr;}boolTest(MyList<string>list)=>list.arr.GetType()==typeof(string[]); codegen: ; Method Program:Foo(MyList`1[System.String]):ubyte:this (FullOpts)G_M55733_IG01:G_M55733_IG02:movrax, gword ptr[rdx+0x08]movrcx,0xD1FFAB1E ; System.String[]cmp qword ptr[rax],rcx setealmovzxrax,alG_M55733_IG03:ret; Total bytes of code: 24 I bet it lost generic context somewhere during opts.. |
stephentoub commentedJan 10, 2024
Even if that's fixed for string, though, I imagine the JIT would have a lot more trouble doing it for an arbitrary non-sealed reference type. |
EgorBo commentedJan 10, 2024
Sure, but looks like it's should be a general goodness to improve this in JIT too if possible |
stephentoub commentedJan 10, 2024
Agreed. Just commenting that this change still improves upon what I imagine the JIT will be able to safely do, unless we build knowledge of |
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.csShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
jkotas commentedJan 10, 2024
Yea, I agree. LGTM. |
EgorBo commentedJan 10, 2024
Handled via#96794 |
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
…pServices/CollectionsMarshal.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
…pServices/CollectionsMarshal.csCo-authored-by: Jan Kotas <jkotas@microsoft.com>
* Reduce asm size of CollectionsMarshal.AsSpanUsing the span constructor brings with it some branches and code we don't need due to knowledge of `List<T>`'s implementation, in particular that a) it always has an array (even if empty), and b) its array is invariant.Erroneous use of AsSpan while the list is being mutated could have also led to an ArgumentOutOfRangeException, which doesn't make much sense in this context. I've changed it to be an InvalidOperationException about the invalid concurrent use.* Update src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs* Update src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.csCo-authored-by: Jan Kotas <jkotas@microsoft.com>---------Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Using the span constructor brings with it some branches and code we don't need due to knowledge of
List<T>'s implementation, in particular that a) it always has an array (even if empty), and b) its array is invariant.Erroneous use of AsSpan while the list is being mutated could have also led to an ArgumentOutOfRangeException from the span's constructor, which doesn't make much sense in this context. I've changed it to be an InvalidOperationException about the invalid concurrent use.