Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bytes -> Hex conversion update #21065

Merged
merged 1 commit into from Sep 23, 2023
Merged

Bytes -> Hex conversion update #21065

merged 1 commit into from Sep 23, 2023

Conversation

rudzen
Copy link
Contributor

@rudzen rudzen commented Sep 18, 2023

Bytes -> Hex conversion update

Hi!

I've updated the functionality which converts a byte array to a lower case hex string.

The performance is improved while still being compatible with the previous version.
For good measure I've added a few unit tests for SHA-1 hashing which uses the wiki examples.

Changes

  • Added new byte-to-hex compute functionality
  • Added sha-1 unit tests which computes hashes from wiki hash examples

Affects

Anything that computes a SHA1 hash through CryptoUtil.

Benchmarks

To get an idea of the scaling, I cooked up a little benchmark to test the changes when hashing with SHA-1.

Runs with data sizes of 32 and 128 data sizes.

BenchmarkDotNet v0.13.8, Windows 11 (10.0.22621.2283/22H2/2022Update/SunValley2)
Intel Core i7-8086K CPU 4.00GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores
.NET SDK 7.0.401
  [Host]     : .NET 6.0.22 (6.0.2223.42425), X64 RyuJIT AVX2
  DefaultJob : .NET 6.0.22 (6.0.2223.42425), X64 RyuJIT AVX2

| Method       | source    | Mean       | Error    | StdDev   | Ratio | RatioSD | Gen0   | Allocated | Alloc Ratio |
|------------- |---------- |-----------:|---------:|---------:|------:|--------:|-------:|----------:|------------:|
| New          | Byte[32]  |   572.1 ns |  3.98 ns |  3.53 ns |  1.00 |    0.00 | 0.0515 |     328 B |        1.00 |
| Mono         | Byte[32]  |   584.4 ns |  6.29 ns |  5.89 ns |  1.02 |    0.01 | 0.0687 |     432 B |        1.32 |
| Convert      | Byte[32]  |   584.6 ns |  7.32 ns |  6.49 ns |  1.02 |    0.01 | 0.0687 |     432 B |        1.32 |
| New          | Byte[128] |   763.4 ns |  6.32 ns |  5.60 ns |  1.33 |    0.01 | 0.0515 |     328 B |        1.00 |
| Convert      | Byte[128] |   779.6 ns |  6.37 ns |  5.65 ns |  1.36 |    0.01 | 0.0687 |     432 B |        1.32 |
| Mono         | Byte[128] |   788.1 ns |  7.79 ns |  7.29 ns |  1.38 |    0.01 | 0.0687 |     432 B |        1.32 |
| BitConverter | Byte[32]  |   952.5 ns |  6.68 ns |  5.92 ns |  1.66 |    0.02 | 0.0916 |     576 B |        1.76 |
| BitConverter | Byte[128] | 1,154.8 ns |  3.34 ns |  2.79 ns |  2.02 |    0.01 | 0.0916 |     576 B |        1.76 |
| Old          | Byte[32]  | 1,979.5 ns | 20.45 ns | 17.07 ns |  3.46 |    0.03 | 0.3357 |    2120 B |        6.46 |
| Old          | Byte[128] | 2,167.2 ns | 15.33 ns | 14.34 ns |  3.79 |    0.04 | 0.3357 |    2120 B |        6.46 |

@PunkPun
Copy link
Member

PunkPun commented Sep 18, 2023

👋

it seems that this solution is incompatible with the mono build

OpenRA.Game/CryptoUtil.cs(283,40): error CS0117: 'MemoryMarshal' does not contain a definition for 'GetArrayDataReference'

@RoosterDragon
Copy link
Member

You can guard with #if NET6_0_OR_GREATER and keep the previous implementation around for mono. Alternatively, implementing the loop without any unsafe might sidestep the problem and still be faster.

How does this method compare to Convert.ToHexString (which would need #if NET5_0_OR_GREATER) followed by a lowercase?

@rudzen
Copy link
Contributor Author

rudzen commented Sep 18, 2023

You can guard with #if NET6_0_OR_GREATER and keep the previous implementation around for mono. Alternatively, implementing the loop without any unsafe might sidestep the problem and still be faster.

How does this method compare to Convert.ToHexString (which would need #if NET5_0_OR_GREATER) followed by a lowercase?

Added an alternate implementation for mono using #if NET5_0_OR_GREATER, hopefully that should work.
I tried out some other variations, including the Convert.ToHexString() and also BitConverter.ToString() but they seem pretty cumbersome in comparison because of the lowercase and/or '-' removal.

Did not know I could bump to the #if NET6_0_OR_GREATER. Will keep that in mind :)

Copy link
Member

@RoosterDragon RoosterDragon left a comment

Choose a reason for hiding this comment

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

This reduces time spent in SHA1 hashing during initial load from ~1.4% to 1.2%, which is nice incremental win.

The downside for me is the maintenace burden here. We've added a fair bit of extra code, had to deal with an alternative implementation for mono and added unsafe code. For some larger overall gains I'd say that is worthwhile, but a fraction of a percent in the project overall doesn't sell it for me. Not your fault by any means, if the project was doing like 10-100x more hashing it'd be more tempting.

In this particular case I think you can get a more favourable balance by investigating Convert.ToHexString or BitConverter.ToString. Since they'd be no more complex than the current code, it'd be an easy sell even if the performance win isn't quite as good.

@PunkPun
Copy link
Member

PunkPun commented Sep 19, 2023

The only other place where we use x2 format is in color code. But it's not hotly used as well

@rudzen
Copy link
Contributor Author

rudzen commented Sep 20, 2023

This reduces time spent in SHA1 hashing during initial load from ~1.4% to 1.2%, which is nice incremental win.

The downside for me is the maintenace burden here. We've added a fair bit of extra code, had to deal with an alternative implementation for mono and added unsafe code. For some larger overall gains I'd say that is worthwhile, but a fraction of a percent in the project overall doesn't sell it for me. Not your fault by any means, if the project was doing like 10-100x more hashing it'd be more tempting.

In this particular case I think you can get a more favourable balance by investigating Convert.ToHexString or BitConverter.ToString. Since they'd be no more complex than the current code, it'd be an easy sell even if the performance win isn't quite as good.

Using Convert.ToHexString yields almost the same result (see updated benchmark table) and should hardly be noticeable 😄.

@PunkPun
Copy link
Member

PunkPun commented Sep 20, 2023

OpenRA/OpenRA.Game/CryptoUtil.cs(269,19): error CS0117: 'Convert' does not contain a definition for 'ToHexString'

it seems it's incompatible with mono 😩

@rudzen
Copy link
Contributor Author

rudzen commented Sep 20, 2023

OpenRA/OpenRA.Game/CryptoUtil.cs(269,19): error CS0117: 'Convert' does not contain a definition for 'ToHexString'

it seems it's incompatible with mono 😩

It would seem mono doesn't like me that much 😢.
Updated it anyways with a simple none-pre processor version that should work just fine.

@PunkPun
Copy link
Member

PunkPun commented Sep 21, 2023

LGTM, could you squash the commits?

added unit test

update ToHex(byte[]) to support mono

added punctuations to unit test summary and parameter description

Replaced with Convert.ToHexString(), public ToHex() + use from Color.ToString()

Adjusted back to a simpler mono compatible version only, with lowered allocation
@rudzen
Copy link
Contributor Author

rudzen commented Sep 22, 2023

LGTM, could you squash the commits?

👍🏻

@PunkPun PunkPun merged commit 7769764 into OpenRA:bleed Sep 23, 2023
3 checks passed
@PunkPun
Copy link
Member

PunkPun commented Sep 23, 2023

Chanelog

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants