Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Perf: UseSpan.CopyTo<Byte>() on .NET 7 and above.#2170

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

Open
jkrejcha wants to merge2 commits intoMessagePack-CSharp:master
base:master
Choose a base branch
Loading
fromjkrejcha:20250301-use-safe-span-methods-if-available

Conversation

@jkrejcha
Copy link

This changesUnsafeMemory to use the safe equivalents in .NET 7 and above as they are more performant than the equivalent code and can take advantage of vectorization on hardware that supports it

This optimization exists on at least .NET 7.

This changes `UnsafeMemory` to use the safe equivalents in .NET 7 and above as they are moreperformant than the equivalent code and can take advantage of vectorization on hardware that supports it
@jkrejchajkrejcha changed the titleUseSpan.CopyTo<Byte>() on .NET 7 and above.Perf: UseSpan.CopyTo<Byte>() on .NET 7 and above.Mar 2, 2025
@AArnott
Copy link
Collaborator

Thanks for this. But we don't need the#if I think. We only target .NET 8 at a minimum at this point, and .NET Standard 2.0 / .NET Framework,which also has these APIs through our reference to System.Memory.

Can you just replace the old code with the new code?

And then we should definitely get@neuecc's review (and maybe@pCYSl5EDgo) because they've done a lot of micro-optimization work in this library.

@AArnottAArnott requested a review fromneueccMarch 2, 2025 22:50
@jkrejcha
Copy link
Author

jkrejcha commentedMar 3, 2025
edited
Loading

Alright. It looks like .NET 7 and below doesn't have the optimization anyway potentially (it replaces with a call toBuffer.Memmove, indirection toGetSpan removed for sake of example).

Output on .NET 8

A.UnsafeMemory64:WriteRaw31_New(System.Span`1[ubyte],System.ReadOnlySpan`1[ubyte]) (FullOpts):       push     rbp       vzeroupper        mov      rbp, rsp       cmp      ecx, 31       jb       SHORT G_M17461_IG05       cmp      esi, 31       jb       SHORT G_M17461_IG06       vmovdqu  xmm0, xmmword ptr [rdx]       vmovdqu  xmm1, xmmword ptr [rdx+0x0F]       vmovdqu  xmmword ptr [rdi], xmm0       vmovdqu  xmmword ptr [rdi+0x0F], xmm1       pop      rbp       ret      G_M17461_IG05:  ;; offset=0x0025       call     [System.ThrowHelper:ThrowArgumentOutOfRangeException()]       int3     G_M17461_IG06:  ;; offset=0x002C       call     [System.ThrowHelper:ThrowArgumentException_DestinationTooShort()]       int3

vs. .NET 7

A.UnsafeMemory64:WriteRaw31_New(System.Span`1[ubyte],System.ReadOnlySpan`1[ubyte]):       push     rbp       mov      rbp, rsp       cmp      ecx, 31       jb       SHORT G_M17461_IG04       cmp      esi, 31       jb       SHORT G_M17461_IG05       mov      rsi, rdx       mov      edx, 31       call     [System.Buffer:Memmove(byref,byref,ulong)]       nop             pop      rbp       ret      G_M17461_IG04:       call     [System.ThrowHelper:ThrowArgumentOutOfRangeException()]       int3     G_M17461_IG05:       call     [System.ThrowHelper:ThrowArgumentException_DestinationTooShort()]       int3 ``` which still may be faster but hard to tell for sure without testing more thoroughly.

@AArnott
Copy link
Collaborator

Thanks. Two more thoughts.

So long as you're looking at native assembly emitted, can you look at .NET Framework? That's important too.

If this really is going to be the way to go, I guess we can get rid of all the many methods that you're changing too, as they were named and implemented specially for the length of the span, and that evidently isn't necessary any more since their implementations are now identical.

But feel free to wait for@neuecc's take before doing more work, since he may know something about why this isn't doable in the first place. Unity is another target runtime and it may be important there that the code remains as-is, for example.

@neuecc
Copy link
Member

This seems like a remnant from an era before Span existed.
I think the original original receivedbyte[].
In other words, I think the changes are fine.

@neuecc
Copy link
Member

neuecc commentedMar 4, 2025
edited
Loading

In the end, it goes to SpanHelpers.memmove?
https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs,36
The original goal is that if we know the length in advance, we can avoid branching.
The code size also becomes smaller, which should allow for inlining.
There's no room for vectorization when dealing with small data.
Instead of vectorizing, we are unrolling with a fixed approach
However, since memmove is intrinsic, would its behavior in .NET differ from the code's appearance?
It's difficult to immediately determine whether it's better than a handwritten implementation (although not needing "fixed" is good).

@neuecc
Copy link
Member

Minimal benchmarking shows that Span is better than the original.
However, manual fixed value optimisation using Unsafe.WriteUnaligned was even better.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@neueccneueccAwaiting requested review from neuecc

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

3 participants

@jkrejcha@AArnott@neuecc

[8]ページ先頭

©2009-2025 Movatter.jp