-
Notifications
You must be signed in to change notification settings - Fork 20
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
ACP: add bool::select #468
Comments
This shouldn't go on the bool type itself. It's more something fit for the |
@the8472 It's not a hint, it's an actual computation - selecting between two values. In fact if you read my ACP a bit more carefully you'll notice I'm trying to move away from the concept of 'unpredictability' in the API completely, which is the only thing that could be hinted about in the first place. |
|
I'd assume this would be allowed to generate a branch if no other codegen option exists. |
It does seem a bit weird to have this as a method on bool. Maybe some code examples would change my mind but it feels too niche to not be a separate function or a wrapper type. |
Yes, the description should probably be expanded to explicitly mention this isn't a hard guarantee (even if compiling on a platform that has, e.g. |
Which then sounds like merely a hint to codegen... |
in the same sense that |
You have a point. But I'm not convinced yet. Unlike in SIMD llvm does have optimizations that turns cmovs into branches. So there's a difference between computing two values, picking one and then maybe the compiler transforms it depending on what it thinks is profitable and explicitly telling the compiler that you know better. |
None of the functions in |
The proposed API does not fit into if std::hint::unpredictable(boolean) {
true_value
} else {
false_value
} This should be "optimized" to match std::hint::unpredictable(integer) {
0 => a,
1 => b,
3 => c,
9 => d,
27 => e,
_ => etc,
} OTOH in rust-lang/rust#120370 According to the LLVM docs the #[unpredictable]
if boolean { true_value } else { false_value }
#[unpredictable]
match integer { _ => etc } Footnotes
|
@kennytm If let true_value = true_expr;
let false_value = false_expr;
#[unpredictable]
if boolean { true_value } else { false_value } otherwise the Rust compiler is not allowed to write it using eager evaluation + a I'd really much rather just write boolean.select(true_expr, false_expr) which much closer matches my intent. The whole point of The fact that the API |
That documentation is incorrect and outdated. It does in fact work on
|
@workingjubilee I didn't mean The bug (?) I mentioned in the footnote is that #![feature(core_intrinsics)]
pub fn select_unpredictable(b: bool, t: u32, f: u32) -> u32 {
if std::intrinsics::select_unpredictable(b, true, false) {
t
} else {
f
}
} produces %t.f = select i1 %b, i32 %t, i32 %f
ret i32 %t.f instead of this when you use the intrinsic directly %0 = select i1 %b, i32 %t, i32 %f, !unpredictable !3
ret i32 %0 |
I meant the Did you verify I have found evidence to suggest it is simply incorrect. |
Oh. If you're just introducing the knowledge that trying to reiterate clang's implementation of |
@workingjubilee if https://llvm.org/docs/LangRef.html is not correct then why it is not fixed 😅, the title literally said "LLVM 20.0.0git documentation" and reachable from https://llvm.org/docs/Reference.html#llvm-ir ← https://llvm.org/docs/#documentation ← https://llvm.org/. Also I don't see how that phrase I've quoted implies attaching godbolt for reference. |
For what it's worth, this API gets a +1 from me. Efficient programs need ways to avoid expensive branch mispredicts. Generally branchless - or less confusingly named jumpless - programming is achieved via a small toolkit of building blocks:
Out of this non-exhaustive list of options one relies particularly heavy on compiler co-operation, branchless value selection. |
I wonder if it'd be worth adding both I wouldn't mind using And the version with the most constraints on codegen having a longer name is probably good. (And I'm also tempted to add it to MIR, because needing 4 basic blocks for every |
@scottmcm I had a similar thought as well. There are different scenarios and and either When a programmer writes |
We discussed this in today's @rust-lang/libs-api meeting. We'd like to see this called Otherwise: 👍 🚢 |
Right, you don't want to be using this unless you specifically want to tell the compiler that the condition is unpredictable by a branch predictor. Otherwise it is better to let LLVM's heuristics decide whether to use a branch or conditional move. We also rejected other names such as |
I've implemented this in rust-lang/rust#133964. |
…oss35 core: implement `bool::select_unpredictable` Tracking issue: rust-lang#133962 ACP: rust-lang/libs-team#468
Rollup merge of rust-lang#133964 - joboet:select_unpredictable, r=tgross35 core: implement `bool::select_unpredictable` Tracking issue: rust-lang#133962 ACP: rust-lang/libs-team#468
Proposal
Problem statement
It is a common theme in performance-oriented programming to want to select between two outcomes, but without changing which code runs as this invokes branch prediction. Branch prediction is great when it works but has two downsides:
Currently in Rust if you want to have a good chance of something being branchless you have to write your code in a particular way, and even then it regularly fails to compile to branchless instructions, depending on the mood of the compiler.
Motivating examples or use cases
There are tons of examples, I don't think I have to convince anyone familiar with performance-sensitive code of its use. Binary searches, sorting, filtering of data based on a condition, the applications are nearly endless. In performance-sensitive code, if you write an
if
where both sides of theif
are cheap to evaluate and the condition is not 99%+ consistent there's a good chance a branchlessselect
statement would be faster.And of course there is the consistency argument even if the branch is 99%+ consistent in most cases, for example audio DSP code might want to ensure a kernel is always consistent in its speed to prevent audio crackling when a rare sequence of unpredictable samples come along, or high-frequency trading code may want to ensure it always has a response in a certain amount of nanoseconds.
Solution sketch
I propose we add the following method to the
bool
primitive type:This interface is similar to the
std::simd::Mask::select
API, just for one value.Alternatives
There is the
select_unpredictable
intrinsic. However, it is an intrinsic so it should not really be used. There is no viable alternative, even writing things such as[false_val, true_val][cond as usize]
can and do get rewritten by the compiler to branches, or just generate plain terrible code. One can reach for inline assembly but this pessimizes surrounding code and is not portable.As for bikeshedding about the name, I considered the following alternatives, in order of preference:
select_predicated
(see predication)select_eager
select_branchless
select_unpredictable
(as the intrinsic was named)However, I prefer just the simple
select
. I think the fact it always eagerly evaluates both arguments (as functions do) is already self-documenting enough to readers to show it is not a control flow structure, but rather a selection operator between two pre-computed values. I think it is fairly natural that this then in turn compiles to a branchless select, even when simply readingcond.select(a, b)
.My reason for not favoring the current name of the intrinsic
select_unpredictable
is that (in my opinion) it confuses a problem with the solution. This can lead to confusing scenarios where the solution is applicable but the problem is not, e.g. someone could make an informed choice to useselect_unpredictable
while the boolean condition is in fact predictable - they just wanted consistency or avoid conditional jumps for other reasons.Links and related work
select_unpredictable
The text was updated successfully, but these errors were encountered: