Skip to content

Improve String.Substring performance - #62577

Closed
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf
Closed

Improve String.Substring performance#62577
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf

Conversation

@nietras

@nietrasnietras commented Dec 9, 2021

Copy link
Copy Markdown
Contributor

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@ghostghost added the community-contribution Indicates that the PR has been added by a community member label Dec 9, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

Using Span.Slice guard clauses for perf. Regressions I believe are due to benchmark issues. In any case, is changing guard clauses to this an acceptable change? Should exception messages be preserved?

// * Warnings *
MultimodalDistribution
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.56)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.89)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.75)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 3)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.78)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.83)
BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-ICQHYC : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-KPKVVD : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsilMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0815.3259 ns0.9471 ns1.0906 ns15.6270 ns11.1665 ns16.1263 ns1.000.000.002440 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz087.1677 ns0.3913 ns0.4506 ns7.2510 ns6.2912 ns7.9841 ns0.470.050.002440 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0?1.0918 ns0.0057 ns0.0051 ns1.0905 ns1.0861 ns1.1031 ns1.000.00--
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz0?0.4470 ns0.0059 ns0.0055 ns0.4444 ns0.4404 ns0.4585 ns0.410.01--
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz747.6604 ns0.3992 ns0.4597 ns7.7002 ns6.7318 ns8.2808 ns1.000.000.001932 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7413.2061 ns0.9380 ns1.0802 ns13.5553 ns11.1386 ns14.6399 ns1.730.210.001932 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz7?9.0171 ns1.6091 ns1.8531 ns9.2496 ns5.2419 ns12.1909 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7?14.7738 ns0.7346 ns0.8165 ns14.9451 ns11.5924 ns15.5339 ns1.730.440.002948 B
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10112.1967 ns1.0340 ns1.1908 ns12.3111 ns10.2499 ns14.3337 ns1.000.000.001424 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz1014.3119 ns0.0334 ns0.0279 ns4.3081 ns4.2731 ns4.3719 ns0.350.040.001424 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10?10.0238 ns0.5880 ns0.6772 ns10.1232 ns8.0144 ns10.7685 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz10?10.8206 ns0.7301 ns0.8407 ns10.7733 ns8.8957 ns11.9972 ns1.090.140.002948 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz19?10.1852 ns0.8323 ns0.9585 ns10.1999 ns8.4898 ns11.9423 ns1.000.000.001424 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz19?6.4746 ns0.4515 ns0.5200 ns6.2816 ns5.9014 ns7.5252 ns0.640.080.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B
Author:nietras
Assignees:-
Labels:

area-System.Runtime, community-contribution

Milestone:-

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@adamsitnikadamsitnik added the tenet-performance Performance related issue label Dec 10, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks tried to address feedback. Still don't know what to do about getting reliable benchmark tests. These do not show expected results currently but are also volatile. Substring(0) still faster of course.

Tried running StringReaderReadLineTests.ReadLine tests but no significant change there except [0, 0] case, so this PR does not appear to currently improve on that.

Let me know, what next steps you guys think should be.

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@nietras

Copy link
Copy Markdown
ContributorAuthor

Cleaned up. Benchmarks below, only significant improvements for when returning same string or empty. Regressions are often due to variance in benchmarks. Main point probably is throw code is separated out reducing code size of the method. I have included the cost of new'ing a similar string for comparison here.

Substring(int)

MethodJobToolchainStartMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADIm01.933 ns0.0043 ns0.0038 ns1.934 ns1.926 ns1.939 ns1.000.00----867 B
SubstringJob-WPCFEPpr01.283 ns0.0061 ns0.0057 ns1.282 ns1.275 ns1.294 ns0.660.00----471 B
SubstringJob-WNUADIm85.323 ns0.0295 ns0.0276 ns5.324 ns5.286 ns5.382 ns1.000.000.0024--40 B867 B
SubstringJob-WPCFEPpr85.906 ns0.0271 ns0.0240 ns5.895 ns5.875 ns5.947 ns1.110.010.0024--40 B471 B
NewStringJob-WNUADIm83.755 ns0.0320 ns0.0299 ns3.760 ns3.706 ns3.805 ns1.000.000.0024--40 B40 B
NewStringJob-WPCFEPpr83.758 ns0.0170 ns0.0151 ns3.760 ns3.730 ns3.782 ns1.000.010.0024--40 B40 B
SubstringJob-WNUADIm161.570 ns0.0473 ns0.0419 ns1.552 ns1.538 ns1.655 ns1.000.00----867 B
SubstringJob-WPCFEPpr161.578 ns0.0353 ns0.0330 ns1.594 ns1.511 ns1.614 ns1.000.03----471 B
NewStringJob-WNUADIm161.371 ns0.0218 ns0.0193 ns1.372 ns1.343 ns1.413 ns1.000.00----40 B
NewStringJob-WPCFEPpr161.296 ns0.0040 ns0.0036 ns1.296 ns1.290 ns1.302 ns0.950.01----40 B

Substring(int,int)

MethodJobToolchainSliceMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADImI: 0 L: 01.713 ns0.0042 ns0.0039 ns1.712 ns1.707 ns1.721 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L: 01.292 ns0.0063 ns0.0059 ns1.290 ns1.283 ns1.303 ns0.750.00----483 B
NewStringJob-WNUADImI: 0 L: 01.702 ns0.0046 ns0.0043 ns1.701 ns1.693 ns1.710 ns1.000.00----27 B
NewStringJob-WPCFEPprI: 0 L: 01.501 ns0.0034 ns0.0029 ns1.501 ns1.495 ns1.505 ns0.880.00----27 B
SubstringJob-WNUADImI: 0 L:161.923 ns0.0025 ns0.0022 ns1.924 ns1.918 ns1.926 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L:161.504 ns0.0027 ns0.0026 ns1.505 ns1.499 ns1.509 ns0.780.00----483 B
NewStringJob-WNUADImI: 0 L:164.252 ns0.0147 ns0.0130 ns4.255 ns4.218 ns4.268 ns1.000.000.0033--56 B27 B
NewStringJob-WPCFEPprI: 0 L:164.214 ns0.0461 ns0.0409 ns4.207 ns4.163 ns4.280 ns0.990.010.0033--56 B27 B
SubstringJob-WNUADImI: 1 L: 15.146 ns0.0528 ns0.0494 ns5.154 ns4.973 ns5.177 ns1.000.000.0014--24 B864 B
SubstringJob-WPCFEPprI: 1 L: 15.194 ns0.0180 ns0.0159 ns5.194 ns5.161 ns5.221 ns1.010.010.0014--24 B483 B
NewStringJob-WNUADImI: 1 L: 13.656 ns0.0179 ns0.0167 ns3.658 ns3.623 ns3.678 ns1.000.000.0014--24 B27 B
NewStringJob-WPCFEPprI: 1 L: 13.685 ns0.0186 ns0.0174 ns3.684 ns3.656 ns3.716 ns1.010.010.0014--24 B27 B
SubstringJob-WNUADImI: 2 L: 25.554 ns0.0214 ns0.0189 ns5.556 ns5.522 ns5.589 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 2 L: 25.375 ns0.0403 ns0.0377 ns5.368 ns5.322 ns5.445 ns0.970.010.0019--32 B483 B
NewStringJob-WNUADImI: 2 L: 23.842 ns0.0142 ns0.0133 ns3.842 ns3.815 ns3.868 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 2 L: 23.871 ns0.0179 ns0.0168 ns3.866 ns3.848 ns3.905 ns1.010.010.0019--32 B27 B
SubstringJob-WNUADImI: 3 L: 35.708 ns0.1507 ns0.1736 ns5.616 ns5.568 ns6.077 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 3 L: 35.395 ns0.0215 ns0.0179 ns5.399 ns5.346 ns5.413 ns0.940.030.0019--32 B483 B
NewStringJob-WNUADImI: 3 L: 33.859 ns0.0231 ns0.0205 ns3.856 ns3.835 ns3.907 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 3 L: 33.820 ns0.0243 ns0.0227 ns3.827 ns3.787 ns3.857 ns0.990.010.0019--32 B27 B
SubstringJob-WNUADImI: 4 L: 45.352 ns0.0226 ns0.0176 ns5.354 ns5.314 ns5.380 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 4 L: 413.276 ns0.2797 ns0.2872 ns13.330 ns12.226 ns13.534 ns2.470.060.0019--32 B483 B
NewStringJob-WNUADImI: 4 L: 43.825 ns0.0109 ns0.0102 ns3.827 ns3.807 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 4 L: 43.815 ns0.0236 ns0.0221 ns3.812 ns3.791 ns3.864 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 5 L: 55.334 ns0.0321 ns0.0300 ns5.327 ns5.295 ns5.385 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 5 L: 55.618 ns0.0410 ns0.0343 ns5.614 ns5.566 ns5.694 ns1.050.010.0019--32 B483 B
NewStringJob-WNUADImI: 5 L: 53.813 ns0.0179 ns0.0168 ns3.816 ns3.786 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 5 L: 53.801 ns0.0241 ns0.0213 ns3.802 ns3.771 ns3.836 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 6 L: 65.492 ns0.0333 ns0.0311 ns5.489 ns5.447 ns5.544 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 6 L: 65.679 ns0.0306 ns0.0286 ns5.675 ns5.640 ns5.724 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 6 L: 63.986 ns0.0096 ns0.0090 ns3.985 ns3.970 ns4.005 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 6 L: 63.958 ns0.0164 ns0.0146 ns3.957 ns3.939 ns3.984 ns0.990.000.0024--40 B27 B
SubstringJob-WNUADImI: 7 L: 75.511 ns0.0220 ns0.0205 ns5.513 ns5.476 ns5.545 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 7 L: 75.702 ns0.0298 ns0.0279 ns5.700 ns5.645 ns5.745 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 7 L: 73.853 ns0.0175 ns0.0164 ns3.854 ns3.825 ns3.880 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 7 L: 73.988 ns0.0207 ns0.0184 ns3.987 ns3.962 ns4.020 ns1.040.010.0024--40 B27 B
SubstringJob-WNUADImI: 8 L: 85.491 ns0.0296 ns0.0277 ns5.499 ns5.449 ns5.538 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 8 L: 85.680 ns0.0358 ns0.0335 ns5.685 ns5.623 ns5.742 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 8 L: 84.015 ns0.0120 ns0.0112 ns4.015 ns3.986 ns4.030 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 8 L: 83.971 ns0.0296 ns0.0277 ns3.972 ns3.929 ns4.018 ns0.990.010.0024--40 B27 B

ASM

M

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1B31ACA8048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrdi,rcxmovesi,edxtestesi,esijl near ptr M01_L03movebx,[rdi+8]cmpebx,esijl near ptr M01_L04testr8d,r8djl near ptr M01_L05movecx,ebxsubecx,r8dcmpecx,esijl near ptr M01_L06testr8d,r8dje short M01_L02testesi,esije short M01_L01M01_L00:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrdi,0Cmovedx,esileardx,[rdi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L01:cmpebx,r8djne short M01_L00movrax,rdiaddrsp,20poprbxpoprsipoprdiretM01_L02:movrax,1B31ACA3020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L03:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A855movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L04:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A891movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L05:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A519movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L06:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,9EBDmovrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWint3; Total bytes of code 512

PR

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1FA62268048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrsi,rcxmovedi,edxmovecx,[rsi+8]moveax,edimovedx,r8daddrax,rdxmovedx,ecxcmprax,rdxja short M01_L02testr8d,r8djne short M01_L00movrax,1FA62263020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L00:cmpr8d,ecxjne short M01_L01movrax,rsiaddrsp,20poprbxpoprsipoprdiretM01_L01:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrsi,0Cmovedx,edileardx,[rsi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L02:movrcx,rsimovedx,edicall System.String.ThrowSubstringArgumentOutRangeException(Int32, Int32)int3; Total bytes of code 131

@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks should I close this or?

@danmoseley

Copy link
Copy Markdown
Contributor

I'm going to close this as the improvements are as you say marginal. Thank you for the attempt!

@ghostghost locked as resolved and limited conversation to collaborators Jun 1, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtimecommunity-contributionIndicates that the PR has been added by a community membertenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@nietras@danmoseley@GrabYourPitchforks@jkotas@deeprobin@vcsjones@jeffhandley@adamsitnik
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Improve String.Substring performance by nietras · Pull Request #62577 · dotnet/runtime · GitHub
Skip to content

Improve String.Substring performance - #62577

Closed
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf
Closed

Improve String.Substring performance#62577
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf

Conversation

@nietras

@nietrasnietras commented Dec 9, 2021

Copy link
Copy Markdown
Contributor

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@ghostghost added the community-contribution Indicates that the PR has been added by a community member label Dec 9, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

Using Span.Slice guard clauses for perf. Regressions I believe are due to benchmark issues. In any case, is changing guard clauses to this an acceptable change? Should exception messages be preserved?

// * Warnings *
MultimodalDistribution
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.56)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.89)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.75)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 3)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.78)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.83)
BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-ICQHYC : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-KPKVVD : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsilMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0815.3259 ns0.9471 ns1.0906 ns15.6270 ns11.1665 ns16.1263 ns1.000.000.002440 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz087.1677 ns0.3913 ns0.4506 ns7.2510 ns6.2912 ns7.9841 ns0.470.050.002440 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0?1.0918 ns0.0057 ns0.0051 ns1.0905 ns1.0861 ns1.1031 ns1.000.00--
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz0?0.4470 ns0.0059 ns0.0055 ns0.4444 ns0.4404 ns0.4585 ns0.410.01--
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz747.6604 ns0.3992 ns0.4597 ns7.7002 ns6.7318 ns8.2808 ns1.000.000.001932 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7413.2061 ns0.9380 ns1.0802 ns13.5553 ns11.1386 ns14.6399 ns1.730.210.001932 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz7?9.0171 ns1.6091 ns1.8531 ns9.2496 ns5.2419 ns12.1909 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7?14.7738 ns0.7346 ns0.8165 ns14.9451 ns11.5924 ns15.5339 ns1.730.440.002948 B
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10112.1967 ns1.0340 ns1.1908 ns12.3111 ns10.2499 ns14.3337 ns1.000.000.001424 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz1014.3119 ns0.0334 ns0.0279 ns4.3081 ns4.2731 ns4.3719 ns0.350.040.001424 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10?10.0238 ns0.5880 ns0.6772 ns10.1232 ns8.0144 ns10.7685 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz10?10.8206 ns0.7301 ns0.8407 ns10.7733 ns8.8957 ns11.9972 ns1.090.140.002948 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz19?10.1852 ns0.8323 ns0.9585 ns10.1999 ns8.4898 ns11.9423 ns1.000.000.001424 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz19?6.4746 ns0.4515 ns0.5200 ns6.2816 ns5.9014 ns7.5252 ns0.640.080.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B
Author:nietras
Assignees:-
Labels:

area-System.Runtime, community-contribution

Milestone:-

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@adamsitnikadamsitnik added the tenet-performance Performance related issue label Dec 10, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks tried to address feedback. Still don't know what to do about getting reliable benchmark tests. These do not show expected results currently but are also volatile. Substring(0) still faster of course.

Tried running StringReaderReadLineTests.ReadLine tests but no significant change there except [0, 0] case, so this PR does not appear to currently improve on that.

Let me know, what next steps you guys think should be.

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@nietras

Copy link
Copy Markdown
ContributorAuthor

Cleaned up. Benchmarks below, only significant improvements for when returning same string or empty. Regressions are often due to variance in benchmarks. Main point probably is throw code is separated out reducing code size of the method. I have included the cost of new'ing a similar string for comparison here.

Substring(int)

MethodJobToolchainStartMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADIm01.933 ns0.0043 ns0.0038 ns1.934 ns1.926 ns1.939 ns1.000.00----867 B
SubstringJob-WPCFEPpr01.283 ns0.0061 ns0.0057 ns1.282 ns1.275 ns1.294 ns0.660.00----471 B
SubstringJob-WNUADIm85.323 ns0.0295 ns0.0276 ns5.324 ns5.286 ns5.382 ns1.000.000.0024--40 B867 B
SubstringJob-WPCFEPpr85.906 ns0.0271 ns0.0240 ns5.895 ns5.875 ns5.947 ns1.110.010.0024--40 B471 B
NewStringJob-WNUADIm83.755 ns0.0320 ns0.0299 ns3.760 ns3.706 ns3.805 ns1.000.000.0024--40 B40 B
NewStringJob-WPCFEPpr83.758 ns0.0170 ns0.0151 ns3.760 ns3.730 ns3.782 ns1.000.010.0024--40 B40 B
SubstringJob-WNUADIm161.570 ns0.0473 ns0.0419 ns1.552 ns1.538 ns1.655 ns1.000.00----867 B
SubstringJob-WPCFEPpr161.578 ns0.0353 ns0.0330 ns1.594 ns1.511 ns1.614 ns1.000.03----471 B
NewStringJob-WNUADIm161.371 ns0.0218 ns0.0193 ns1.372 ns1.343 ns1.413 ns1.000.00----40 B
NewStringJob-WPCFEPpr161.296 ns0.0040 ns0.0036 ns1.296 ns1.290 ns1.302 ns0.950.01----40 B

Substring(int,int)

MethodJobToolchainSliceMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADImI: 0 L: 01.713 ns0.0042 ns0.0039 ns1.712 ns1.707 ns1.721 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L: 01.292 ns0.0063 ns0.0059 ns1.290 ns1.283 ns1.303 ns0.750.00----483 B
NewStringJob-WNUADImI: 0 L: 01.702 ns0.0046 ns0.0043 ns1.701 ns1.693 ns1.710 ns1.000.00----27 B
NewStringJob-WPCFEPprI: 0 L: 01.501 ns0.0034 ns0.0029 ns1.501 ns1.495 ns1.505 ns0.880.00----27 B
SubstringJob-WNUADImI: 0 L:161.923 ns0.0025 ns0.0022 ns1.924 ns1.918 ns1.926 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L:161.504 ns0.0027 ns0.0026 ns1.505 ns1.499 ns1.509 ns0.780.00----483 B
NewStringJob-WNUADImI: 0 L:164.252 ns0.0147 ns0.0130 ns4.255 ns4.218 ns4.268 ns1.000.000.0033--56 B27 B
NewStringJob-WPCFEPprI: 0 L:164.214 ns0.0461 ns0.0409 ns4.207 ns4.163 ns4.280 ns0.990.010.0033--56 B27 B
SubstringJob-WNUADImI: 1 L: 15.146 ns0.0528 ns0.0494 ns5.154 ns4.973 ns5.177 ns1.000.000.0014--24 B864 B
SubstringJob-WPCFEPprI: 1 L: 15.194 ns0.0180 ns0.0159 ns5.194 ns5.161 ns5.221 ns1.010.010.0014--24 B483 B
NewStringJob-WNUADImI: 1 L: 13.656 ns0.0179 ns0.0167 ns3.658 ns3.623 ns3.678 ns1.000.000.0014--24 B27 B
NewStringJob-WPCFEPprI: 1 L: 13.685 ns0.0186 ns0.0174 ns3.684 ns3.656 ns3.716 ns1.010.010.0014--24 B27 B
SubstringJob-WNUADImI: 2 L: 25.554 ns0.0214 ns0.0189 ns5.556 ns5.522 ns5.589 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 2 L: 25.375 ns0.0403 ns0.0377 ns5.368 ns5.322 ns5.445 ns0.970.010.0019--32 B483 B
NewStringJob-WNUADImI: 2 L: 23.842 ns0.0142 ns0.0133 ns3.842 ns3.815 ns3.868 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 2 L: 23.871 ns0.0179 ns0.0168 ns3.866 ns3.848 ns3.905 ns1.010.010.0019--32 B27 B
SubstringJob-WNUADImI: 3 L: 35.708 ns0.1507 ns0.1736 ns5.616 ns5.568 ns6.077 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 3 L: 35.395 ns0.0215 ns0.0179 ns5.399 ns5.346 ns5.413 ns0.940.030.0019--32 B483 B
NewStringJob-WNUADImI: 3 L: 33.859 ns0.0231 ns0.0205 ns3.856 ns3.835 ns3.907 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 3 L: 33.820 ns0.0243 ns0.0227 ns3.827 ns3.787 ns3.857 ns0.990.010.0019--32 B27 B
SubstringJob-WNUADImI: 4 L: 45.352 ns0.0226 ns0.0176 ns5.354 ns5.314 ns5.380 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 4 L: 413.276 ns0.2797 ns0.2872 ns13.330 ns12.226 ns13.534 ns2.470.060.0019--32 B483 B
NewStringJob-WNUADImI: 4 L: 43.825 ns0.0109 ns0.0102 ns3.827 ns3.807 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 4 L: 43.815 ns0.0236 ns0.0221 ns3.812 ns3.791 ns3.864 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 5 L: 55.334 ns0.0321 ns0.0300 ns5.327 ns5.295 ns5.385 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 5 L: 55.618 ns0.0410 ns0.0343 ns5.614 ns5.566 ns5.694 ns1.050.010.0019--32 B483 B
NewStringJob-WNUADImI: 5 L: 53.813 ns0.0179 ns0.0168 ns3.816 ns3.786 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 5 L: 53.801 ns0.0241 ns0.0213 ns3.802 ns3.771 ns3.836 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 6 L: 65.492 ns0.0333 ns0.0311 ns5.489 ns5.447 ns5.544 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 6 L: 65.679 ns0.0306 ns0.0286 ns5.675 ns5.640 ns5.724 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 6 L: 63.986 ns0.0096 ns0.0090 ns3.985 ns3.970 ns4.005 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 6 L: 63.958 ns0.0164 ns0.0146 ns3.957 ns3.939 ns3.984 ns0.990.000.0024--40 B27 B
SubstringJob-WNUADImI: 7 L: 75.511 ns0.0220 ns0.0205 ns5.513 ns5.476 ns5.545 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 7 L: 75.702 ns0.0298 ns0.0279 ns5.700 ns5.645 ns5.745 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 7 L: 73.853 ns0.0175 ns0.0164 ns3.854 ns3.825 ns3.880 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 7 L: 73.988 ns0.0207 ns0.0184 ns3.987 ns3.962 ns4.020 ns1.040.010.0024--40 B27 B
SubstringJob-WNUADImI: 8 L: 85.491 ns0.0296 ns0.0277 ns5.499 ns5.449 ns5.538 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 8 L: 85.680 ns0.0358 ns0.0335 ns5.685 ns5.623 ns5.742 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 8 L: 84.015 ns0.0120 ns0.0112 ns4.015 ns3.986 ns4.030 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 8 L: 83.971 ns0.0296 ns0.0277 ns3.972 ns3.929 ns4.018 ns0.990.010.0024--40 B27 B

ASM

M

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1B31ACA8048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrdi,rcxmovesi,edxtestesi,esijl near ptr M01_L03movebx,[rdi+8]cmpebx,esijl near ptr M01_L04testr8d,r8djl near ptr M01_L05movecx,ebxsubecx,r8dcmpecx,esijl near ptr M01_L06testr8d,r8dje short M01_L02testesi,esije short M01_L01M01_L00:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrdi,0Cmovedx,esileardx,[rdi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L01:cmpebx,r8djne short M01_L00movrax,rdiaddrsp,20poprbxpoprsipoprdiretM01_L02:movrax,1B31ACA3020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L03:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A855movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L04:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A891movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L05:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A519movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L06:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,9EBDmovrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWint3; Total bytes of code 512

PR

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1FA62268048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrsi,rcxmovedi,edxmovecx,[rsi+8]moveax,edimovedx,r8daddrax,rdxmovedx,ecxcmprax,rdxja short M01_L02testr8d,r8djne short M01_L00movrax,1FA62263020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L00:cmpr8d,ecxjne short M01_L01movrax,rsiaddrsp,20poprbxpoprsipoprdiretM01_L01:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrsi,0Cmovedx,edileardx,[rsi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L02:movrcx,rsimovedx,edicall System.String.ThrowSubstringArgumentOutRangeException(Int32, Int32)int3; Total bytes of code 131

@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks should I close this or?

@danmoseley

Copy link
Copy Markdown
Contributor

I'm going to close this as the improvements are as you say marginal. Thank you for the attempt!

@ghostghost locked as resolved and limited conversation to collaborators Jun 1, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtimecommunity-contributionIndicates that the PR has been added by a community membertenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@nietras@danmoseley@GrabYourPitchforks@jkotas@deeprobin@vcsjones@jeffhandley@adamsitnik
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Improve String.Substring performance by nietras · Pull Request #62577 · dotnet/runtime · GitHub
Skip to content

Improve String.Substring performance - #62577

Closed
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf
Closed

Improve String.Substring performance#62577
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf

Conversation

@nietras

@nietrasnietras commented Dec 9, 2021

Copy link
Copy Markdown
Contributor

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@ghostghost added the community-contribution Indicates that the PR has been added by a community member label Dec 9, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

Using Span.Slice guard clauses for perf. Regressions I believe are due to benchmark issues. In any case, is changing guard clauses to this an acceptable change? Should exception messages be preserved?

// * Warnings *
MultimodalDistribution
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.56)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.89)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.75)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 3)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.78)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.83)
BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-ICQHYC : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-KPKVVD : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsilMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0815.3259 ns0.9471 ns1.0906 ns15.6270 ns11.1665 ns16.1263 ns1.000.000.002440 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz087.1677 ns0.3913 ns0.4506 ns7.2510 ns6.2912 ns7.9841 ns0.470.050.002440 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0?1.0918 ns0.0057 ns0.0051 ns1.0905 ns1.0861 ns1.1031 ns1.000.00--
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz0?0.4470 ns0.0059 ns0.0055 ns0.4444 ns0.4404 ns0.4585 ns0.410.01--
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz747.6604 ns0.3992 ns0.4597 ns7.7002 ns6.7318 ns8.2808 ns1.000.000.001932 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7413.2061 ns0.9380 ns1.0802 ns13.5553 ns11.1386 ns14.6399 ns1.730.210.001932 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz7?9.0171 ns1.6091 ns1.8531 ns9.2496 ns5.2419 ns12.1909 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7?14.7738 ns0.7346 ns0.8165 ns14.9451 ns11.5924 ns15.5339 ns1.730.440.002948 B
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10112.1967 ns1.0340 ns1.1908 ns12.3111 ns10.2499 ns14.3337 ns1.000.000.001424 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz1014.3119 ns0.0334 ns0.0279 ns4.3081 ns4.2731 ns4.3719 ns0.350.040.001424 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10?10.0238 ns0.5880 ns0.6772 ns10.1232 ns8.0144 ns10.7685 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz10?10.8206 ns0.7301 ns0.8407 ns10.7733 ns8.8957 ns11.9972 ns1.090.140.002948 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz19?10.1852 ns0.8323 ns0.9585 ns10.1999 ns8.4898 ns11.9423 ns1.000.000.001424 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz19?6.4746 ns0.4515 ns0.5200 ns6.2816 ns5.9014 ns7.5252 ns0.640.080.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B
Author:nietras
Assignees:-
Labels:

area-System.Runtime, community-contribution

Milestone:-

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@adamsitnikadamsitnik added the tenet-performance Performance related issue label Dec 10, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks tried to address feedback. Still don't know what to do about getting reliable benchmark tests. These do not show expected results currently but are also volatile. Substring(0) still faster of course.

Tried running StringReaderReadLineTests.ReadLine tests but no significant change there except [0, 0] case, so this PR does not appear to currently improve on that.

Let me know, what next steps you guys think should be.

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@nietras

Copy link
Copy Markdown
ContributorAuthor

Cleaned up. Benchmarks below, only significant improvements for when returning same string or empty. Regressions are often due to variance in benchmarks. Main point probably is throw code is separated out reducing code size of the method. I have included the cost of new'ing a similar string for comparison here.

Substring(int)

MethodJobToolchainStartMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADIm01.933 ns0.0043 ns0.0038 ns1.934 ns1.926 ns1.939 ns1.000.00----867 B
SubstringJob-WPCFEPpr01.283 ns0.0061 ns0.0057 ns1.282 ns1.275 ns1.294 ns0.660.00----471 B
SubstringJob-WNUADIm85.323 ns0.0295 ns0.0276 ns5.324 ns5.286 ns5.382 ns1.000.000.0024--40 B867 B
SubstringJob-WPCFEPpr85.906 ns0.0271 ns0.0240 ns5.895 ns5.875 ns5.947 ns1.110.010.0024--40 B471 B
NewStringJob-WNUADIm83.755 ns0.0320 ns0.0299 ns3.760 ns3.706 ns3.805 ns1.000.000.0024--40 B40 B
NewStringJob-WPCFEPpr83.758 ns0.0170 ns0.0151 ns3.760 ns3.730 ns3.782 ns1.000.010.0024--40 B40 B
SubstringJob-WNUADIm161.570 ns0.0473 ns0.0419 ns1.552 ns1.538 ns1.655 ns1.000.00----867 B
SubstringJob-WPCFEPpr161.578 ns0.0353 ns0.0330 ns1.594 ns1.511 ns1.614 ns1.000.03----471 B
NewStringJob-WNUADIm161.371 ns0.0218 ns0.0193 ns1.372 ns1.343 ns1.413 ns1.000.00----40 B
NewStringJob-WPCFEPpr161.296 ns0.0040 ns0.0036 ns1.296 ns1.290 ns1.302 ns0.950.01----40 B

Substring(int,int)

MethodJobToolchainSliceMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADImI: 0 L: 01.713 ns0.0042 ns0.0039 ns1.712 ns1.707 ns1.721 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L: 01.292 ns0.0063 ns0.0059 ns1.290 ns1.283 ns1.303 ns0.750.00----483 B
NewStringJob-WNUADImI: 0 L: 01.702 ns0.0046 ns0.0043 ns1.701 ns1.693 ns1.710 ns1.000.00----27 B
NewStringJob-WPCFEPprI: 0 L: 01.501 ns0.0034 ns0.0029 ns1.501 ns1.495 ns1.505 ns0.880.00----27 B
SubstringJob-WNUADImI: 0 L:161.923 ns0.0025 ns0.0022 ns1.924 ns1.918 ns1.926 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L:161.504 ns0.0027 ns0.0026 ns1.505 ns1.499 ns1.509 ns0.780.00----483 B
NewStringJob-WNUADImI: 0 L:164.252 ns0.0147 ns0.0130 ns4.255 ns4.218 ns4.268 ns1.000.000.0033--56 B27 B
NewStringJob-WPCFEPprI: 0 L:164.214 ns0.0461 ns0.0409 ns4.207 ns4.163 ns4.280 ns0.990.010.0033--56 B27 B
SubstringJob-WNUADImI: 1 L: 15.146 ns0.0528 ns0.0494 ns5.154 ns4.973 ns5.177 ns1.000.000.0014--24 B864 B
SubstringJob-WPCFEPprI: 1 L: 15.194 ns0.0180 ns0.0159 ns5.194 ns5.161 ns5.221 ns1.010.010.0014--24 B483 B
NewStringJob-WNUADImI: 1 L: 13.656 ns0.0179 ns0.0167 ns3.658 ns3.623 ns3.678 ns1.000.000.0014--24 B27 B
NewStringJob-WPCFEPprI: 1 L: 13.685 ns0.0186 ns0.0174 ns3.684 ns3.656 ns3.716 ns1.010.010.0014--24 B27 B
SubstringJob-WNUADImI: 2 L: 25.554 ns0.0214 ns0.0189 ns5.556 ns5.522 ns5.589 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 2 L: 25.375 ns0.0403 ns0.0377 ns5.368 ns5.322 ns5.445 ns0.970.010.0019--32 B483 B
NewStringJob-WNUADImI: 2 L: 23.842 ns0.0142 ns0.0133 ns3.842 ns3.815 ns3.868 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 2 L: 23.871 ns0.0179 ns0.0168 ns3.866 ns3.848 ns3.905 ns1.010.010.0019--32 B27 B
SubstringJob-WNUADImI: 3 L: 35.708 ns0.1507 ns0.1736 ns5.616 ns5.568 ns6.077 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 3 L: 35.395 ns0.0215 ns0.0179 ns5.399 ns5.346 ns5.413 ns0.940.030.0019--32 B483 B
NewStringJob-WNUADImI: 3 L: 33.859 ns0.0231 ns0.0205 ns3.856 ns3.835 ns3.907 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 3 L: 33.820 ns0.0243 ns0.0227 ns3.827 ns3.787 ns3.857 ns0.990.010.0019--32 B27 B
SubstringJob-WNUADImI: 4 L: 45.352 ns0.0226 ns0.0176 ns5.354 ns5.314 ns5.380 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 4 L: 413.276 ns0.2797 ns0.2872 ns13.330 ns12.226 ns13.534 ns2.470.060.0019--32 B483 B
NewStringJob-WNUADImI: 4 L: 43.825 ns0.0109 ns0.0102 ns3.827 ns3.807 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 4 L: 43.815 ns0.0236 ns0.0221 ns3.812 ns3.791 ns3.864 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 5 L: 55.334 ns0.0321 ns0.0300 ns5.327 ns5.295 ns5.385 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 5 L: 55.618 ns0.0410 ns0.0343 ns5.614 ns5.566 ns5.694 ns1.050.010.0019--32 B483 B
NewStringJob-WNUADImI: 5 L: 53.813 ns0.0179 ns0.0168 ns3.816 ns3.786 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 5 L: 53.801 ns0.0241 ns0.0213 ns3.802 ns3.771 ns3.836 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 6 L: 65.492 ns0.0333 ns0.0311 ns5.489 ns5.447 ns5.544 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 6 L: 65.679 ns0.0306 ns0.0286 ns5.675 ns5.640 ns5.724 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 6 L: 63.986 ns0.0096 ns0.0090 ns3.985 ns3.970 ns4.005 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 6 L: 63.958 ns0.0164 ns0.0146 ns3.957 ns3.939 ns3.984 ns0.990.000.0024--40 B27 B
SubstringJob-WNUADImI: 7 L: 75.511 ns0.0220 ns0.0205 ns5.513 ns5.476 ns5.545 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 7 L: 75.702 ns0.0298 ns0.0279 ns5.700 ns5.645 ns5.745 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 7 L: 73.853 ns0.0175 ns0.0164 ns3.854 ns3.825 ns3.880 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 7 L: 73.988 ns0.0207 ns0.0184 ns3.987 ns3.962 ns4.020 ns1.040.010.0024--40 B27 B
SubstringJob-WNUADImI: 8 L: 85.491 ns0.0296 ns0.0277 ns5.499 ns5.449 ns5.538 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 8 L: 85.680 ns0.0358 ns0.0335 ns5.685 ns5.623 ns5.742 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 8 L: 84.015 ns0.0120 ns0.0112 ns4.015 ns3.986 ns4.030 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 8 L: 83.971 ns0.0296 ns0.0277 ns3.972 ns3.929 ns4.018 ns0.990.010.0024--40 B27 B

ASM

M

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1B31ACA8048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrdi,rcxmovesi,edxtestesi,esijl near ptr M01_L03movebx,[rdi+8]cmpebx,esijl near ptr M01_L04testr8d,r8djl near ptr M01_L05movecx,ebxsubecx,r8dcmpecx,esijl near ptr M01_L06testr8d,r8dje short M01_L02testesi,esije short M01_L01M01_L00:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrdi,0Cmovedx,esileardx,[rdi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L01:cmpebx,r8djne short M01_L00movrax,rdiaddrsp,20poprbxpoprsipoprdiretM01_L02:movrax,1B31ACA3020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L03:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A855movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L04:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A891movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L05:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A519movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L06:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,9EBDmovrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWint3; Total bytes of code 512

PR

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1FA62268048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrsi,rcxmovedi,edxmovecx,[rsi+8]moveax,edimovedx,r8daddrax,rdxmovedx,ecxcmprax,rdxja short M01_L02testr8d,r8djne short M01_L00movrax,1FA62263020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L00:cmpr8d,ecxjne short M01_L01movrax,rsiaddrsp,20poprbxpoprsipoprdiretM01_L01:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrsi,0Cmovedx,edileardx,[rsi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L02:movrcx,rsimovedx,edicall System.String.ThrowSubstringArgumentOutRangeException(Int32, Int32)int3; Total bytes of code 131

@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks should I close this or?

@danmoseley

Copy link
Copy Markdown
Contributor

I'm going to close this as the improvements are as you say marginal. Thank you for the attempt!

@ghostghost locked as resolved and limited conversation to collaborators Jun 1, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtimecommunity-contributionIndicates that the PR has been added by a community membertenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@nietras@danmoseley@GrabYourPitchforks@jkotas@deeprobin@vcsjones@jeffhandley@adamsitnik
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Improve String.Substring performance by nietras · Pull Request #62577 · dotnet/runtime · GitHub
Skip to content

Improve String.Substring performance - #62577

Closed
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf
Closed

Improve String.Substring performance#62577
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf

Conversation

@nietras

@nietrasnietras commented Dec 9, 2021

Copy link
Copy Markdown
Contributor

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@ghostghost added the community-contribution Indicates that the PR has been added by a community member label Dec 9, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

Using Span.Slice guard clauses for perf. Regressions I believe are due to benchmark issues. In any case, is changing guard clauses to this an acceptable change? Should exception messages be preserved?

// * Warnings *
MultimodalDistribution
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.56)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.89)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.75)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 3)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.78)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.83)
BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-ICQHYC : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-KPKVVD : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsilMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0815.3259 ns0.9471 ns1.0906 ns15.6270 ns11.1665 ns16.1263 ns1.000.000.002440 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz087.1677 ns0.3913 ns0.4506 ns7.2510 ns6.2912 ns7.9841 ns0.470.050.002440 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0?1.0918 ns0.0057 ns0.0051 ns1.0905 ns1.0861 ns1.1031 ns1.000.00--
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz0?0.4470 ns0.0059 ns0.0055 ns0.4444 ns0.4404 ns0.4585 ns0.410.01--
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz747.6604 ns0.3992 ns0.4597 ns7.7002 ns6.7318 ns8.2808 ns1.000.000.001932 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7413.2061 ns0.9380 ns1.0802 ns13.5553 ns11.1386 ns14.6399 ns1.730.210.001932 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz7?9.0171 ns1.6091 ns1.8531 ns9.2496 ns5.2419 ns12.1909 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7?14.7738 ns0.7346 ns0.8165 ns14.9451 ns11.5924 ns15.5339 ns1.730.440.002948 B
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10112.1967 ns1.0340 ns1.1908 ns12.3111 ns10.2499 ns14.3337 ns1.000.000.001424 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz1014.3119 ns0.0334 ns0.0279 ns4.3081 ns4.2731 ns4.3719 ns0.350.040.001424 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10?10.0238 ns0.5880 ns0.6772 ns10.1232 ns8.0144 ns10.7685 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz10?10.8206 ns0.7301 ns0.8407 ns10.7733 ns8.8957 ns11.9972 ns1.090.140.002948 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz19?10.1852 ns0.8323 ns0.9585 ns10.1999 ns8.4898 ns11.9423 ns1.000.000.001424 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz19?6.4746 ns0.4515 ns0.5200 ns6.2816 ns5.9014 ns7.5252 ns0.640.080.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B
Author:nietras
Assignees:-
Labels:

area-System.Runtime, community-contribution

Milestone:-

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@adamsitnikadamsitnik added the tenet-performance Performance related issue label Dec 10, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks tried to address feedback. Still don't know what to do about getting reliable benchmark tests. These do not show expected results currently but are also volatile. Substring(0) still faster of course.

Tried running StringReaderReadLineTests.ReadLine tests but no significant change there except [0, 0] case, so this PR does not appear to currently improve on that.

Let me know, what next steps you guys think should be.

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@nietras

Copy link
Copy Markdown
ContributorAuthor

Cleaned up. Benchmarks below, only significant improvements for when returning same string or empty. Regressions are often due to variance in benchmarks. Main point probably is throw code is separated out reducing code size of the method. I have included the cost of new'ing a similar string for comparison here.

Substring(int)

MethodJobToolchainStartMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADIm01.933 ns0.0043 ns0.0038 ns1.934 ns1.926 ns1.939 ns1.000.00----867 B
SubstringJob-WPCFEPpr01.283 ns0.0061 ns0.0057 ns1.282 ns1.275 ns1.294 ns0.660.00----471 B
SubstringJob-WNUADIm85.323 ns0.0295 ns0.0276 ns5.324 ns5.286 ns5.382 ns1.000.000.0024--40 B867 B
SubstringJob-WPCFEPpr85.906 ns0.0271 ns0.0240 ns5.895 ns5.875 ns5.947 ns1.110.010.0024--40 B471 B
NewStringJob-WNUADIm83.755 ns0.0320 ns0.0299 ns3.760 ns3.706 ns3.805 ns1.000.000.0024--40 B40 B
NewStringJob-WPCFEPpr83.758 ns0.0170 ns0.0151 ns3.760 ns3.730 ns3.782 ns1.000.010.0024--40 B40 B
SubstringJob-WNUADIm161.570 ns0.0473 ns0.0419 ns1.552 ns1.538 ns1.655 ns1.000.00----867 B
SubstringJob-WPCFEPpr161.578 ns0.0353 ns0.0330 ns1.594 ns1.511 ns1.614 ns1.000.03----471 B
NewStringJob-WNUADIm161.371 ns0.0218 ns0.0193 ns1.372 ns1.343 ns1.413 ns1.000.00----40 B
NewStringJob-WPCFEPpr161.296 ns0.0040 ns0.0036 ns1.296 ns1.290 ns1.302 ns0.950.01----40 B

Substring(int,int)

MethodJobToolchainSliceMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADImI: 0 L: 01.713 ns0.0042 ns0.0039 ns1.712 ns1.707 ns1.721 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L: 01.292 ns0.0063 ns0.0059 ns1.290 ns1.283 ns1.303 ns0.750.00----483 B
NewStringJob-WNUADImI: 0 L: 01.702 ns0.0046 ns0.0043 ns1.701 ns1.693 ns1.710 ns1.000.00----27 B
NewStringJob-WPCFEPprI: 0 L: 01.501 ns0.0034 ns0.0029 ns1.501 ns1.495 ns1.505 ns0.880.00----27 B
SubstringJob-WNUADImI: 0 L:161.923 ns0.0025 ns0.0022 ns1.924 ns1.918 ns1.926 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L:161.504 ns0.0027 ns0.0026 ns1.505 ns1.499 ns1.509 ns0.780.00----483 B
NewStringJob-WNUADImI: 0 L:164.252 ns0.0147 ns0.0130 ns4.255 ns4.218 ns4.268 ns1.000.000.0033--56 B27 B
NewStringJob-WPCFEPprI: 0 L:164.214 ns0.0461 ns0.0409 ns4.207 ns4.163 ns4.280 ns0.990.010.0033--56 B27 B
SubstringJob-WNUADImI: 1 L: 15.146 ns0.0528 ns0.0494 ns5.154 ns4.973 ns5.177 ns1.000.000.0014--24 B864 B
SubstringJob-WPCFEPprI: 1 L: 15.194 ns0.0180 ns0.0159 ns5.194 ns5.161 ns5.221 ns1.010.010.0014--24 B483 B
NewStringJob-WNUADImI: 1 L: 13.656 ns0.0179 ns0.0167 ns3.658 ns3.623 ns3.678 ns1.000.000.0014--24 B27 B
NewStringJob-WPCFEPprI: 1 L: 13.685 ns0.0186 ns0.0174 ns3.684 ns3.656 ns3.716 ns1.010.010.0014--24 B27 B
SubstringJob-WNUADImI: 2 L: 25.554 ns0.0214 ns0.0189 ns5.556 ns5.522 ns5.589 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 2 L: 25.375 ns0.0403 ns0.0377 ns5.368 ns5.322 ns5.445 ns0.970.010.0019--32 B483 B
NewStringJob-WNUADImI: 2 L: 23.842 ns0.0142 ns0.0133 ns3.842 ns3.815 ns3.868 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 2 L: 23.871 ns0.0179 ns0.0168 ns3.866 ns3.848 ns3.905 ns1.010.010.0019--32 B27 B
SubstringJob-WNUADImI: 3 L: 35.708 ns0.1507 ns0.1736 ns5.616 ns5.568 ns6.077 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 3 L: 35.395 ns0.0215 ns0.0179 ns5.399 ns5.346 ns5.413 ns0.940.030.0019--32 B483 B
NewStringJob-WNUADImI: 3 L: 33.859 ns0.0231 ns0.0205 ns3.856 ns3.835 ns3.907 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 3 L: 33.820 ns0.0243 ns0.0227 ns3.827 ns3.787 ns3.857 ns0.990.010.0019--32 B27 B
SubstringJob-WNUADImI: 4 L: 45.352 ns0.0226 ns0.0176 ns5.354 ns5.314 ns5.380 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 4 L: 413.276 ns0.2797 ns0.2872 ns13.330 ns12.226 ns13.534 ns2.470.060.0019--32 B483 B
NewStringJob-WNUADImI: 4 L: 43.825 ns0.0109 ns0.0102 ns3.827 ns3.807 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 4 L: 43.815 ns0.0236 ns0.0221 ns3.812 ns3.791 ns3.864 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 5 L: 55.334 ns0.0321 ns0.0300 ns5.327 ns5.295 ns5.385 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 5 L: 55.618 ns0.0410 ns0.0343 ns5.614 ns5.566 ns5.694 ns1.050.010.0019--32 B483 B
NewStringJob-WNUADImI: 5 L: 53.813 ns0.0179 ns0.0168 ns3.816 ns3.786 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 5 L: 53.801 ns0.0241 ns0.0213 ns3.802 ns3.771 ns3.836 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 6 L: 65.492 ns0.0333 ns0.0311 ns5.489 ns5.447 ns5.544 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 6 L: 65.679 ns0.0306 ns0.0286 ns5.675 ns5.640 ns5.724 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 6 L: 63.986 ns0.0096 ns0.0090 ns3.985 ns3.970 ns4.005 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 6 L: 63.958 ns0.0164 ns0.0146 ns3.957 ns3.939 ns3.984 ns0.990.000.0024--40 B27 B
SubstringJob-WNUADImI: 7 L: 75.511 ns0.0220 ns0.0205 ns5.513 ns5.476 ns5.545 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 7 L: 75.702 ns0.0298 ns0.0279 ns5.700 ns5.645 ns5.745 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 7 L: 73.853 ns0.0175 ns0.0164 ns3.854 ns3.825 ns3.880 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 7 L: 73.988 ns0.0207 ns0.0184 ns3.987 ns3.962 ns4.020 ns1.040.010.0024--40 B27 B
SubstringJob-WNUADImI: 8 L: 85.491 ns0.0296 ns0.0277 ns5.499 ns5.449 ns5.538 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 8 L: 85.680 ns0.0358 ns0.0335 ns5.685 ns5.623 ns5.742 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 8 L: 84.015 ns0.0120 ns0.0112 ns4.015 ns3.986 ns4.030 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 8 L: 83.971 ns0.0296 ns0.0277 ns3.972 ns3.929 ns4.018 ns0.990.010.0024--40 B27 B

ASM

M

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1B31ACA8048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrdi,rcxmovesi,edxtestesi,esijl near ptr M01_L03movebx,[rdi+8]cmpebx,esijl near ptr M01_L04testr8d,r8djl near ptr M01_L05movecx,ebxsubecx,r8dcmpecx,esijl near ptr M01_L06testr8d,r8dje short M01_L02testesi,esije short M01_L01M01_L00:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrdi,0Cmovedx,esileardx,[rdi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L01:cmpebx,r8djne short M01_L00movrax,rdiaddrsp,20poprbxpoprsipoprdiretM01_L02:movrax,1B31ACA3020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L03:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A855movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L04:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A891movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L05:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A519movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L06:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,9EBDmovrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWint3; Total bytes of code 512

PR

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1FA62268048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrsi,rcxmovedi,edxmovecx,[rsi+8]moveax,edimovedx,r8daddrax,rdxmovedx,ecxcmprax,rdxja short M01_L02testr8d,r8djne short M01_L00movrax,1FA62263020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L00:cmpr8d,ecxjne short M01_L01movrax,rsiaddrsp,20poprbxpoprsipoprdiretM01_L01:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrsi,0Cmovedx,edileardx,[rsi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L02:movrcx,rsimovedx,edicall System.String.ThrowSubstringArgumentOutRangeException(Int32, Int32)int3; Total bytes of code 131

@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks should I close this or?

@danmoseley

Copy link
Copy Markdown
Contributor

I'm going to close this as the improvements are as you say marginal. Thank you for the attempt!

@ghostghost locked as resolved and limited conversation to collaborators Jun 1, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtimecommunity-contributionIndicates that the PR has been added by a community membertenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@nietras@danmoseley@GrabYourPitchforks@jkotas@deeprobin@vcsjones@jeffhandley@adamsitnik
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Improve String.Substring performance by nietras · Pull Request #62577 · dotnet/runtime · GitHub
Skip to content

Improve String.Substring performance - #62577

Closed
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf
Closed

Improve String.Substring performance#62577
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf

Conversation

@nietras

@nietrasnietras commented Dec 9, 2021

Copy link
Copy Markdown
Contributor

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@ghostghost added the community-contribution Indicates that the PR has been added by a community member label Dec 9, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

Using Span.Slice guard clauses for perf. Regressions I believe are due to benchmark issues. In any case, is changing guard clauses to this an acceptable change? Should exception messages be preserved?

// * Warnings *
MultimodalDistribution
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.56)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.89)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.75)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 3)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.78)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.83)
BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-ICQHYC : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-KPKVVD : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsilMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0815.3259 ns0.9471 ns1.0906 ns15.6270 ns11.1665 ns16.1263 ns1.000.000.002440 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz087.1677 ns0.3913 ns0.4506 ns7.2510 ns6.2912 ns7.9841 ns0.470.050.002440 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0?1.0918 ns0.0057 ns0.0051 ns1.0905 ns1.0861 ns1.1031 ns1.000.00--
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz0?0.4470 ns0.0059 ns0.0055 ns0.4444 ns0.4404 ns0.4585 ns0.410.01--
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz747.6604 ns0.3992 ns0.4597 ns7.7002 ns6.7318 ns8.2808 ns1.000.000.001932 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7413.2061 ns0.9380 ns1.0802 ns13.5553 ns11.1386 ns14.6399 ns1.730.210.001932 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz7?9.0171 ns1.6091 ns1.8531 ns9.2496 ns5.2419 ns12.1909 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7?14.7738 ns0.7346 ns0.8165 ns14.9451 ns11.5924 ns15.5339 ns1.730.440.002948 B
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10112.1967 ns1.0340 ns1.1908 ns12.3111 ns10.2499 ns14.3337 ns1.000.000.001424 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz1014.3119 ns0.0334 ns0.0279 ns4.3081 ns4.2731 ns4.3719 ns0.350.040.001424 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10?10.0238 ns0.5880 ns0.6772 ns10.1232 ns8.0144 ns10.7685 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz10?10.8206 ns0.7301 ns0.8407 ns10.7733 ns8.8957 ns11.9972 ns1.090.140.002948 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz19?10.1852 ns0.8323 ns0.9585 ns10.1999 ns8.4898 ns11.9423 ns1.000.000.001424 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz19?6.4746 ns0.4515 ns0.5200 ns6.2816 ns5.9014 ns7.5252 ns0.640.080.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B
Author:nietras
Assignees:-
Labels:

area-System.Runtime, community-contribution

Milestone:-

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@adamsitnikadamsitnik added the tenet-performance Performance related issue label Dec 10, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks tried to address feedback. Still don't know what to do about getting reliable benchmark tests. These do not show expected results currently but are also volatile. Substring(0) still faster of course.

Tried running StringReaderReadLineTests.ReadLine tests but no significant change there except [0, 0] case, so this PR does not appear to currently improve on that.

Let me know, what next steps you guys think should be.

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@nietras

Copy link
Copy Markdown
ContributorAuthor

Cleaned up. Benchmarks below, only significant improvements for when returning same string or empty. Regressions are often due to variance in benchmarks. Main point probably is throw code is separated out reducing code size of the method. I have included the cost of new'ing a similar string for comparison here.

Substring(int)

MethodJobToolchainStartMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADIm01.933 ns0.0043 ns0.0038 ns1.934 ns1.926 ns1.939 ns1.000.00----867 B
SubstringJob-WPCFEPpr01.283 ns0.0061 ns0.0057 ns1.282 ns1.275 ns1.294 ns0.660.00----471 B
SubstringJob-WNUADIm85.323 ns0.0295 ns0.0276 ns5.324 ns5.286 ns5.382 ns1.000.000.0024--40 B867 B
SubstringJob-WPCFEPpr85.906 ns0.0271 ns0.0240 ns5.895 ns5.875 ns5.947 ns1.110.010.0024--40 B471 B
NewStringJob-WNUADIm83.755 ns0.0320 ns0.0299 ns3.760 ns3.706 ns3.805 ns1.000.000.0024--40 B40 B
NewStringJob-WPCFEPpr83.758 ns0.0170 ns0.0151 ns3.760 ns3.730 ns3.782 ns1.000.010.0024--40 B40 B
SubstringJob-WNUADIm161.570 ns0.0473 ns0.0419 ns1.552 ns1.538 ns1.655 ns1.000.00----867 B
SubstringJob-WPCFEPpr161.578 ns0.0353 ns0.0330 ns1.594 ns1.511 ns1.614 ns1.000.03----471 B
NewStringJob-WNUADIm161.371 ns0.0218 ns0.0193 ns1.372 ns1.343 ns1.413 ns1.000.00----40 B
NewStringJob-WPCFEPpr161.296 ns0.0040 ns0.0036 ns1.296 ns1.290 ns1.302 ns0.950.01----40 B

Substring(int,int)

MethodJobToolchainSliceMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADImI: 0 L: 01.713 ns0.0042 ns0.0039 ns1.712 ns1.707 ns1.721 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L: 01.292 ns0.0063 ns0.0059 ns1.290 ns1.283 ns1.303 ns0.750.00----483 B
NewStringJob-WNUADImI: 0 L: 01.702 ns0.0046 ns0.0043 ns1.701 ns1.693 ns1.710 ns1.000.00----27 B
NewStringJob-WPCFEPprI: 0 L: 01.501 ns0.0034 ns0.0029 ns1.501 ns1.495 ns1.505 ns0.880.00----27 B
SubstringJob-WNUADImI: 0 L:161.923 ns0.0025 ns0.0022 ns1.924 ns1.918 ns1.926 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L:161.504 ns0.0027 ns0.0026 ns1.505 ns1.499 ns1.509 ns0.780.00----483 B
NewStringJob-WNUADImI: 0 L:164.252 ns0.0147 ns0.0130 ns4.255 ns4.218 ns4.268 ns1.000.000.0033--56 B27 B
NewStringJob-WPCFEPprI: 0 L:164.214 ns0.0461 ns0.0409 ns4.207 ns4.163 ns4.280 ns0.990.010.0033--56 B27 B
SubstringJob-WNUADImI: 1 L: 15.146 ns0.0528 ns0.0494 ns5.154 ns4.973 ns5.177 ns1.000.000.0014--24 B864 B
SubstringJob-WPCFEPprI: 1 L: 15.194 ns0.0180 ns0.0159 ns5.194 ns5.161 ns5.221 ns1.010.010.0014--24 B483 B
NewStringJob-WNUADImI: 1 L: 13.656 ns0.0179 ns0.0167 ns3.658 ns3.623 ns3.678 ns1.000.000.0014--24 B27 B
NewStringJob-WPCFEPprI: 1 L: 13.685 ns0.0186 ns0.0174 ns3.684 ns3.656 ns3.716 ns1.010.010.0014--24 B27 B
SubstringJob-WNUADImI: 2 L: 25.554 ns0.0214 ns0.0189 ns5.556 ns5.522 ns5.589 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 2 L: 25.375 ns0.0403 ns0.0377 ns5.368 ns5.322 ns5.445 ns0.970.010.0019--32 B483 B
NewStringJob-WNUADImI: 2 L: 23.842 ns0.0142 ns0.0133 ns3.842 ns3.815 ns3.868 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 2 L: 23.871 ns0.0179 ns0.0168 ns3.866 ns3.848 ns3.905 ns1.010.010.0019--32 B27 B
SubstringJob-WNUADImI: 3 L: 35.708 ns0.1507 ns0.1736 ns5.616 ns5.568 ns6.077 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 3 L: 35.395 ns0.0215 ns0.0179 ns5.399 ns5.346 ns5.413 ns0.940.030.0019--32 B483 B
NewStringJob-WNUADImI: 3 L: 33.859 ns0.0231 ns0.0205 ns3.856 ns3.835 ns3.907 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 3 L: 33.820 ns0.0243 ns0.0227 ns3.827 ns3.787 ns3.857 ns0.990.010.0019--32 B27 B
SubstringJob-WNUADImI: 4 L: 45.352 ns0.0226 ns0.0176 ns5.354 ns5.314 ns5.380 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 4 L: 413.276 ns0.2797 ns0.2872 ns13.330 ns12.226 ns13.534 ns2.470.060.0019--32 B483 B
NewStringJob-WNUADImI: 4 L: 43.825 ns0.0109 ns0.0102 ns3.827 ns3.807 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 4 L: 43.815 ns0.0236 ns0.0221 ns3.812 ns3.791 ns3.864 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 5 L: 55.334 ns0.0321 ns0.0300 ns5.327 ns5.295 ns5.385 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 5 L: 55.618 ns0.0410 ns0.0343 ns5.614 ns5.566 ns5.694 ns1.050.010.0019--32 B483 B
NewStringJob-WNUADImI: 5 L: 53.813 ns0.0179 ns0.0168 ns3.816 ns3.786 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 5 L: 53.801 ns0.0241 ns0.0213 ns3.802 ns3.771 ns3.836 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 6 L: 65.492 ns0.0333 ns0.0311 ns5.489 ns5.447 ns5.544 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 6 L: 65.679 ns0.0306 ns0.0286 ns5.675 ns5.640 ns5.724 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 6 L: 63.986 ns0.0096 ns0.0090 ns3.985 ns3.970 ns4.005 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 6 L: 63.958 ns0.0164 ns0.0146 ns3.957 ns3.939 ns3.984 ns0.990.000.0024--40 B27 B
SubstringJob-WNUADImI: 7 L: 75.511 ns0.0220 ns0.0205 ns5.513 ns5.476 ns5.545 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 7 L: 75.702 ns0.0298 ns0.0279 ns5.700 ns5.645 ns5.745 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 7 L: 73.853 ns0.0175 ns0.0164 ns3.854 ns3.825 ns3.880 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 7 L: 73.988 ns0.0207 ns0.0184 ns3.987 ns3.962 ns4.020 ns1.040.010.0024--40 B27 B
SubstringJob-WNUADImI: 8 L: 85.491 ns0.0296 ns0.0277 ns5.499 ns5.449 ns5.538 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 8 L: 85.680 ns0.0358 ns0.0335 ns5.685 ns5.623 ns5.742 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 8 L: 84.015 ns0.0120 ns0.0112 ns4.015 ns3.986 ns4.030 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 8 L: 83.971 ns0.0296 ns0.0277 ns3.972 ns3.929 ns4.018 ns0.990.010.0024--40 B27 B

ASM

M

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1B31ACA8048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrdi,rcxmovesi,edxtestesi,esijl near ptr M01_L03movebx,[rdi+8]cmpebx,esijl near ptr M01_L04testr8d,r8djl near ptr M01_L05movecx,ebxsubecx,r8dcmpecx,esijl near ptr M01_L06testr8d,r8dje short M01_L02testesi,esije short M01_L01M01_L00:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrdi,0Cmovedx,esileardx,[rdi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L01:cmpebx,r8djne short M01_L00movrax,rdiaddrsp,20poprbxpoprsipoprdiretM01_L02:movrax,1B31ACA3020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L03:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A855movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L04:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A891movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L05:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A519movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L06:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,9EBDmovrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWint3; Total bytes of code 512

PR

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1FA62268048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrsi,rcxmovedi,edxmovecx,[rsi+8]moveax,edimovedx,r8daddrax,rdxmovedx,ecxcmprax,rdxja short M01_L02testr8d,r8djne short M01_L00movrax,1FA62263020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L00:cmpr8d,ecxjne short M01_L01movrax,rsiaddrsp,20poprbxpoprsipoprdiretM01_L01:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrsi,0Cmovedx,edileardx,[rsi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L02:movrcx,rsimovedx,edicall System.String.ThrowSubstringArgumentOutRangeException(Int32, Int32)int3; Total bytes of code 131

@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks should I close this or?

@danmoseley

Copy link
Copy Markdown
Contributor

I'm going to close this as the improvements are as you say marginal. Thank you for the attempt!

@ghostghost locked as resolved and limited conversation to collaborators Jun 1, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtimecommunity-contributionIndicates that the PR has been added by a community membertenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@nietras@danmoseley@GrabYourPitchforks@jkotas@deeprobin@vcsjones@jeffhandley@adamsitnik
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Improve String.Substring performance by nietras · Pull Request #62577 · dotnet/runtime · GitHub
Skip to content

Improve String.Substring performance - #62577

Closed
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf
Closed

Improve String.Substring performance#62577
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf

Conversation

@nietras

@nietrasnietras commented Dec 9, 2021

Copy link
Copy Markdown
Contributor

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@ghostghost added the community-contribution Indicates that the PR has been added by a community member label Dec 9, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

Using Span.Slice guard clauses for perf. Regressions I believe are due to benchmark issues. In any case, is changing guard clauses to this an acceptable change? Should exception messages be preserved?

// * Warnings *
MultimodalDistribution
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.56)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.89)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.75)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 3)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.78)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.83)
BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-ICQHYC : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-KPKVVD : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsilMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0815.3259 ns0.9471 ns1.0906 ns15.6270 ns11.1665 ns16.1263 ns1.000.000.002440 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz087.1677 ns0.3913 ns0.4506 ns7.2510 ns6.2912 ns7.9841 ns0.470.050.002440 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0?1.0918 ns0.0057 ns0.0051 ns1.0905 ns1.0861 ns1.1031 ns1.000.00--
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz0?0.4470 ns0.0059 ns0.0055 ns0.4444 ns0.4404 ns0.4585 ns0.410.01--
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz747.6604 ns0.3992 ns0.4597 ns7.7002 ns6.7318 ns8.2808 ns1.000.000.001932 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7413.2061 ns0.9380 ns1.0802 ns13.5553 ns11.1386 ns14.6399 ns1.730.210.001932 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz7?9.0171 ns1.6091 ns1.8531 ns9.2496 ns5.2419 ns12.1909 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7?14.7738 ns0.7346 ns0.8165 ns14.9451 ns11.5924 ns15.5339 ns1.730.440.002948 B
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10112.1967 ns1.0340 ns1.1908 ns12.3111 ns10.2499 ns14.3337 ns1.000.000.001424 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz1014.3119 ns0.0334 ns0.0279 ns4.3081 ns4.2731 ns4.3719 ns0.350.040.001424 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10?10.0238 ns0.5880 ns0.6772 ns10.1232 ns8.0144 ns10.7685 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz10?10.8206 ns0.7301 ns0.8407 ns10.7733 ns8.8957 ns11.9972 ns1.090.140.002948 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz19?10.1852 ns0.8323 ns0.9585 ns10.1999 ns8.4898 ns11.9423 ns1.000.000.001424 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz19?6.4746 ns0.4515 ns0.5200 ns6.2816 ns5.9014 ns7.5252 ns0.640.080.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B
Author:nietras
Assignees:-
Labels:

area-System.Runtime, community-contribution

Milestone:-

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@adamsitnikadamsitnik added the tenet-performance Performance related issue label Dec 10, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks tried to address feedback. Still don't know what to do about getting reliable benchmark tests. These do not show expected results currently but are also volatile. Substring(0) still faster of course.

Tried running StringReaderReadLineTests.ReadLine tests but no significant change there except [0, 0] case, so this PR does not appear to currently improve on that.

Let me know, what next steps you guys think should be.

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@nietras

Copy link
Copy Markdown
ContributorAuthor

Cleaned up. Benchmarks below, only significant improvements for when returning same string or empty. Regressions are often due to variance in benchmarks. Main point probably is throw code is separated out reducing code size of the method. I have included the cost of new'ing a similar string for comparison here.

Substring(int)

MethodJobToolchainStartMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADIm01.933 ns0.0043 ns0.0038 ns1.934 ns1.926 ns1.939 ns1.000.00----867 B
SubstringJob-WPCFEPpr01.283 ns0.0061 ns0.0057 ns1.282 ns1.275 ns1.294 ns0.660.00----471 B
SubstringJob-WNUADIm85.323 ns0.0295 ns0.0276 ns5.324 ns5.286 ns5.382 ns1.000.000.0024--40 B867 B
SubstringJob-WPCFEPpr85.906 ns0.0271 ns0.0240 ns5.895 ns5.875 ns5.947 ns1.110.010.0024--40 B471 B
NewStringJob-WNUADIm83.755 ns0.0320 ns0.0299 ns3.760 ns3.706 ns3.805 ns1.000.000.0024--40 B40 B
NewStringJob-WPCFEPpr83.758 ns0.0170 ns0.0151 ns3.760 ns3.730 ns3.782 ns1.000.010.0024--40 B40 B
SubstringJob-WNUADIm161.570 ns0.0473 ns0.0419 ns1.552 ns1.538 ns1.655 ns1.000.00----867 B
SubstringJob-WPCFEPpr161.578 ns0.0353 ns0.0330 ns1.594 ns1.511 ns1.614 ns1.000.03----471 B
NewStringJob-WNUADIm161.371 ns0.0218 ns0.0193 ns1.372 ns1.343 ns1.413 ns1.000.00----40 B
NewStringJob-WPCFEPpr161.296 ns0.0040 ns0.0036 ns1.296 ns1.290 ns1.302 ns0.950.01----40 B

Substring(int,int)

MethodJobToolchainSliceMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADImI: 0 L: 01.713 ns0.0042 ns0.0039 ns1.712 ns1.707 ns1.721 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L: 01.292 ns0.0063 ns0.0059 ns1.290 ns1.283 ns1.303 ns0.750.00----483 B
NewStringJob-WNUADImI: 0 L: 01.702 ns0.0046 ns0.0043 ns1.701 ns1.693 ns1.710 ns1.000.00----27 B
NewStringJob-WPCFEPprI: 0 L: 01.501 ns0.0034 ns0.0029 ns1.501 ns1.495 ns1.505 ns0.880.00----27 B
SubstringJob-WNUADImI: 0 L:161.923 ns0.0025 ns0.0022 ns1.924 ns1.918 ns1.926 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L:161.504 ns0.0027 ns0.0026 ns1.505 ns1.499 ns1.509 ns0.780.00----483 B
NewStringJob-WNUADImI: 0 L:164.252 ns0.0147 ns0.0130 ns4.255 ns4.218 ns4.268 ns1.000.000.0033--56 B27 B
NewStringJob-WPCFEPprI: 0 L:164.214 ns0.0461 ns0.0409 ns4.207 ns4.163 ns4.280 ns0.990.010.0033--56 B27 B
SubstringJob-WNUADImI: 1 L: 15.146 ns0.0528 ns0.0494 ns5.154 ns4.973 ns5.177 ns1.000.000.0014--24 B864 B
SubstringJob-WPCFEPprI: 1 L: 15.194 ns0.0180 ns0.0159 ns5.194 ns5.161 ns5.221 ns1.010.010.0014--24 B483 B
NewStringJob-WNUADImI: 1 L: 13.656 ns0.0179 ns0.0167 ns3.658 ns3.623 ns3.678 ns1.000.000.0014--24 B27 B
NewStringJob-WPCFEPprI: 1 L: 13.685 ns0.0186 ns0.0174 ns3.684 ns3.656 ns3.716 ns1.010.010.0014--24 B27 B
SubstringJob-WNUADImI: 2 L: 25.554 ns0.0214 ns0.0189 ns5.556 ns5.522 ns5.589 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 2 L: 25.375 ns0.0403 ns0.0377 ns5.368 ns5.322 ns5.445 ns0.970.010.0019--32 B483 B
NewStringJob-WNUADImI: 2 L: 23.842 ns0.0142 ns0.0133 ns3.842 ns3.815 ns3.868 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 2 L: 23.871 ns0.0179 ns0.0168 ns3.866 ns3.848 ns3.905 ns1.010.010.0019--32 B27 B
SubstringJob-WNUADImI: 3 L: 35.708 ns0.1507 ns0.1736 ns5.616 ns5.568 ns6.077 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 3 L: 35.395 ns0.0215 ns0.0179 ns5.399 ns5.346 ns5.413 ns0.940.030.0019--32 B483 B
NewStringJob-WNUADImI: 3 L: 33.859 ns0.0231 ns0.0205 ns3.856 ns3.835 ns3.907 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 3 L: 33.820 ns0.0243 ns0.0227 ns3.827 ns3.787 ns3.857 ns0.990.010.0019--32 B27 B
SubstringJob-WNUADImI: 4 L: 45.352 ns0.0226 ns0.0176 ns5.354 ns5.314 ns5.380 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 4 L: 413.276 ns0.2797 ns0.2872 ns13.330 ns12.226 ns13.534 ns2.470.060.0019--32 B483 B
NewStringJob-WNUADImI: 4 L: 43.825 ns0.0109 ns0.0102 ns3.827 ns3.807 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 4 L: 43.815 ns0.0236 ns0.0221 ns3.812 ns3.791 ns3.864 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 5 L: 55.334 ns0.0321 ns0.0300 ns5.327 ns5.295 ns5.385 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 5 L: 55.618 ns0.0410 ns0.0343 ns5.614 ns5.566 ns5.694 ns1.050.010.0019--32 B483 B
NewStringJob-WNUADImI: 5 L: 53.813 ns0.0179 ns0.0168 ns3.816 ns3.786 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 5 L: 53.801 ns0.0241 ns0.0213 ns3.802 ns3.771 ns3.836 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 6 L: 65.492 ns0.0333 ns0.0311 ns5.489 ns5.447 ns5.544 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 6 L: 65.679 ns0.0306 ns0.0286 ns5.675 ns5.640 ns5.724 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 6 L: 63.986 ns0.0096 ns0.0090 ns3.985 ns3.970 ns4.005 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 6 L: 63.958 ns0.0164 ns0.0146 ns3.957 ns3.939 ns3.984 ns0.990.000.0024--40 B27 B
SubstringJob-WNUADImI: 7 L: 75.511 ns0.0220 ns0.0205 ns5.513 ns5.476 ns5.545 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 7 L: 75.702 ns0.0298 ns0.0279 ns5.700 ns5.645 ns5.745 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 7 L: 73.853 ns0.0175 ns0.0164 ns3.854 ns3.825 ns3.880 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 7 L: 73.988 ns0.0207 ns0.0184 ns3.987 ns3.962 ns4.020 ns1.040.010.0024--40 B27 B
SubstringJob-WNUADImI: 8 L: 85.491 ns0.0296 ns0.0277 ns5.499 ns5.449 ns5.538 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 8 L: 85.680 ns0.0358 ns0.0335 ns5.685 ns5.623 ns5.742 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 8 L: 84.015 ns0.0120 ns0.0112 ns4.015 ns3.986 ns4.030 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 8 L: 83.971 ns0.0296 ns0.0277 ns3.972 ns3.929 ns4.018 ns0.990.010.0024--40 B27 B

ASM

M

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1B31ACA8048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrdi,rcxmovesi,edxtestesi,esijl near ptr M01_L03movebx,[rdi+8]cmpebx,esijl near ptr M01_L04testr8d,r8djl near ptr M01_L05movecx,ebxsubecx,r8dcmpecx,esijl near ptr M01_L06testr8d,r8dje short M01_L02testesi,esije short M01_L01M01_L00:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrdi,0Cmovedx,esileardx,[rdi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L01:cmpebx,r8djne short M01_L00movrax,rdiaddrsp,20poprbxpoprsipoprdiretM01_L02:movrax,1B31ACA3020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L03:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A855movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L04:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A891movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L05:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A519movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L06:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,9EBDmovrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWint3; Total bytes of code 512

PR

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1FA62268048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrsi,rcxmovedi,edxmovecx,[rsi+8]moveax,edimovedx,r8daddrax,rdxmovedx,ecxcmprax,rdxja short M01_L02testr8d,r8djne short M01_L00movrax,1FA62263020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L00:cmpr8d,ecxjne short M01_L01movrax,rsiaddrsp,20poprbxpoprsipoprdiretM01_L01:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrsi,0Cmovedx,edileardx,[rsi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L02:movrcx,rsimovedx,edicall System.String.ThrowSubstringArgumentOutRangeException(Int32, Int32)int3; Total bytes of code 131

@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks should I close this or?

@danmoseley

Copy link
Copy Markdown
Contributor

I'm going to close this as the improvements are as you say marginal. Thank you for the attempt!

@ghostghost locked as resolved and limited conversation to collaborators Jun 1, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtimecommunity-contributionIndicates that the PR has been added by a community membertenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@nietras@danmoseley@GrabYourPitchforks@jkotas@deeprobin@vcsjones@jeffhandley@adamsitnik
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Improve String.Substring performance by nietras · Pull Request #62577 · dotnet/runtime · GitHub
Skip to content

Improve String.Substring performance - #62577

Closed
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf
Closed

Improve String.Substring performance#62577
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf

Conversation

@nietras

@nietrasnietras commented Dec 9, 2021

Copy link
Copy Markdown
Contributor

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@ghostghost added the community-contribution Indicates that the PR has been added by a community member label Dec 9, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

Using Span.Slice guard clauses for perf. Regressions I believe are due to benchmark issues. In any case, is changing guard clauses to this an acceptable change? Should exception messages be preserved?

// * Warnings *
MultimodalDistribution
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.56)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.89)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.75)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 3)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.78)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.83)
BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-ICQHYC : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-KPKVVD : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsilMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0815.3259 ns0.9471 ns1.0906 ns15.6270 ns11.1665 ns16.1263 ns1.000.000.002440 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz087.1677 ns0.3913 ns0.4506 ns7.2510 ns6.2912 ns7.9841 ns0.470.050.002440 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0?1.0918 ns0.0057 ns0.0051 ns1.0905 ns1.0861 ns1.1031 ns1.000.00--
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz0?0.4470 ns0.0059 ns0.0055 ns0.4444 ns0.4404 ns0.4585 ns0.410.01--
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz747.6604 ns0.3992 ns0.4597 ns7.7002 ns6.7318 ns8.2808 ns1.000.000.001932 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7413.2061 ns0.9380 ns1.0802 ns13.5553 ns11.1386 ns14.6399 ns1.730.210.001932 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz7?9.0171 ns1.6091 ns1.8531 ns9.2496 ns5.2419 ns12.1909 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7?14.7738 ns0.7346 ns0.8165 ns14.9451 ns11.5924 ns15.5339 ns1.730.440.002948 B
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10112.1967 ns1.0340 ns1.1908 ns12.3111 ns10.2499 ns14.3337 ns1.000.000.001424 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz1014.3119 ns0.0334 ns0.0279 ns4.3081 ns4.2731 ns4.3719 ns0.350.040.001424 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10?10.0238 ns0.5880 ns0.6772 ns10.1232 ns8.0144 ns10.7685 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz10?10.8206 ns0.7301 ns0.8407 ns10.7733 ns8.8957 ns11.9972 ns1.090.140.002948 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz19?10.1852 ns0.8323 ns0.9585 ns10.1999 ns8.4898 ns11.9423 ns1.000.000.001424 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz19?6.4746 ns0.4515 ns0.5200 ns6.2816 ns5.9014 ns7.5252 ns0.640.080.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B
Author:nietras
Assignees:-
Labels:

area-System.Runtime, community-contribution

Milestone:-

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@adamsitnikadamsitnik added the tenet-performance Performance related issue label Dec 10, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks tried to address feedback. Still don't know what to do about getting reliable benchmark tests. These do not show expected results currently but are also volatile. Substring(0) still faster of course.

Tried running StringReaderReadLineTests.ReadLine tests but no significant change there except [0, 0] case, so this PR does not appear to currently improve on that.

Let me know, what next steps you guys think should be.

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@nietras

Copy link
Copy Markdown
ContributorAuthor

Cleaned up. Benchmarks below, only significant improvements for when returning same string or empty. Regressions are often due to variance in benchmarks. Main point probably is throw code is separated out reducing code size of the method. I have included the cost of new'ing a similar string for comparison here.

Substring(int)

MethodJobToolchainStartMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADIm01.933 ns0.0043 ns0.0038 ns1.934 ns1.926 ns1.939 ns1.000.00----867 B
SubstringJob-WPCFEPpr01.283 ns0.0061 ns0.0057 ns1.282 ns1.275 ns1.294 ns0.660.00----471 B
SubstringJob-WNUADIm85.323 ns0.0295 ns0.0276 ns5.324 ns5.286 ns5.382 ns1.000.000.0024--40 B867 B
SubstringJob-WPCFEPpr85.906 ns0.0271 ns0.0240 ns5.895 ns5.875 ns5.947 ns1.110.010.0024--40 B471 B
NewStringJob-WNUADIm83.755 ns0.0320 ns0.0299 ns3.760 ns3.706 ns3.805 ns1.000.000.0024--40 B40 B
NewStringJob-WPCFEPpr83.758 ns0.0170 ns0.0151 ns3.760 ns3.730 ns3.782 ns1.000.010.0024--40 B40 B
SubstringJob-WNUADIm161.570 ns0.0473 ns0.0419 ns1.552 ns1.538 ns1.655 ns1.000.00----867 B
SubstringJob-WPCFEPpr161.578 ns0.0353 ns0.0330 ns1.594 ns1.511 ns1.614 ns1.000.03----471 B
NewStringJob-WNUADIm161.371 ns0.0218 ns0.0193 ns1.372 ns1.343 ns1.413 ns1.000.00----40 B
NewStringJob-WPCFEPpr161.296 ns0.0040 ns0.0036 ns1.296 ns1.290 ns1.302 ns0.950.01----40 B

Substring(int,int)

MethodJobToolchainSliceMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADImI: 0 L: 01.713 ns0.0042 ns0.0039 ns1.712 ns1.707 ns1.721 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L: 01.292 ns0.0063 ns0.0059 ns1.290 ns1.283 ns1.303 ns0.750.00----483 B
NewStringJob-WNUADImI: 0 L: 01.702 ns0.0046 ns0.0043 ns1.701 ns1.693 ns1.710 ns1.000.00----27 B
NewStringJob-WPCFEPprI: 0 L: 01.501 ns0.0034 ns0.0029 ns1.501 ns1.495 ns1.505 ns0.880.00----27 B
SubstringJob-WNUADImI: 0 L:161.923 ns0.0025 ns0.0022 ns1.924 ns1.918 ns1.926 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L:161.504 ns0.0027 ns0.0026 ns1.505 ns1.499 ns1.509 ns0.780.00----483 B
NewStringJob-WNUADImI: 0 L:164.252 ns0.0147 ns0.0130 ns4.255 ns4.218 ns4.268 ns1.000.000.0033--56 B27 B
NewStringJob-WPCFEPprI: 0 L:164.214 ns0.0461 ns0.0409 ns4.207 ns4.163 ns4.280 ns0.990.010.0033--56 B27 B
SubstringJob-WNUADImI: 1 L: 15.146 ns0.0528 ns0.0494 ns5.154 ns4.973 ns5.177 ns1.000.000.0014--24 B864 B
SubstringJob-WPCFEPprI: 1 L: 15.194 ns0.0180 ns0.0159 ns5.194 ns5.161 ns5.221 ns1.010.010.0014--24 B483 B
NewStringJob-WNUADImI: 1 L: 13.656 ns0.0179 ns0.0167 ns3.658 ns3.623 ns3.678 ns1.000.000.0014--24 B27 B
NewStringJob-WPCFEPprI: 1 L: 13.685 ns0.0186 ns0.0174 ns3.684 ns3.656 ns3.716 ns1.010.010.0014--24 B27 B
SubstringJob-WNUADImI: 2 L: 25.554 ns0.0214 ns0.0189 ns5.556 ns5.522 ns5.589 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 2 L: 25.375 ns0.0403 ns0.0377 ns5.368 ns5.322 ns5.445 ns0.970.010.0019--32 B483 B
NewStringJob-WNUADImI: 2 L: 23.842 ns0.0142 ns0.0133 ns3.842 ns3.815 ns3.868 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 2 L: 23.871 ns0.0179 ns0.0168 ns3.866 ns3.848 ns3.905 ns1.010.010.0019--32 B27 B
SubstringJob-WNUADImI: 3 L: 35.708 ns0.1507 ns0.1736 ns5.616 ns5.568 ns6.077 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 3 L: 35.395 ns0.0215 ns0.0179 ns5.399 ns5.346 ns5.413 ns0.940.030.0019--32 B483 B
NewStringJob-WNUADImI: 3 L: 33.859 ns0.0231 ns0.0205 ns3.856 ns3.835 ns3.907 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 3 L: 33.820 ns0.0243 ns0.0227 ns3.827 ns3.787 ns3.857 ns0.990.010.0019--32 B27 B
SubstringJob-WNUADImI: 4 L: 45.352 ns0.0226 ns0.0176 ns5.354 ns5.314 ns5.380 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 4 L: 413.276 ns0.2797 ns0.2872 ns13.330 ns12.226 ns13.534 ns2.470.060.0019--32 B483 B
NewStringJob-WNUADImI: 4 L: 43.825 ns0.0109 ns0.0102 ns3.827 ns3.807 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 4 L: 43.815 ns0.0236 ns0.0221 ns3.812 ns3.791 ns3.864 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 5 L: 55.334 ns0.0321 ns0.0300 ns5.327 ns5.295 ns5.385 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 5 L: 55.618 ns0.0410 ns0.0343 ns5.614 ns5.566 ns5.694 ns1.050.010.0019--32 B483 B
NewStringJob-WNUADImI: 5 L: 53.813 ns0.0179 ns0.0168 ns3.816 ns3.786 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 5 L: 53.801 ns0.0241 ns0.0213 ns3.802 ns3.771 ns3.836 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 6 L: 65.492 ns0.0333 ns0.0311 ns5.489 ns5.447 ns5.544 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 6 L: 65.679 ns0.0306 ns0.0286 ns5.675 ns5.640 ns5.724 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 6 L: 63.986 ns0.0096 ns0.0090 ns3.985 ns3.970 ns4.005 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 6 L: 63.958 ns0.0164 ns0.0146 ns3.957 ns3.939 ns3.984 ns0.990.000.0024--40 B27 B
SubstringJob-WNUADImI: 7 L: 75.511 ns0.0220 ns0.0205 ns5.513 ns5.476 ns5.545 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 7 L: 75.702 ns0.0298 ns0.0279 ns5.700 ns5.645 ns5.745 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 7 L: 73.853 ns0.0175 ns0.0164 ns3.854 ns3.825 ns3.880 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 7 L: 73.988 ns0.0207 ns0.0184 ns3.987 ns3.962 ns4.020 ns1.040.010.0024--40 B27 B
SubstringJob-WNUADImI: 8 L: 85.491 ns0.0296 ns0.0277 ns5.499 ns5.449 ns5.538 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 8 L: 85.680 ns0.0358 ns0.0335 ns5.685 ns5.623 ns5.742 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 8 L: 84.015 ns0.0120 ns0.0112 ns4.015 ns3.986 ns4.030 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 8 L: 83.971 ns0.0296 ns0.0277 ns3.972 ns3.929 ns4.018 ns0.990.010.0024--40 B27 B

ASM

M

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1B31ACA8048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrdi,rcxmovesi,edxtestesi,esijl near ptr M01_L03movebx,[rdi+8]cmpebx,esijl near ptr M01_L04testr8d,r8djl near ptr M01_L05movecx,ebxsubecx,r8dcmpecx,esijl near ptr M01_L06testr8d,r8dje short M01_L02testesi,esije short M01_L01M01_L00:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrdi,0Cmovedx,esileardx,[rdi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L01:cmpebx,r8djne short M01_L00movrax,rdiaddrsp,20poprbxpoprsipoprdiretM01_L02:movrax,1B31ACA3020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L03:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A855movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L04:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A891movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L05:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A519movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L06:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,9EBDmovrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWint3; Total bytes of code 512

PR

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1FA62268048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrsi,rcxmovedi,edxmovecx,[rsi+8]moveax,edimovedx,r8daddrax,rdxmovedx,ecxcmprax,rdxja short M01_L02testr8d,r8djne short M01_L00movrax,1FA62263020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L00:cmpr8d,ecxjne short M01_L01movrax,rsiaddrsp,20poprbxpoprsipoprdiretM01_L01:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrsi,0Cmovedx,edileardx,[rsi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L02:movrcx,rsimovedx,edicall System.String.ThrowSubstringArgumentOutRangeException(Int32, Int32)int3; Total bytes of code 131

@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks should I close this or?

@danmoseley

Copy link
Copy Markdown
Contributor

I'm going to close this as the improvements are as you say marginal. Thank you for the attempt!

@ghostghost locked as resolved and limited conversation to collaborators Jun 1, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtimecommunity-contributionIndicates that the PR has been added by a community membertenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@nietras@danmoseley@GrabYourPitchforks@jkotas@deeprobin@vcsjones@jeffhandley@adamsitnik
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Improve String.Substring performance by nietras · Pull Request #62577 · dotnet/runtime · GitHub
Skip to content

Improve String.Substring performance - #62577

Closed
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf
Closed

Improve String.Substring performance#62577
nietras wants to merge 8 commits into
dotnet:mainfrom
nietras:substring-perf

Conversation

@nietras

@nietrasnietras commented Dec 9, 2021

Copy link
Copy Markdown
Contributor

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@ghostghost added the community-contribution Indicates that the PR has been added by a community member label Dec 9, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

Using Span.Slice guard clauses for perf. Regressions I believe are due to benchmark issues. In any case, is changing guard clauses to this an acceptable change? Should exception messages be preserved?

// * Warnings *
MultimodalDistribution
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.56)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.89)
Perf_String.Substring_IntInt: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.75)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-m\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 3)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution is bimodal (mValue = 3.78)
Perf_String.Substring_Int: PowerPlanMode=00000000-0000-0000-0000-000000000000, Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog, Toolchain=\runtime-pr\artifacts\bin\testhost\net7.0-windows-Release-x64\shared\Microsoft.NETCore.App\7.0.0\CoreRun.exe, IterationTime=250.0000 ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> It seems that the distribution can have several modes (mValue = 2.83)
BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-ICQHYC : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-KPKVVD : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsilMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0815.3259 ns0.9471 ns1.0906 ns15.6270 ns11.1665 ns16.1263 ns1.000.000.002440 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz087.1677 ns0.3913 ns0.4506 ns7.2510 ns6.2912 ns7.9841 ns0.470.050.002440 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz0?1.0918 ns0.0057 ns0.0051 ns1.0905 ns1.0861 ns1.1031 ns1.000.00--
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz0?0.4470 ns0.0059 ns0.0055 ns0.4444 ns0.4404 ns0.4585 ns0.410.01--
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz747.6604 ns0.3992 ns0.4597 ns7.7002 ns6.7318 ns8.2808 ns1.000.000.001932 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7413.2061 ns0.9380 ns1.0802 ns13.5553 ns11.1386 ns14.6399 ns1.730.210.001932 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz7?9.0171 ns1.6091 ns1.8531 ns9.2496 ns5.2419 ns12.1909 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz7?14.7738 ns0.7346 ns0.8165 ns14.9451 ns11.5924 ns15.5339 ns1.730.440.002948 B
Substring_IntIntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10112.1967 ns1.0340 ns1.1908 ns12.3111 ns10.2499 ns14.3337 ns1.000.000.001424 B
Substring_IntIntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz1014.3119 ns0.0334 ns0.0279 ns4.3081 ns4.2731 ns4.3719 ns0.350.040.001424 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz10?10.0238 ns0.5880 ns0.6772 ns10.1232 ns8.0144 ns10.7685 ns1.000.000.002948 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz10?10.8206 ns0.7301 ns0.8407 ns10.7733 ns8.8957 ns11.9972 ns1.090.140.002948 B
Substring_IntJob-ICQHYCmdzsdzsDDZSDZSDZSddsz19?10.1852 ns0.8323 ns0.9585 ns10.1999 ns8.4898 ns11.9423 ns1.000.000.001424 B
Substring_IntJob-KPKVVDprdzsdzsDDZSDZSDZSddsz19?6.4746 ns0.4515 ns0.5200 ns6.2816 ns5.9014 ns7.5252 ns0.640.080.001424 B

@ghost

ghost commented Dec 9, 2021

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

WIP just to get the ball rolling regarding discussion of changing exception messages or similar. cc @adamsitnik@GrabYourPitchforks@stephentoub as discussed in #60463

Change to Substring(int startIndex) improvements (note highly volatile). This comes with all the caveats of potential increases code size etc.

BenchmarkDotNet=v0.13.1.1620-nightly, OS=Windows 10.0.19044.1348 (21H2)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=7.0.100-alpha.1.21568.2
[Host] : .NET 6.0.0 (6.0.21.48005), X64 RyuJIT
Job-FWUOEW : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
Job-HPGSVV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable,-bl:benchmarkdotnet.binlog IterationTime=250.0000 ms MaxIterationCount=20 MinIterationCount=15 WarmupCount=1 
MethodJobToolchainsiMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Allocated
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz01.1022 ns0.0068 ns0.0064 ns1.1049 ns1.0853 ns1.1092 ns1.000.00--
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz00.4543 ns0.0069 ns0.0065 ns0.4541 ns0.4382 ns0.4658 ns0.410.01--
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz78.6245 ns2.2287 ns2.5666 ns9.5946 ns5.4283 ns12.5358 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz77.1005 ns1.1957 ns1.3770 ns6.5014 ns5.6306 ns9.5741 ns0.880.220.002948 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1010.3062 ns0.6670 ns0.7681 ns10.5393 ns8.8757 ns11.3953 ns1.000.000.002948 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz107.5328 ns0.6341 ns0.7302 ns7.4150 ns6.3654 ns9.1740 ns0.730.070.002848 B
Substring_IntJob-FWUOEWmdzsdzsDDZSDZSDZSddsz1912.9457 ns1.6428 ns1.8919 ns13.3785 ns5.5022 ns14.3101 ns1.000.000.001424 B
Substring_IntJob-HPGSVVprdzsdzsDDZSDZSDZSddsz196.8188 ns0.9429 ns1.0858 ns6.6514 ns5.0843 ns9.0324 ns0.550.180.001424 B
Author:nietras
Assignees:-
Labels:

area-System.Runtime, community-contribution

Milestone:-

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@adamsitnikadamsitnik added the tenet-performance Performance related issue label Dec 10, 2021
@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks tried to address feedback. Still don't know what to do about getting reliable benchmark tests. These do not show expected results currently but are also volatile. Substring(0) still faster of course.

Tried running StringReaderReadLineTests.ReadLine tests but no significant change there except [0, 0] case, so this PR does not appear to currently improve on that.

Let me know, what next steps you guys think should be.

Comment threadsrc/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
@nietras

Copy link
Copy Markdown
ContributorAuthor

Cleaned up. Benchmarks below, only significant improvements for when returning same string or empty. Regressions are often due to variance in benchmarks. Main point probably is throw code is separated out reducing code size of the method. I have included the cost of new'ing a similar string for comparison here.

Substring(int)

MethodJobToolchainStartMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADIm01.933 ns0.0043 ns0.0038 ns1.934 ns1.926 ns1.939 ns1.000.00----867 B
SubstringJob-WPCFEPpr01.283 ns0.0061 ns0.0057 ns1.282 ns1.275 ns1.294 ns0.660.00----471 B
SubstringJob-WNUADIm85.323 ns0.0295 ns0.0276 ns5.324 ns5.286 ns5.382 ns1.000.000.0024--40 B867 B
SubstringJob-WPCFEPpr85.906 ns0.0271 ns0.0240 ns5.895 ns5.875 ns5.947 ns1.110.010.0024--40 B471 B
NewStringJob-WNUADIm83.755 ns0.0320 ns0.0299 ns3.760 ns3.706 ns3.805 ns1.000.000.0024--40 B40 B
NewStringJob-WPCFEPpr83.758 ns0.0170 ns0.0151 ns3.760 ns3.730 ns3.782 ns1.000.010.0024--40 B40 B
SubstringJob-WNUADIm161.570 ns0.0473 ns0.0419 ns1.552 ns1.538 ns1.655 ns1.000.00----867 B
SubstringJob-WPCFEPpr161.578 ns0.0353 ns0.0330 ns1.594 ns1.511 ns1.614 ns1.000.03----471 B
NewStringJob-WNUADIm161.371 ns0.0218 ns0.0193 ns1.372 ns1.343 ns1.413 ns1.000.00----40 B
NewStringJob-WPCFEPpr161.296 ns0.0040 ns0.0036 ns1.296 ns1.290 ns1.302 ns0.950.01----40 B

Substring(int,int)

MethodJobToolchainSliceMeanErrorStdDevMedianMinMaxRatioRatioSDGen 0Gen 1Gen 2AllocatedCode Size
SubstringJob-WNUADImI: 0 L: 01.713 ns0.0042 ns0.0039 ns1.712 ns1.707 ns1.721 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L: 01.292 ns0.0063 ns0.0059 ns1.290 ns1.283 ns1.303 ns0.750.00----483 B
NewStringJob-WNUADImI: 0 L: 01.702 ns0.0046 ns0.0043 ns1.701 ns1.693 ns1.710 ns1.000.00----27 B
NewStringJob-WPCFEPprI: 0 L: 01.501 ns0.0034 ns0.0029 ns1.501 ns1.495 ns1.505 ns0.880.00----27 B
SubstringJob-WNUADImI: 0 L:161.923 ns0.0025 ns0.0022 ns1.924 ns1.918 ns1.926 ns1.000.00----864 B
SubstringJob-WPCFEPprI: 0 L:161.504 ns0.0027 ns0.0026 ns1.505 ns1.499 ns1.509 ns0.780.00----483 B
NewStringJob-WNUADImI: 0 L:164.252 ns0.0147 ns0.0130 ns4.255 ns4.218 ns4.268 ns1.000.000.0033--56 B27 B
NewStringJob-WPCFEPprI: 0 L:164.214 ns0.0461 ns0.0409 ns4.207 ns4.163 ns4.280 ns0.990.010.0033--56 B27 B
SubstringJob-WNUADImI: 1 L: 15.146 ns0.0528 ns0.0494 ns5.154 ns4.973 ns5.177 ns1.000.000.0014--24 B864 B
SubstringJob-WPCFEPprI: 1 L: 15.194 ns0.0180 ns0.0159 ns5.194 ns5.161 ns5.221 ns1.010.010.0014--24 B483 B
NewStringJob-WNUADImI: 1 L: 13.656 ns0.0179 ns0.0167 ns3.658 ns3.623 ns3.678 ns1.000.000.0014--24 B27 B
NewStringJob-WPCFEPprI: 1 L: 13.685 ns0.0186 ns0.0174 ns3.684 ns3.656 ns3.716 ns1.010.010.0014--24 B27 B
SubstringJob-WNUADImI: 2 L: 25.554 ns0.0214 ns0.0189 ns5.556 ns5.522 ns5.589 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 2 L: 25.375 ns0.0403 ns0.0377 ns5.368 ns5.322 ns5.445 ns0.970.010.0019--32 B483 B
NewStringJob-WNUADImI: 2 L: 23.842 ns0.0142 ns0.0133 ns3.842 ns3.815 ns3.868 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 2 L: 23.871 ns0.0179 ns0.0168 ns3.866 ns3.848 ns3.905 ns1.010.010.0019--32 B27 B
SubstringJob-WNUADImI: 3 L: 35.708 ns0.1507 ns0.1736 ns5.616 ns5.568 ns6.077 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 3 L: 35.395 ns0.0215 ns0.0179 ns5.399 ns5.346 ns5.413 ns0.940.030.0019--32 B483 B
NewStringJob-WNUADImI: 3 L: 33.859 ns0.0231 ns0.0205 ns3.856 ns3.835 ns3.907 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 3 L: 33.820 ns0.0243 ns0.0227 ns3.827 ns3.787 ns3.857 ns0.990.010.0019--32 B27 B
SubstringJob-WNUADImI: 4 L: 45.352 ns0.0226 ns0.0176 ns5.354 ns5.314 ns5.380 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 4 L: 413.276 ns0.2797 ns0.2872 ns13.330 ns12.226 ns13.534 ns2.470.060.0019--32 B483 B
NewStringJob-WNUADImI: 4 L: 43.825 ns0.0109 ns0.0102 ns3.827 ns3.807 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 4 L: 43.815 ns0.0236 ns0.0221 ns3.812 ns3.791 ns3.864 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 5 L: 55.334 ns0.0321 ns0.0300 ns5.327 ns5.295 ns5.385 ns1.000.000.0019--32 B864 B
SubstringJob-WPCFEPprI: 5 L: 55.618 ns0.0410 ns0.0343 ns5.614 ns5.566 ns5.694 ns1.050.010.0019--32 B483 B
NewStringJob-WNUADImI: 5 L: 53.813 ns0.0179 ns0.0168 ns3.816 ns3.786 ns3.839 ns1.000.000.0019--32 B27 B
NewStringJob-WPCFEPprI: 5 L: 53.801 ns0.0241 ns0.0213 ns3.802 ns3.771 ns3.836 ns1.000.010.0019--32 B27 B
SubstringJob-WNUADImI: 6 L: 65.492 ns0.0333 ns0.0311 ns5.489 ns5.447 ns5.544 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 6 L: 65.679 ns0.0306 ns0.0286 ns5.675 ns5.640 ns5.724 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 6 L: 63.986 ns0.0096 ns0.0090 ns3.985 ns3.970 ns4.005 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 6 L: 63.958 ns0.0164 ns0.0146 ns3.957 ns3.939 ns3.984 ns0.990.000.0024--40 B27 B
SubstringJob-WNUADImI: 7 L: 75.511 ns0.0220 ns0.0205 ns5.513 ns5.476 ns5.545 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 7 L: 75.702 ns0.0298 ns0.0279 ns5.700 ns5.645 ns5.745 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 7 L: 73.853 ns0.0175 ns0.0164 ns3.854 ns3.825 ns3.880 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 7 L: 73.988 ns0.0207 ns0.0184 ns3.987 ns3.962 ns4.020 ns1.040.010.0024--40 B27 B
SubstringJob-WNUADImI: 8 L: 85.491 ns0.0296 ns0.0277 ns5.499 ns5.449 ns5.538 ns1.000.000.0024--40 B864 B
SubstringJob-WPCFEPprI: 8 L: 85.680 ns0.0358 ns0.0335 ns5.685 ns5.623 ns5.742 ns1.030.010.0024--40 B483 B
NewStringJob-WNUADImI: 8 L: 84.015 ns0.0120 ns0.0112 ns4.015 ns3.986 ns4.030 ns1.000.000.0024--40 B27 B
NewStringJob-WPCFEPprI: 8 L: 83.971 ns0.0296 ns0.0277 ns3.972 ns3.929 ns4.018 ns0.990.010.0024--40 B27 B

ASM

M

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1B31ACA8048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrdi,rcxmovesi,edxtestesi,esijl near ptr M01_L03movebx,[rdi+8]cmpebx,esijl near ptr M01_L04testr8d,r8djl near ptr M01_L05movecx,ebxsubecx,r8dcmpecx,esijl near ptr M01_L06testr8d,r8dje short M01_L02testesi,esije short M01_L01M01_L00:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrdi,0Cmovedx,esileardx,[rdi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L01:cmpebx,r8djne short M01_L00movrax,rdiaddrsp,20poprbxpoprsipoprdiretM01_L02:movrax,1B31ACA3020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L03:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A855movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L04:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,19A4movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A891movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L05:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,0A519movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWM01_L06:movrcx,offset MT_System.ArgumentOutOfRangeExceptioncall CORINFO_HELP_NEWSFASTmovrsi,raxmovecx,57movrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrdi,raxmovecx,9EBDmovrdx,7FF7DA114000call CORINFO_HELP_STRCNSmovrcx,raxcall System.SR.GetResourceString(System.String)movr8,raxmovrdx,rdimovrcx,rsicall System.ArgumentOutOfRangeException..ctor(System.String, System.String)movrcx,rsicall CORINFO_HELP_THROWint3; Total bytes of code 512

PR

; System.Tests.Perf_String_Substring_IntInt.Substring()subrsp,28movr8,[rcx+8]movrcx,1FA62268048movrcx,[rcx]movedx,[r8+8]movr8d,[r8+0C]call System.String.Substring(Int32, Int32)nopaddrsp,28ret; Total bytes of code 40
; System.String.Substring(Int32, Int32)pushrdipushrsipushrbxsubrsp,20movrsi,rcxmovedi,edxmovecx,[rsi+8]moveax,edimovedx,r8daddrax,rdxmovedx,ecxcmprax,rdxja short M01_L02testr8d,r8djne short M01_L00movrax,1FA62263020movrax,[rax]addrsp,20poprbxpoprsipoprdiretM01_L00:cmpr8d,ecxjne short M01_L01movrax,rsiaddrsp,20poprbxpoprsipoprdiretM01_L01:movecx,r8dcall System.String.FastAllocateString(Int32)movrbx,raxmovr8d,[rbx+8]learcx,[rbx+0C]addrsi,0Cmovedx,edileardx,[rsi+rdx*2]addr8,r8call System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr)movrax,rbxaddrsp,20poprbxpoprsipoprdiretM01_L02:movrcx,rsimovedx,edicall System.String.ThrowSubstringArgumentOutRangeException(Int32, Int32)int3; Total bytes of code 131

@nietras

Copy link
Copy Markdown
ContributorAuthor

@GrabYourPitchforks should I close this or?

@danmoseley

Copy link
Copy Markdown
Contributor

I'm going to close this as the improvements are as you say marginal. Thank you for the attempt!

@ghostghost locked as resolved and limited conversation to collaborators Jun 1, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtimecommunity-contributionIndicates that the PR has been added by a community membertenet-performancePerformance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@nietras@danmoseley@GrabYourPitchforks@jkotas@deeprobin@vcsjones@jeffhandley@adamsitnik