Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Mir-Opt for copying enums with large discrepancies #85158

Merged
merged 9 commits into from
Feb 11, 2023

Conversation

JulianKnodt
Copy link
Contributor

I have been meaning to make this for quite a while, based off of this hackmd.

I'm not sure where to put this opt now that I've made it, so I'd appreciate suggestions on that!
It's also one long chain of statements, not sure if there's a more friendly format to make it.

r? @tmiasko
I would r oli but he's on leave so he suggested I r tmiasko or wesleywiser.

@rust-log-analyzer

This comment has been minimized.

Copy link
Contributor

@tmiasko tmiasko left a comment

Choose a reason for hiding this comment

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

Hmm, so the idea is to transform: _a = _b; into:

_index = discriminant(_b);
_bytes  = [size of each variant in bytes][_index]
CopyNonOverlapping(src: &_b as *const u8, dst: &_a as *mut u8, count: _bytes);

The transformation is localized to a single assignment, requires layout information, some aspects of it feel non-trivial to express directly in MIR (e.g., preserving the alignment). Maybe codegen would be a better fit?

compiler/rustc_mir/src/transform/large_enums.rs Outdated Show resolved Hide resolved
compiler/rustc_mir/src/transform/large_enums.rs Outdated Show resolved Hide resolved
compiler/rustc_mir/src/transform/large_enums.rs Outdated Show resolved Hide resolved
compiler/rustc_mir/src/transform/large_enums.rs Outdated Show resolved Hide resolved
@tmiasko
Copy link
Contributor

tmiasko commented May 12, 2021

This general pattern for copying the data appears to be opaque to memcpy optimizations, i.e., every single transformed copy remains. Any ideas how this could be improved?

@scottmcm
Copy link
Member

Maybe codegen would be a better fit?

That would mean it would have the monomorphized versions of the enum too, right? Could be handy for anything using a generic Result and such...

@JulianKnodt
Copy link
Contributor Author

Mmmm I've not written anything for codegen at all, but if that makes more sense I can close this and move it to that, altho seeing a guide before it would be helpful.

@tmiasko
Copy link
Contributor

tmiasko commented May 12, 2021

As you prefer. The current implementation is not that far from the point where we could run some tests on it. The one missing component is a mapping from a discriminant to a variant index (or limiting the transformation to the cases where there is direct correspondence between the two).

@JulianKnodt
Copy link
Contributor Author

ah I thought reading from the array of sizes was handled around line 132, but I guess mapping discriminants to variant idxes is not guaranteed to be 1-1, is there anyway to check that?

@bjorn3
Copy link
Member

bjorn3 commented May 13, 2021

but I guess mapping discriminants to variant idxes is not guaranteed to be 1-1, is there anyway to check that?

I believe

enum Foo {
    Bar = 2,
    Baz = 0,
}

has variant index 0 for Bar, but discriminant 2 and variant index 1 for Baz, but discriminant 0.

@tmiasko
Copy link
Contributor

tmiasko commented May 13, 2021

mapping discriminants to variant idxes is not guaranteed to be 1-1, is there anyway to check that?

The InterpCx::read_discriminant exercises related API and contains explanatory comments, might be good starting point.

@camelid camelid added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label May 28, 2021
@camelid camelid added the A-mir-opt Area: MIR optimizations label May 28, 2021
@camelid
Copy link
Member

camelid commented May 28, 2021

Does this fix #54360?

@JulianKnodt
Copy link
Contributor Author

I believe so? I don't remember the original issue.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

Variants::Multiple { variants, .. } => {
let min = variants.iter().map(|v| v.size).min().unwrap();
let max = variants.iter().map(|v| v.size).max().unwrap();
if max.bytes() - min.bytes() < D {
Copy link
Contributor

Choose a reason for hiding this comment

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

The max - min criterion is based on the most optimistic scenario.

Consider two enums with the same maximum absolute deviation in variant sizes. The first enum has one large outlier and remaining variants are small, the second enum conversely has one small outlier and remaining variants are large. Both enums would be be equally good candidates under current criterion, but the first one would seem like a much better candidate.

What about assuming that variants are uniformly distributed and calculating expected reduction in the number of bytes copied?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops I never directly addressed this, I'm not sure if uniform distribution makes a ton of sense, as I'd expect if this is hit that one variant is probably significantly larger than the others. Probably something to experiment with.

@tmiasko
Copy link
Contributor

tmiasko commented May 28, 2021

In terms of placement in MIR pipeline, I would put it towards the end, somewhere after SimplifyLocals, maybe just last? I wouldn't expect it to create any new optimization opportunities.

Changing a bunch of struct constructors to `from`, no extra destructuring,
getting the type of the discriminant.
Since we're changing a bunch of stuff, necessary to remove some codegen tests
which look for specific things. Also attempting to restart a test which timed out, maybe due to
fastly failing?
Instead of storing an extra array for discriminant values, create an allocation there and store
those in an allocation immediately.
There is a distinction between running this on wasm and i686, even though they should be
identical. This technically is not _incorrect_, it's just an unexpected difference, which is
worth investigating, but not for correctness.
@JulianKnodt JulianKnodt force-pushed the array_const_val branch 5 times, most recently from c67e312 to e5352d9 Compare February 7, 2023 10:43
@JulianKnodt
Copy link
Contributor Author

Should be ok to retry submitting again at this point

@cjgillot
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Feb 10, 2023

📌 Commit 15d4728 has been approved by cjgillot

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 10, 2023
@bors
Copy link
Contributor

bors commented Feb 10, 2023

⌛ Testing commit 15d4728 with merge 5a8dfd9...

@bors
Copy link
Contributor

bors commented Feb 11, 2023

☀️ Test successful - checks-actions
Approved by: cjgillot
Pushing 5a8dfd9 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Feb 11, 2023
@bors bors merged commit 5a8dfd9 into rust-lang:master Feb 11, 2023
@rustbot rustbot added this to the 1.69.0 milestone Feb 11, 2023
@JulianKnodt JulianKnodt deleted the array_const_val branch February 11, 2023 01:26
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (5a8dfd9): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-2.2% [-2.2%, -2.2%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -2.2% [-2.2%, -2.2%] 1

Cycles

This benchmark run did not return any relevant results for this metric.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-mir-opt Area: MIR optimizations merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.