rope: Micro optimize the creation of masks (#41132)

Using compiler explorer I saw that the compiler wasn't clever enough to
optimise away the branches in the masking code. I thought the compiler
would have a better chance if we always branched, which [turned out to
be the case](https://godbolt.org/z/PM594Pz18).

Running the benchmarks the biggest benefit I saw was:
```
push/65536              time:   [2.9067 ms 2.9243 ms 2.9417 ms]
                        thrpt:  [21.246 MiB/s 21.373 MiB/s 21.502 MiB/s]
                 change:
                        time:   [-8.3452% -7.2617% -6.2009%] (p = 0.00 < 0.05)
                        thrpt:  [+6.6108% +7.8303% +9.1050%]
                        Performance has improved.
```
But I did also see some regressions:
```
slice/4096              time:   [66.195 µs 66.815 µs 67.448 µs]
                        thrpt:  [57.915 MiB/s 58.464 MiB/s 59.012 MiB/s]
                 change:
                        time:   [+3.7131% +5.1698% +6.6971%] (p = 0.00 < 0.05)
                        thrpt:  [-6.2768% -4.9157% -3.5802%]
                        Performance has regressed.
```

Release Notes:

- N/A
This commit is contained in:
Adam Richardson 2025-10-27 15:16:16 +00:00 committed by GitHub
parent 2284131bfc
commit 370d4ce200
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -32,6 +32,16 @@ pub struct Chunk {
pub text: ArrayString<MAX_BASE>,
}
#[inline(always)]
const fn saturating_shl_mask(offset: u32) -> Bitmap {
(1 as Bitmap).unbounded_shl(offset).wrapping_sub(1)
}
#[inline(always)]
const fn saturating_shr_mask(offset: u32) -> Bitmap {
!Bitmap::MAX.unbounded_shr(offset)
}
impl Chunk {
pub const MASK_BITS: usize = Bitmap::BITS as usize;
@ -291,34 +301,19 @@ impl<'a> ChunkSlice<'a> {
/// Get number of chars in first line
#[inline(always)]
pub fn first_line_chars(&self) -> u32 {
if self.newlines == 0 {
self.chars.count_ones()
} else {
let mask = ((1 as Bitmap) << self.newlines.trailing_zeros()) - 1;
(self.chars & mask).count_ones()
}
(self.chars & saturating_shl_mask(self.newlines.trailing_zeros())).count_ones()
}
/// Get number of chars in last line
#[inline(always)]
pub fn last_line_chars(&self) -> u32 {
if self.newlines == 0 {
self.chars.count_ones()
} else {
let mask = !(Bitmap::MAX >> self.newlines.leading_zeros());
(self.chars & mask).count_ones()
}
(self.chars & saturating_shr_mask(self.newlines.leading_zeros())).count_ones()
}
/// Get number of UTF-16 code units in last line
#[inline(always)]
pub fn last_line_len_utf16(&self) -> u32 {
if self.newlines == 0 {
self.chars_utf16.count_ones()
} else {
let mask = !(Bitmap::MAX >> self.newlines.leading_zeros());
(self.chars_utf16 & mask).count_ones()
}
(self.chars_utf16 & saturating_shr_mask(self.newlines.leading_zeros())).count_ones()
}
/// Get the longest row in the chunk and its length in characters.
@ -492,8 +487,8 @@ impl<'a> ChunkSlice<'a> {
#[inline(always)]
pub fn offset_to_point_utf16(&self, offset: usize) -> PointUtf16 {
let mask = (1 as Bitmap).unbounded_shl(offset as u32).wrapping_sub(1);
let row = (self.newlines & mask).count_ones();
let mask = saturating_shl_mask(offset as u32);
let row = (self.newlines & saturating_shl_mask(offset as u32)).count_ones();
let newline_ix = Bitmap::BITS - (self.newlines & mask).leading_zeros();
let column = if newline_ix as usize == MAX_BASE {
0