-
Notifications
You must be signed in to change notification settings - Fork 47
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
Improve efficiency of is_empty
#354
Comments
It's almost correct, except when API IMO you can fix this by changing |
It would fail for |
In that case, maybe we should check for capacity and length, instead of modifying |
Oh dear. Yes, I completely failed to consider that capacity and length are independent, so zero-length strings can be on the heap. In that case, I don't think there's anything to be done here. If static string didn't have it's own discriminant, then this would work: pub fn is_empty(&self) -> bool {
let heap_len = self.1;
let last_byte = self.last_byte();
// Empty inline string also contains 0 in the bytes holding heap length
heap_len == 0 && (last_byte == 192 || last_byte == 224)
} That produces branch-free code which might be a bit faster. But adding Unless anyone else sees any mileage in this, I'll close the issue. |
Yeah, I guess this particular method is as good as it gets for the current data representation. But nevertheless, thank you for looking into possible optimizations! It is very easy to convince yourself that whatever you implemented is the optimum, so it's always welcome to get a new perspective to challenge your view and maybe teach you something new! |
I think you code might still work, since static string is guaranteed to be non-empty now (inlined if empty) and its layout is stable:
|
Unfortunately I was out of date - the value of I think I'll give up at this point! But thanks both for entertaining my attempts. |
Oh wait! Maybe I've got something: const LENGTH_MASK: u8 = 192;
const HEAP_MASK: u8 = 216;
const HEAP_MASK_AFTER_SUB: usize = (HEAP_MASK as usize)
.wrapping_sub(LENGTH_MASK as usize);
pub fn is_empty_new(&self) -> bool {
let len_heap = ensure_read(self.1);
let last_byte = self.last_byte() as usize;
let mut len = last_byte.wrapping_sub(LENGTH_MASK as usize);
// NB `==` not `>=`
if len == HEAP_MASK_AFTER_SUB {
len = len_heap;
}
len == 0
} If I'm not mistaken (but very possible I am), this uses 1 less register, as https://godbolt.org/z/x3Whd115j This relies on static strings never being 0 length (if that is the case). I thought this might also have application for |
Or is this better? const LENGTH_MASK: u8 = 192;
const HEAP_MASK: u8 = 216;
const HEAP_MASK_AFTER_SUB: usize = (HEAP_MASK as usize)
.wrapping_sub(LENGTH_MASK as usize);
pub fn is_empty_new2(&self) -> bool {
let len_heap = self.1;
let last_byte = self.last_byte() as usize;
let len = last_byte.wrapping_sub(LENGTH_MASK as usize);
let is_empty_inline = len == 0;
// NB `==` not `>=`
let is_empty_heap = len == HEAP_MASK_AFTER_SUB && len_heap == 0;
is_empty_inline || is_empty_heap
} Same instruction count, but |
Without measuring it's hard to tell which version is the best. If I remember correctly, instructions like A call to |
is_empty
checks both inline length and heap length:compact_str/compact_str/src/repr/mod.rs
Lines 445 to 453 in 302c595
I believe an empty string is always stored inline. Therefore it could be reduced to:
If my assumption is correct, let me know and I'll make a PR.
The text was updated successfully, but these errors were encountered: