Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add goto
  • Loading branch information
hrrrrustic committed May 20, 2023
commit b8cb6dd43fe2a002f252cfd995d0c45028a5e6e2
14 changes: 10 additions & 4 deletions src/libraries/System.Private.CoreLib/src/System/Convert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2969,25 +2969,31 @@ public static bool TryFromHexString(string source, Span<byte> destination, out i

public static bool TryFromHexString(ReadOnlySpan<char> source, Span<byte> destination, out int bytesWritten)
{
bytesWritten = 0;
var length = source.Length;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var length = source.Length;
int length = source.Length;

Use concrete types here. Same on other places.

Perf-wise this could be nuint, so all the division and modulo operations are treated as (fast) bit-operations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perf-wise this could be nuint, so all the division and modulo operations are treated as (fast) bit-operations.

That's interesting. We rarely use native width types except in interop contexts. @jkotas @EgorBo is it correct that in hot code of this type, it can have perf benefit for simple storage of counters and offsets?

Copy link
Member

@EgorBo EgorBo May 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afair, JIT knows that Span.Length (and Array.Length as well) are never negative so it doesn't need extra efforts from the C# side 🙂 e.g.:

int Foo(Span<int> s) => s.Length % 4;
; Method Foo(System.Span`1[int]):int:this
       mov      eax, dword ptr [rdx+08H]
       and      eax, 3
       ret      
; Total bytes of code: 7

(some of the changes landed in .NET 8.0 so might be not visible on sharplab yet)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right -- I'm not aware of patterns where nuint gives better code.

Copy link
Member

@gfoidl gfoidl May 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JIT knows that Span.Length (and Array.Length as well) are never negative

Thanks for the reminder -- I'll forget sometimes about the new JIT-goodness.

We rarely use native width types except in interop contexts

Quite a lot of vectorized code uses the native width types, because memory operations don't need (sign-) extending moves then (e.g. Unsafe.Add(ref ptr, intVariable) vs. Unsafe.Add(ref ptr, nuintVariable) -- https://godbolt.org/z/v8E31Wdsx).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would Math.DivRem be more efficient here? Especially once it becomes an intrinsic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would Math.DivRem be more efficient here? Especially once it becomes an intrinsic.

I doubt it, since the JIT optimizes length % 2 != 0 to (length & 1) != 0.

Copy link
Member

@BrennanConroy BrennanConroy May 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That comment wasn't just about the single length check, but also the division done a few lines later. DivRem in theory combines the two operations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the division done a few lines later

The JIT can optimize (uint)length / 2 to (uint)length >> 1. On X86, DivRem would likely be implemented using a div instruction, which is an relatively expensive operation.


if (length % 2 != 0)
return false;
goto FalseResult;

if (length == 0)
{
bytesWritten = 0;
return true;
}

var twicedLength = length * 2;

if (destination.Length < twicedLength)
return false;
goto FalseResult;

if (!HexConverter.TryDecodeFromUtf16(source, destination))
return false;
goto FalseResult;

bytesWritten = twicedLength;
return true;

FalseResult:
bytesWritten = 0;
return false;
}

/// <summary>
Expand Down