Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

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

Provide feedback

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

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Avoid most of the (small) regressions for seeded and derived Random instances#57530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
stephentoub merged 4 commits intodotnet:mainfromstephentoub:randomperf
Aug 17, 2021

Conversation

@stephentoub
Copy link
Member

Fixes#57272
cc:@GrabYourPitchforks,@tannergooding

When we introduced the new Random algorithm, we did so by factoring the old algorithm out into an implementation strategy class that is instantiated for all use other thannew Random(). This ends up penalizing other uses (providing a seed and/or deriving from Random) by adding more virtual dispatch than is strictly necessary, in particular fornew Random(seed). This PR negates most but not all of that (expected and small) regression by splitting the compat implementation in two, one class fornew Random(seed) and one fornew DerivedRandom()/new DerivedRandom(seed); the former no longer needs to make virtual calls back out to the parent type. The former is also one that a consumer can't really do anything to improve, whereas in the derived case, the derivation may override to provide a more optimal implementation.

Additionally, we introduced NextInt64 in this release, which means we can still change the algorithm it uses / what derived methods it calls. This changes the implementation to make 3 calls instead of 8.

I also experimented but ultimately decided against the proposal in the cited issue to lazily-initialize the array for a derived type the first time it uses the state. The lazy initialization was measurable, and most derived types I found do end up using one of the base methods (even though obviously it could be used purely as an abstraction). We can reconsider in the future.

TypeMethodJobToolchainMeanErrorStdDevRatio
RandomDerivedNextJob-MYJVJF\main\corerun.exe8.256 ns0.1118 ns0.0873 ns1.00
RandomDerivedNextJob-SVARSY\pr\corerun.exe8.216 ns0.0484 ns0.0452 ns1.00
RandomSeedNextJob-MYJVJF\main\corerun.exe9.153 ns0.0145 ns0.0129 ns1.00
RandomSeedNextJob-SVARSY\pr\corerun.exe8.282 ns0.0595 ns0.0527 ns0.90
RandomDerivedNextMaxJob-MYJVJF\main\corerun.exe13.140 ns0.0733 ns0.0685 ns1.00
RandomDerivedNextMaxJob-SVARSY\pr\corerun.exe12.978 ns0.0863 ns0.0765 ns0.99
RandomSeedNextMaxJob-MYJVJF\main\corerun.exe12.890 ns0.0623 ns0.0583 ns1.00
RandomSeedNextMaxJob-SVARSY\pr\corerun.exe9.623 ns0.0717 ns0.0636 ns0.75
RandomDerivedNextMinMaxJob-MYJVJF\main\corerun.exe13.435 ns0.0845 ns0.0749 ns1.00
RandomDerivedNextMinMaxJob-SVARSY\pr\corerun.exe13.756 ns0.1365 ns0.1277 ns1.02
RandomSeedNextMinMaxJob-MYJVJF\main\corerun.exe13.366 ns0.0667 ns0.0591 ns1.00
RandomSeedNextMinMaxJob-SVARSY\pr\corerun.exe10.686 ns0.0471 ns0.0441 ns0.80
RandomDerivedNextInt64Job-MYJVJF\main\corerun.exe78.238 ns0.2008 ns0.1677 ns1.00
RandomDerivedNextInt64Job-SVARSY\pr\corerun.exe43.399 ns0.6699 ns0.5939 ns0.55
RandomSeedNextInt64Job-MYJVJF\main\corerun.exe74.153 ns0.4255 ns0.3772 ns1.00
RandomSeedNextInt64Job-SVARSY\pr\corerun.exe23.521 ns0.0418 ns0.0327 ns0.32
RandomDerivedNextInt64MaxJob-MYJVJF\main\corerun.exe113.069 ns0.5636 ns0.5272 ns1.00
RandomDerivedNextInt64MaxJob-SVARSY\pr\corerun.exe68.993 ns0.8238 ns0.6431 ns0.61
RandomSeedNextInt64MaxJob-MYJVJF\main\corerun.exe117.979 ns0.6594 ns0.8098 ns1.00
RandomSeedNextInt64MaxJob-SVARSY\pr\corerun.exe40.567 ns0.1941 ns0.1816 ns0.34
RandomDerivedNextInt64MinMaxJob-MYJVJF\main\corerun.exe112.131 ns0.3612 ns0.3017 ns1.00
RandomDerivedNextInt64MinMaxJob-SVARSY\pr\corerun.exe67.423 ns0.1900 ns0.1684 ns0.60
RandomSeedNextInt64MinMaxJob-MYJVJF\main\corerun.exe116.348 ns0.4822 ns0.4275 ns1.00
RandomSeedNextInt64MinMaxJob-SVARSY\pr\corerun.exe41.832 ns0.2730 ns0.2554 ns0.36
RandomDerivedNextSingleJob-MYJVJF\main\corerun.exe11.778 ns0.0799 ns0.0709 ns1.00
RandomDerivedNextSingleJob-SVARSY\pr\corerun.exe11.889 ns0.0619 ns0.0517 ns1.01
RandomSeedNextSingleJob-MYJVJF\main\corerun.exe11.718 ns0.0680 ns0.0636 ns1.00
RandomSeedNextSingleJob-SVARSY\pr\corerun.exe9.043 ns0.0333 ns0.0295 ns0.77
RandomDerivedNextDoubleJob-MYJVJF\main\corerun.exe12.318 ns0.1869 ns0.1561 ns1.00
RandomDerivedNextDoubleJob-SVARSY\pr\corerun.exe11.460 ns0.0667 ns0.0557 ns0.93
RandomSeedNextDoubleJob-MYJVJF\main\corerun.exe11.387 ns0.0752 ns0.0704 ns1.00
RandomSeedNextDoubleJob-SVARSY\pr\corerun.exe8.947 ns0.1129 ns0.1056 ns0.79
RandomDerivedNextBytesArrayJob-MYJVJF\main\corerun.exe62,459.876 ns384.2017 ns340.5848 ns1.00
RandomDerivedNextBytesArrayJob-SVARSY\pr\corerun.exe68,100.319 ns321.5379 ns300.7667 ns1.09
RandomSeedNextBytesArrayJob-MYJVJF\main\corerun.exe62,466.595 ns260.9075 ns231.2877 ns1.00
RandomSeedNextBytesArrayJob-SVARSY\pr\corerun.exe68,294.184 ns755.4770 ns706.6736 ns1.09
RandomDerivedNextBytesSpanJob-MYJVJF\main\corerun.exe80,514.292 ns600.0956 ns561.3298 ns1.00
RandomDerivedNextBytesSpanJob-SVARSY\pr\corerun.exe81,179.767 ns634.0510 ns529.4613 ns1.01
RandomSeedNextBytesSpanJob-MYJVJF\main\corerun.exe85,292.812 ns648.1876 ns606.3151 ns1.00
RandomSeedNextBytesSpanJob-SVARSY\pr\corerun.exe64,892.721 ns594.0378 ns555.6634 ns0.76
publicclassRandomSeed:TestBase{publicoverrideRandomCreate()=>newRandom(42);}publicclassRandomDerived:TestBase{publicoverrideRandomCreate()=>newDerivedRandom();privatesealedclassDerivedRandom:Random{}}publicabstractclassTestBase{privatebyte[]_buffer=newbyte[10_000];privateRandom_random;publicabstractRandomCreate();[GlobalSetup]publicvoidSetup()=>_random=Create();[Benchmark]publicRandomCtor()=>Create();[Benchmark]publicintNext()=>_random.Next();[Benchmark]publicvoidNextMax()=>_random.Next(42);[Benchmark]publicvoidNextMinMax()=>_random.Next(0,42);[Benchmark]publicvoidNextInt64()=>_random.NextInt64();[Benchmark]publicvoidNextInt64Max()=>_random.NextInt64(42);[Benchmark]publicvoidNextInt64MinMax()=>_random.NextInt64(0,42);[Benchmark]publicvoidNextSingle()=>_random.NextSingle();[Benchmark]publicvoidNextDouble()=>_random.NextDouble();[Benchmark]publicvoidNextBytesArray()=>_random.NextBytes(_buffer);[Benchmark]publicvoidNextBytesSpan()=>_random.NextBytes((Span<byte>)_buffer);}

I'm not currently sure whyNextBytesArray gets a little slower... that'll need more investigation.

rgwood and pentp reacted with rocket emoji
@ghost
Copy link

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 onearea label.

@ghost
Copy link

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

Issue Details

Fixes#57272
cc:@GrabYourPitchforks,@tannergooding

When we introduced the new Random algorithm, we did so by factoring the old algorithm out into an implementation strategy class that is instantiated for all use other thannew Random(). This ends up penalizing other uses (providing a seed and/or deriving from Random) by adding more virtual dispatch than is strictly necessary, in particular fornew Random(seed). This PR negates most but not all of that (expected and small) regression by splitting the compat implementation in two, one class fornew Random(seed) and one fornew DerivedRandom()/new DerivedRandom(seed); the former no longer needs to make virtual calls back out to the parent type. The former is also one that a consumer can't really do anything to improve, whereas in the derived case, the derivation may override to provide a more optimal implementation.

Additionally, we introduced NextInt64 in this release, which means we can still change the algorithm it uses / what derived methods it calls. This changes the implementation to make 3 calls instead of 8.

I also experimented but ultimately decided against the proposal in the cited issue to lazily-initialize the array for a derived type the first time it uses the state. The lazy initialization was measurable, and most derived types I found do end up using one of the base methods (even though obviously it could be used purely as an abstraction). We can reconsider in the future.

TypeMethodJobToolchainMeanErrorStdDevRatio
RandomDerivedNextJob-MYJVJF\main\corerun.exe8.256 ns0.1118 ns0.0873 ns1.00
RandomDerivedNextJob-SVARSY\pr\corerun.exe8.216 ns0.0484 ns0.0452 ns1.00
RandomSeedNextJob-MYJVJF\main\corerun.exe9.153 ns0.0145 ns0.0129 ns1.00
RandomSeedNextJob-SVARSY\pr\corerun.exe8.282 ns0.0595 ns0.0527 ns0.90
RandomDerivedNextMaxJob-MYJVJF\main\corerun.exe13.140 ns0.0733 ns0.0685 ns1.00
RandomDerivedNextMaxJob-SVARSY\pr\corerun.exe12.978 ns0.0863 ns0.0765 ns0.99
RandomSeedNextMaxJob-MYJVJF\main\corerun.exe12.890 ns0.0623 ns0.0583 ns1.00
RandomSeedNextMaxJob-SVARSY\pr\corerun.exe9.623 ns0.0717 ns0.0636 ns0.75
RandomDerivedNextMinMaxJob-MYJVJF\main\corerun.exe13.435 ns0.0845 ns0.0749 ns1.00
RandomDerivedNextMinMaxJob-SVARSY\pr\corerun.exe13.756 ns0.1365 ns0.1277 ns1.02
RandomSeedNextMinMaxJob-MYJVJF\main\corerun.exe13.366 ns0.0667 ns0.0591 ns1.00
RandomSeedNextMinMaxJob-SVARSY\pr\corerun.exe10.686 ns0.0471 ns0.0441 ns0.80
RandomDerivedNextInt64Job-MYJVJF\main\corerun.exe78.238 ns0.2008 ns0.1677 ns1.00
RandomDerivedNextInt64Job-SVARSY\pr\corerun.exe43.399 ns0.6699 ns0.5939 ns0.55
RandomSeedNextInt64Job-MYJVJF\main\corerun.exe74.153 ns0.4255 ns0.3772 ns1.00
RandomSeedNextInt64Job-SVARSY\pr\corerun.exe23.521 ns0.0418 ns0.0327 ns0.32
RandomDerivedNextInt64MaxJob-MYJVJF\main\corerun.exe113.069 ns0.5636 ns0.5272 ns1.00
RandomDerivedNextInt64MaxJob-SVARSY\pr\corerun.exe68.993 ns0.8238 ns0.6431 ns0.61
RandomSeedNextInt64MaxJob-MYJVJF\main\corerun.exe117.979 ns0.6594 ns0.8098 ns1.00
RandomSeedNextInt64MaxJob-SVARSY\pr\corerun.exe40.567 ns0.1941 ns0.1816 ns0.34
RandomDerivedNextInt64MinMaxJob-MYJVJF\main\corerun.exe112.131 ns0.3612 ns0.3017 ns1.00
RandomDerivedNextInt64MinMaxJob-SVARSY\pr\corerun.exe67.423 ns0.1900 ns0.1684 ns0.60
RandomSeedNextInt64MinMaxJob-MYJVJF\main\corerun.exe116.348 ns0.4822 ns0.4275 ns1.00
RandomSeedNextInt64MinMaxJob-SVARSY\pr\corerun.exe41.832 ns0.2730 ns0.2554 ns0.36
RandomDerivedNextSingleJob-MYJVJF\main\corerun.exe11.778 ns0.0799 ns0.0709 ns1.00
RandomDerivedNextSingleJob-SVARSY\pr\corerun.exe11.889 ns0.0619 ns0.0517 ns1.01
RandomSeedNextSingleJob-MYJVJF\main\corerun.exe11.718 ns0.0680 ns0.0636 ns1.00
RandomSeedNextSingleJob-SVARSY\pr\corerun.exe9.043 ns0.0333 ns0.0295 ns0.77
RandomDerivedNextDoubleJob-MYJVJF\main\corerun.exe12.318 ns0.1869 ns0.1561 ns1.00
RandomDerivedNextDoubleJob-SVARSY\pr\corerun.exe11.460 ns0.0667 ns0.0557 ns0.93
RandomSeedNextDoubleJob-MYJVJF\main\corerun.exe11.387 ns0.0752 ns0.0704 ns1.00
RandomSeedNextDoubleJob-SVARSY\pr\corerun.exe8.947 ns0.1129 ns0.1056 ns0.79
RandomDerivedNextBytesArrayJob-MYJVJF\main\corerun.exe62,459.876 ns384.2017 ns340.5848 ns1.00
RandomDerivedNextBytesArrayJob-SVARSY\pr\corerun.exe68,100.319 ns321.5379 ns300.7667 ns1.09
RandomSeedNextBytesArrayJob-MYJVJF\main\corerun.exe62,466.595 ns260.9075 ns231.2877 ns1.00
RandomSeedNextBytesArrayJob-SVARSY\pr\corerun.exe68,294.184 ns755.4770 ns706.6736 ns1.09
RandomDerivedNextBytesSpanJob-MYJVJF\main\corerun.exe80,514.292 ns600.0956 ns561.3298 ns1.00
RandomDerivedNextBytesSpanJob-SVARSY\pr\corerun.exe81,179.767 ns634.0510 ns529.4613 ns1.01
RandomSeedNextBytesSpanJob-MYJVJF\main\corerun.exe85,292.812 ns648.1876 ns606.3151 ns1.00
RandomSeedNextBytesSpanJob-SVARSY\pr\corerun.exe64,892.721 ns594.0378 ns555.6634 ns0.76
publicclassRandomSeed:TestBase{publicoverrideRandomCreate()=>newRandom(42);}publicclassRandomDerived:TestBase{publicoverrideRandomCreate()=>newDerivedRandom();privatesealedclassDerivedRandom:Random{}}publicabstractclassTestBase{privatebyte[]_buffer=newbyte[10_000];privateRandom_random;publicabstractRandomCreate();[GlobalSetup]publicvoidSetup()=>_random=Create();[Benchmark]publicRandomCtor()=>Create();[Benchmark]publicintNext()=>_random.Next();[Benchmark]publicvoidNextMax()=>_random.Next(42);[Benchmark]publicvoidNextMinMax()=>_random.Next(0,42);[Benchmark]publicvoidNextInt64()=>_random.NextInt64();[Benchmark]publicvoidNextInt64Max()=>_random.NextInt64(42);[Benchmark]publicvoidNextInt64MinMax()=>_random.NextInt64(0,42);[Benchmark]publicvoidNextSingle()=>_random.NextSingle();[Benchmark]publicvoidNextDouble()=>_random.NextDouble();[Benchmark]publicvoidNextBytesArray()=>_random.NextBytes(_buffer);[Benchmark]publicvoidNextBytesSpan()=>_random.NextBytes((Span<byte>)_buffer);}

I'm not currently sure whyNextBytesArray gets a little slower... that'll need more investigation.

Author:stephentoub
Assignees:-
Labels:

area-System.Runtime

Milestone:6.0.0

Copy link
Member

@tannergoodingtannergooding left a comment

Choose a reason for hiding this comment

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

LGTM.

When we introduced the new Random algorithm, we did so by factoring the old algorithm out into an implementation strategy class that is instantiated for all use other than `new Random()`.  This ends up penalizing other uses (providing a seed and/or deriving from Random) by adding more virtual dispatch than is strictly necessary, in particular for `new Random(seed)`.  This PR negates most of that (expected) regression by splitting the compat implementation in two, one class for `new Random(seed)` and one for `new DerivedRandom()`/`new DerivedRandom(seed)`; the former no longer needs to make virtual calls back out to the parent type.  The former is also one that a consumer can't really do anything to improve, whereas in the derived case, the derivation may override to provide a more optimal implementation.
We haven't shipped this yet, so we can change its implementation to make 3 calls instead of 8 and to delegate to a different overload of Next.
@stephentoubstephentoub merged commitf7633f4 intodotnet:mainAug 17, 2021
@stephentoubstephentoub deleted the randomperf branchAugust 17, 2021 15:07
@ghostghost locked asresolvedand limited conversation to collaboratorsSep 16, 2021
Sign up for freeto subscribe to this conversation on GitHub. Already have an account?Sign in.

Reviewers

@GrabYourPitchforksGrabYourPitchforksGrabYourPitchforks approved these changes

@tannergoodingtannergoodingtannergooding approved these changes

Assignees

No one assigned

Projects

None yet

Milestone

6.0.0

Development

Successfully merging this pull request may close these issues.

[.NET 6] Improving the default Random.NextInt64 performance for derived classes

3 participants

@stephentoub@GrabYourPitchforks@tannergooding

[8]ページ先頭

©2009-2025 Movatter.jp