-
Notifications
You must be signed in to change notification settings - Fork 381
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
enable optimizer in tests and procmacros #5155
base: master
Are you sure you want to change the base?
Changes from 5 commits
112b8f3
9531071
3a892cc
40a2be1
e0ba967
3627c25
c9dd72f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1277,13 +1277,6 @@ pub mod tests { | |
} | ||
} | ||
|
||
fn get_executable_byte(&self) -> u8 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this function was only used in a test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change seems unrelated to the unit tests speed up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only reason this change is happening is to make optimizations possible for unittests. For more substantial changes separate PRs have been made by teams handling respective pieces of the code. I'll be making some as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this check is not really useful (as is claimed in the discussion) it can be done in a separate PR, as it has value by itself. For example, should you end up introducing a separate compilation profile for the CI, this PR will increase in size and will take some time to land. But this particular change might go in pretty quickly on its own. It is up to you, of course. I was just speaking from personal experience :) |
||
let executable_bool: bool = self.executable(); | ||
// UNSAFE: Force to interpret mmap-backed bool as u8 to really read the actual memory content | ||
let executable_byte: u8 = unsafe { std::mem::transmute::<bool, u8>(executable_bool) }; | ||
executable_byte | ||
} | ||
|
||
fn set_executable_as_byte(&self, new_executable_byte: u8) { | ||
// UNSAFE: Force to interpret mmap-backed &bool as &u8 to write some crafted value; | ||
unsafe { | ||
|
@@ -1830,7 +1823,6 @@ pub mod tests { | |
{ | ||
let executable_bool: bool = account.executable(); | ||
assert!(!executable_bool); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed this assert as per advice from @roryharr There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More Context: This assert was added a long time ago by ryoqun just to monitor some undefined behaviour. The executable bit is a boolean which uses a byte of storage. The only valid values for the byte are zero and one. This is verified during account load by the function sanitize_executable. This test corrupts the byte, and verifies that sanitize executable fails, which it does. Afterwards it has various asserts to see how it behaves in the event the sanitize check was not present. As per ryoquns comments these are purely for his interest. When compiling with optimizations on, the compiler behaviour changes when accessing the byte by value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ryoqun In case you are still interested |
||
assert_eq!(account.get_executable_byte(), 0); // Wow, not crafted_executable! | ||
} | ||
}) | ||
.unwrap(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the reasons unit tests do not have optimization enabled, is to speed up the compilation for interactive development.
When working on a single unit test, you want the fastest edit/compile/run cycle.
Here is an example of a command I would run when I'm working on a specific unit test:
cargo test \ --package solana-core \ --test epoch_accounts_hash \ --all-features -- \ --exact --nocapture test_snapshots_have_expected_epoch_accounts_hash
Have you considered the increase in the compilation time when switching from
opt-level
0
to1
for these use cases?I think, a common approach for speed improvements of this kind for CI, is to build unit tests with optimizations in CI only.
cargo
supports custom profiles, so, I think, it might be possible to create a custom profile that CI will use, that would enable higher optimizations levels.More details on the custom profiles can be found here:
https://doc.rust-lang.org/cargo/reference/profiles.html#custom-profiles
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
o1 is in many cases practically free in terms of compile times. For example:
running
time cargo test --package solana-core --test epoch_accounts_hash
clean-build with optimizations (o1):
clean-build with no optimizations (default settings):
So, we would save 15 seconds of real build time (on full rebuild), while spending far longer waiting for the actual tests to run (assuming you are running more than one at a time). Some of our tests already take minutes to complete, especially the ones in local-cluster, and we only need to build stuff once to run all the tests. Full CI runs are almost always > 1 hour. o1 gives about 10x boost in speed compared to o0.
Another aspect here is that with optimizer disabled certain kinds of bugs such as race conditions may just never show up. Essentially we end up testing something other than what we run in prod in terms of actual generated assembly, and the differences can be quite dramatic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When working on a single test, I would be running just this one test.
And if the compilation is 15 seconds slower, most of these 15 seconds will be added to my edit/compile/run cycle. The speed-up in a single test execution is normally unnoticeable.
Are you using a multithreaded linker, such as
mold
?local-cluster
is the most extreme example.And it is not a unit test, but an integration test.
This is why it is a separate crate that can have optimization configured for it separately.
This is the CI use case.
And I think speeding up our CI is a great cause.
But for the developer use case, you normally compile one crate and run one test.
And if unit tests are small and fast, you do not benefit from the speed-up.
But suffer from the compilation time increase.
While it is great to be able to find UB issues, I am not sure if this is a good argument here.
I do not have a comprehensive statistics, but my guess is that depending on the UB you may or may not observe it at different optimization levels.
So, it could be the switch between them, that is fruitful.
Or, you could miss it in any case.
You can not really rely on the unit tests to catch the undefined behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While a 15-second increase in the compilation time, I believe, is a rather objective thing to consider, this reminded me of this XKCD:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
15 sec is for a full rebuild after cargo clean. while incrementally building one test the difference is not really measurable.