perf(transformer/styled_components): replace hashmap with array#12170
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
CodSpeed Instrumentation Performance ReportMerging #12170 will not alter performanceComparing Summary
|
|
I also came up with this alternative, which I imagine is more performant. It uses the fact that there are certain bit patterns in the first 3 letters of these strings which are unique (I used a little script to find them). https://godbolt.org/z/4KhzKTTaz But I don't think it's worth the complication. We could at some point try to codegen code like this as a lightweight replacement for hashmaps and #[derive(Copy, Clone)]
#[repr(u8)]
enum StyledComponentsHelper {
CreateGlobalStyle = 0,
Css = 1,
UseTheme = 2,
WithTheme = 3,
InjectGlobal = 4,
Keyframes = 5,
}
const STRS: &[&str] = &[
"createGlobalStyle", "css", "useTheme", "withTheme", "injectGlobal", "keyframes"
];
impl StyledComponentsHelper {
fn from_str(name: &str) -> Option<Self> {
if name.len() < 3 {
return None;
}
// 3rd and 4th bits of 1st byte and 5th bit of 3rd byte make a number
// between 0..=5 which is different for each string
let bytes = name.as_bytes();
let index = ((bytes[0] >> 1) & 6) | ((bytes[2] >> 4) & 1);
if index >= 6 {
return None;
}
if name != STRS[index as usize] {
return None;
}
// This compiles down to a no-op
Some(match index {
0 => StyledComponentsHelper::CreateGlobalStyle,
1 => StyledComponentsHelper::Css,
2 => StyledComponentsHelper::UseTheme,
3 => StyledComponentsHelper::WithTheme,
4 => StyledComponentsHelper::InjectGlobal,
5 => StyledComponentsHelper::Keyframes,
_ => unreachable!(),
})
}
} |
Dunqing
left a comment
There was a problem hiding this comment.
The current approach looks better, and the readability is good as well!
Merge activity
|
Follow-on after #12066. `helpers` was an `FxHashMap<String, SymbolId>`, but we know all the possible keys, and there's only 6 of them. So use a small array `[Option<SymbolId>; 6]` instead, and convert from strings to indexes into that array via a `StyledComponentsHelper` enum. Array (24 bytes) is smaller than an `FxHashMap`, and involves no allocations, as well as avoiding hashing strings. Note: Using an enum rather than an untyped `usize` index, as with an enum, compiler can statically prove indexing into the array can never be out of bounds, so it skips bounds checks.
2d8af41 to
e222e26
Compare
a5ab429 to
bcd5094
Compare

Follow-on after #12066.
helperswas anFxHashMap<String, SymbolId>, but we know all the possible keys, and there's only 6 of them. So use a small array[Option<SymbolId>; 6]instead, and convert from strings to indexes into that array via aStyledComponentsHelperenum.Array (24 bytes) is smaller than an
FxHashMap, and involves no allocations, as well as avoiding hashing strings.Note: Using an enum rather than an untyped
usizeindex, as with an enum, compiler can statically prove indexing into the array can never be out of bounds, so it skips bounds checks.