Skip to content

Fix perf regressions in Utf8Formatter for integers - #85277

Merged
stephentoub merged 1 commit into
dotnet:mainfrom
stephentoub:fixutf8formatterperf
Apr 25, 2023
Merged

Fix perf regressions in Utf8Formatter for integers#85277
stephentoub merged 1 commit into
dotnet:mainfrom
stephentoub:fixutf8formatterperf

Conversation

@stephentoub

Copy link
Copy Markdown
Member

When I added UTF8 support to the core numeric types, I also just routed Utf8Formatter to use the public TryFormat API on each type. That, however, regressed some microbenchmarks due to a) going from StandardFormat to a ReadOnlySpan<char> format and then parsing it back out and b) removing some of the inlining that was there previously. This change puts back into Utf8Formatter.TryFormat the handling of the format and then delegating to the relevant helpers that already exist rather than always going through the public entrypoint (it doesn't do so for 'n', but that's also much rarer to use on a hot path and is also in general more expensive).

privatebyte[]_buffer=newbyte[100];[Benchmark][Arguments(42)]publicboolTryFormatInt32_Default(intvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_);[Benchmark][Arguments(123L)]publicboolTryFormatInt64_Default(longvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_);[Benchmark][Arguments(12345UL)]publicboolTryFormatUInt64_Default(ulongvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_);[Benchmark][Arguments(42)]publicboolTryFormatInt32_D2(intvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_,newStandardFormat('D',2));[Benchmark][Arguments(0x1234UL)]publicboolTryFormatUInt64_X4(ulongvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_,newStandardFormat('X',4));[Benchmark][Arguments(long.MinValue)]publicboolTryFormatInt64_X(longvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_,newStandardFormat('X'));
MethodRuntimevalueMeanErrorStdDevRatioCode Size
TryFormatInt64_Xmain-922337203685477580829.905 ns0.6288 ns1.0154 ns1.512,545 B
TryFormatInt64_Xpr-922337203685477580813.156 ns0.2904 ns0.4607 ns0.67210 B
TryFormatInt64_X.NET 7.0-922337203685477580819.803 ns0.4241 ns0.6082 ns1.00294 B
TryFormatInt64_Defaultmain12310.610 ns0.1336 ns0.1250 ns1.62974 B
TryFormatInt64_Defaultpr1238.754 ns0.1467 ns0.1225 ns1.341,167 B
TryFormatInt64_Default.NET 7.01236.558 ns0.1536 ns0.1508 ns1.00829 B
TryFormatUInt64_Defaultmain1234510.376 ns0.2275 ns0.2337 ns1.41608 B
TryFormatUInt64_Defaultpr123455.027 ns0.0532 ns0.0498 ns0.68318 B
TryFormatUInt64_Default.NET 7.0123457.379 ns0.0931 ns0.0825 ns1.00458 B
TryFormatInt32_Defaultmain429.973 ns0.0832 ns0.0737 ns1.78964 B
TryFormatInt32_Defaultpr422.933 ns0.0472 ns0.0442 ns0.52348 B
TryFormatInt32_Default.NET 7.0425.609 ns0.0647 ns0.0574 ns1.00829 B
TryFormatInt32_D2main4220.623 ns0.1623 ns0.1518 ns3.262,438 B
TryFormatInt32_D2pr424.125 ns0.1098 ns0.0917 ns0.65573 B
TryFormatInt32_D2.NET 7.0426.340 ns0.0438 ns0.0366 ns1.00581 B
TryFormatUInt64_X4main466021.726 ns0.4386 ns0.3662 ns3.692,030 B
TryFormatUInt64_X4pr46606.411 ns0.0568 ns0.0474 ns1.09257 B
TryFormatUInt64_X4.NET 7.046605.841 ns0.1648 ns0.1897 ns1.00290 B

When I added UTF8 support to the core numeric types, I also just routed Utf8Formatter to use the public TryFormat API on each type. That, however, regressed some microbenchmarks due to a) going from `StandardFormat` to a `ReadOnlySpan<char>` format and then parsing it back out and b) removing some of the inlining that was there previously. This change puts back into Utf8Formatter.TryFormat the handling of the format and then delegating to the relevant helpers that already exist rather than always going through the public entrypoint (it doesn't do so for 'n', but that's also much rarer to use on a hot path and is also in general more expensive).
@stephentoubstephentoub added this to the 8.0.0 milestone Apr 24, 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

When I added UTF8 support to the core numeric types, I also just routed Utf8Formatter to use the public TryFormat API on each type. That, however, regressed some microbenchmarks due to a) going from StandardFormat to a ReadOnlySpan<char> format and then parsing it back out and b) removing some of the inlining that was there previously. This change puts back into Utf8Formatter.TryFormat the handling of the format and then delegating to the relevant helpers that already exist rather than always going through the public entrypoint (it doesn't do so for 'n', but that's also much rarer to use on a hot path and is also in general more expensive).

privatebyte[]_buffer=newbyte[100];[Benchmark][Arguments(42)]publicboolTryFormatInt32_Default(intvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_);[Benchmark][Arguments(123L)]publicboolTryFormatInt64_Default(longvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_);[Benchmark][Arguments(12345UL)]publicboolTryFormatUInt64_Default(ulongvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_);[Benchmark][Arguments(42)]publicboolTryFormatInt32_D2(intvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_,newStandardFormat('D',2));[Benchmark][Arguments(0x1234UL)]publicboolTryFormatUInt64_X4(ulongvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_,newStandardFormat('X',4));[Benchmark][Arguments(long.MinValue)]publicboolTryFormatInt64_X(longvalue)=>Utf8Formatter.TryFormat(value,_buffer,out_,newStandardFormat('X'));
MethodRuntimevalueMeanErrorStdDevRatioCode Size
TryFormatInt64_Xmain-922337203685477580829.905 ns0.6288 ns1.0154 ns1.512,545 B
TryFormatInt64_Xpr-922337203685477580813.156 ns0.2904 ns0.4607 ns0.67210 B
TryFormatInt64_X.NET 7.0-922337203685477580819.803 ns0.4241 ns0.6082 ns1.00294 B
TryFormatInt64_Defaultmain12310.610 ns0.1336 ns0.1250 ns1.62974 B
TryFormatInt64_Defaultpr1238.754 ns0.1467 ns0.1225 ns1.341,167 B
TryFormatInt64_Default.NET 7.01236.558 ns0.1536 ns0.1508 ns1.00829 B
TryFormatUInt64_Defaultmain1234510.376 ns0.2275 ns0.2337 ns1.41608 B
TryFormatUInt64_Defaultpr123455.027 ns0.0532 ns0.0498 ns0.68318 B
TryFormatUInt64_Default.NET 7.0123457.379 ns0.0931 ns0.0825 ns1.00458 B
TryFormatInt32_Defaultmain429.973 ns0.0832 ns0.0737 ns1.78964 B
TryFormatInt32_Defaultpr422.933 ns0.0472 ns0.0442 ns0.52348 B
TryFormatInt32_Default.NET 7.0425.609 ns0.0647 ns0.0574 ns1.00829 B
TryFormatInt32_D2main4220.623 ns0.1623 ns0.1518 ns3.262,438 B
TryFormatInt32_D2pr424.125 ns0.1098 ns0.0917 ns0.65573 B
TryFormatInt32_D2.NET 7.0426.340 ns0.0438 ns0.0366 ns1.00581 B
TryFormatUInt64_X4main466021.726 ns0.4386 ns0.3662 ns3.692,030 B
TryFormatUInt64_X4pr46606.411 ns0.0568 ns0.0474 ns1.09257 B
TryFormatUInt64_X4.NET 7.046605.841 ns0.1648 ns0.1897 ns1.00290 B
Author:stephentoub
Assignees:-
Labels:

area-System.Memory, tenet-performance

Milestone:8.0.0

@stephentoub
stephentoub merged commit 759fabe into dotnet:mainApr 25, 2023
@stephentoub
stephentoub deleted the fixutf8formatterperf branch April 25, 2023 02:51
@ghostghost locked as resolved and limited conversation to collaborators May 25, 2023
@DrewScoggins

Copy link
Copy Markdown
Member

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.

4 participants

@stephentoub@DrewScoggins@EgorBo@tannergooding