Which one has better Assembly output between Pattern Matching Enum and Fixed Size Array Enum Lookup?
⚓ Rust 📅 2026-01-18 👤 surdeus 👁️ 2I'm trying to understand this Assembly output between matching enum and fixed size array enum lookup
Code 1:
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum LogLevel {
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
}
impl LogLevel {
#[inline(always)]
const fn as_bytes(self) -> &'static [u8] {
match self {
LogLevel::Trace => b"TRACE",
LogLevel::Debug => b"\x1b[34mDEBUG\x1b[0m",
LogLevel::Info => b"\x1b[32mINFO\x1b[0m",
LogLevel::Warn => b"\x1b[33mWARN\x1b[0m",
LogLevel::Error => b"\x1b[31mERROR\x1b[0m",
}
}
}
#[unsafe(no_mangle)]
fn task(a: LogLevel) -> &'static [u8] {
a.as_bytes()
}
task:
lea rax, [rip + .Lswitch.table.task]
movzx ecx, dil
mov rdx, qword ptr [rax + 8*rcx]
lea rsi, [rip + .Lswitch.table.task.1.rel]
movsxd rax, dword ptr [rsi + 4*rcx]
add rax, rsi
ret
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.0:
.ascii "TRACE"
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.1:
.ascii "\033[34mDEBUG\033[0m"
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.2:
.ascii "\033[32mINFO\033[0m"
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.3:
.ascii "\033[33mWARN\033[0m"
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.4:
.ascii "\033[31mERROR\033[0m"
.Lswitch.table.task:
.quad 5
.quad 14
.quad 13
.quad 13
.quad 14
.Lswitch.table.task.1.rel:
.long .Lanon.478f3a4c51824ad23cb50c1c60670c0f.0-.Lswitch.table.task.1.rel
.long .Lanon.478f3a4c51824ad23cb50c1c60670c0f.1-.Lswitch.table.task.1.rel
.long .Lanon.478f3a4c51824ad23cb50c1c60670c0f.2-.Lswitch.table.task.1.rel
.long .Lanon.478f3a4c51824ad23cb50c1c60670c0f.3-.Lswitch.table.task.1.rel
.long .Lanon.478f3a4c51824ad23cb50c1c60670c0f.4-.Lswitch.table.task.1.rel
Code 2:
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum LogLevel {
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
}
impl LogLevel {
#[inline(always)]
const fn as_bytes(self) -> &'static [u8] {
const LOOKUP: &[&[u8]] = &[
b"TRACE",
b"\x1b[34mDEBUG\x1b[0m",
b"\x1b[32mINFO\x1b[0m",
b"\x1b[33mWARN\x1b[0m",
b"\x1b[31mERROR\x1b[0m",
];
LOOKUP[self as usize]
}
}
#[unsafe(no_mangle)]
fn task(a: LogLevel) -> &'static[u8] {
a.as_bytes()
}
task:
movzx ecx, dil
shl ecx, 4
lea rdx, [rip + .Lanon.478f3a4c51824ad23cb50c1c60670c0f.5]
mov rax, qword ptr [rcx + rdx]
mov rdx, qword ptr [rcx + rdx + 8]
ret
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.0:
.ascii "TRACE"
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.1:
.ascii "\033[34mDEBUG\033[0m"
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.2:
.ascii "\033[32mINFO\033[0m"
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.3:
.ascii "\033[33mWARN\033[0m"
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.4:
.ascii "\033[31mERROR\033[0m"
.Lanon.478f3a4c51824ad23cb50c1c60670c0f.5:
.quad .Lanon.478f3a4c51824ad23cb50c1c60670c0f.0
.asciz "\005\000\000\000\000\000\000"
.quad .Lanon.478f3a4c51824ad23cb50c1c60670c0f.1
.asciz "\016\000\000\000\000\000\000"
.quad .Lanon.478f3a4c51824ad23cb50c1c60670c0f.2
.asciz "\r\000\000\000\000\000\000"
.quad .Lanon.478f3a4c51824ad23cb50c1c60670c0f.3
.asciz "\r\000\000\000\000\000\000"
.quad .Lanon.478f3a4c51824ad23cb50c1c60670c0f.4
.asciz "\016\000\000\000\000\000\000"
If the LogLevel is known at compile time they produce the same Assembly. But if LogLevel is known at runtime they produce different Assembly as shown above. My interpretation is Code 2 is better because 1 linear table, fewer instructions, records are stored in contigously that move will read memory address that close to each other continously (cache localicy for better cache hit). Where Code 2 has 2 different switch tables that if the table is big will cause cache miss, and has more instructions
What is your interpretation version?
1 post - 1 participant
🏷️ Rust_feed