This is entirely out of curiosity, but is there a reason why the in the following code using_enum
produces more instructions than using_u8
? I would have thought that the output would be identical.
Godbolt link
pub fn using_u8(n: u8) -> u8 {
let row_type = n % 3;
match row_type {
0 => a(n),
1 => b(n),
_ => c(n),
}
}
enum RowType {
Left, Middle, Right
}
fn get_row_type(n: u8) -> RowType {
let row_type = n % 3;
match row_type {
0 => RowType::Left,
1 => RowType::Middle,
_ => RowType::Right,
}
}
pub fn using_enum(n: u8) -> u8 {
match get_row_type(n) {
RowType::Left => a(n),
RowType::Middle => b(n),
RowType::Right => c(n),
}
}
#[inline(never)]
fn a(n: u8) -> u8 { n ^ 0b1010101 }
#[inline(never)]
fn b(n: u8) -> u8 { n ^ 0b1101 }
#[inline(never)]
fn c(n: u8) -> u8 { n ^ 0b10101 }
dhm
February 21, 2019, 1:47pm
2
Both things aren't really comparable: the enum example performs 2 matches, whereas your u8 example does not.
I've made the following example, which is better reflects what the enum does (I've chosen to use #[repr(u8)]
since I am using u8
constants):
const LEFT : u8 = 0;
const MIDDLE : u8 = 1;
const RIGHT : u8 = 2;
pub fn get_row_type_u8(n: u8) -> u8 {
let row_type = n % 3;
match row_type {
0 => LEFT,
1 => MIDDLE,
_ => RIGHT,
}
}
#[repr(u8)]
enum RowType {
Left, Middle, Right
}
fn get_row_type_enum(n: u8) -> RowType {
let row_type = n % 3;
match row_type {
0 => RowType::Left,
1 => RowType::Middle,
_ => RowType::Right,
}
}
#[no_mangle]
pub
fn using_enum(n: u8) -> u8 {
match get_row_type_enum(n) {
RowType::Left => a(n),
RowType::Middle => b(n),
RowType::Right => c(n),
}
}
#[no_mangle]
pub
fn using_u8(n: u8) -> u8 {
match get_row_type_u8(n) {
LEFT => a(n),
MIDDLE => b(n),
RIGHT => c(n),
_ => unsafe { std::hint::unreachable_unchecked() },
}
}
#[inline(never)]
fn a(n: u8) -> u8 { n ^ 0b1010101 }
#[inline(never)]
fn b(n: u8) -> u8 { n ^ 0b1101 }
#[inline(never)]
fn c(n: u8) -> u8 { n ^ 0b10101 }
/// An #[inline(always)] std::hint::unreachable_unchecked()
mod std {
pub mod hint {
#[inline(always)]
/// UB UB UB
pub unsafe fn unreachable_unchecked () -> !
{
struct VoidStruct; enum VoidType {}
match ::std::mem::transmute::<_, VoidType>(VoidStruct) {}
}
}
}
This then gives (Godbolt can't handle explicit unreachable hints)
They are outstandingly similar.
Which is confirmed when building in --release
mode:
$ nm ./target/release/libtemp.so | grep using
0000000000000960 T using_enum
0000000000000960 T using_u8
Thus, the problem does not seem to come from using enums but from the 2 consecutive matches.
2 Likes
It looks like a missed optimization somewhere. When I change the match statement in get_row_type
to the below, using_u8
and using_enum
produce the exact same assembly such that it is unified into one function.
match row_type {
0 => RowType::Left,
1 => RowType::Middle,
2 => RowType::Right,
_ => RowType::Right,
}
4 Likes
system
Closed
May 22, 2019, 3:20pm
4
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.