Hi,
I have some expensive functions that only need to work with a small range of inputs. I was hoping I could tell the compiler to expect a small set of inputs, via an assertion or enum, and that it would pre-compute a lookup table for the result, rather than including a lot of slow code. Is there a way to hint this to the compiler? Is this an optimisation that you've considered?
Here's a contrived example of what I mean:
pub fn fibonacci(n: u32) -> u32 {
assert!(n < 7);
#[inline]
const fn inner(n: u32) -> u32 {
match n {
0 => 1,
1 => 1,
n => inner(n-1) + inner(n-2),
}
}
inner(n)
}
Note that I'm hoping it would replace the inner
function call with a lookup table, it doesn't.
Even being a lot more explicit doesn't work, I'd expect this to at least to compile-time evaluate the const
function calls (it doesn't):
#[repr(u32)]
enum Input {
N0 = 0,
N1 = 1,
N2 = 2,
N3 = 3,
N4 = 4,
N5 = 5,
N6 = 6,
}
pub fn fibonacci(input: Input) -> u32 {
const fn inner(n: u32) -> u32 {
match n {
0 => 1,
1 => 1,
n => inner(n-1) + inner(n-2),
}
}
match input {
Input::N0 => inner(0),
Input::N1 => inner(1),
Input::N2 => inner(2),
Input::N3 => inner(3),
Input::N4 => inner(4),
Input::N5 => inner(5),
Input::N6 => inner(6),
}
}
Emits:
example::fibonacci:
cmp edi, 2
jae example::fibonacci::inner
mov eax, 1
ret
I would expect the compiler to have similar output as something like this, for such a small input range:
pub fn fibonacci(input: u32) -> u32 {
assert!(input <= 6);
const fn inner(n: u32) -> u32 {
match n {
0 => 1,
1 => 1,
n => inner(n-1) + inner(n-2),
}
}
const LUT: [u32; 7] = [
inner(0),
inner(1),
inner(2),
inner(3),
inner(4),
inner(5),
inner(6),
];
LUT[input as usize]
}
In fact, that does actually evaluate the const functions, so perhaps there's just some optimisation path that's being cut off? Or being run in an order that loses the range information?
So I guess there's a few things missing here:
- Evaluate const functions when the parameters are known, and the output size doesn't make it a worse-tradeoff
- Recognise that a known small range of inputs (via enum or assert) may be "unrolled" into a
match
statement for potential optimisations - Replace a const match statement with a lookup table
Thanks!