Skip to content

Commit 226449b

Browse files
authored
perf: Use BitmapBuilder in a lot more places (#20776)
1 parent 9501f57 commit 226449b

File tree

39 files changed

+270
-200
lines changed

39 files changed

+270
-200
lines changed

crates/polars-arrow/src/array/binary/mutable.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -299,12 +299,7 @@ impl<O: Offset> MutableBinaryArray<O> {
299299
let iterator = iterator.into_iter();
300300

301301
// soundness: assumed trusted len
302-
let (mut validity, offsets, values) = try_trusted_len_unzip(iterator)?;
303-
304-
if validity.as_mut().unwrap().unset_bits() == 0 {
305-
validity = None;
306-
}
307-
302+
let (validity, offsets, values) = try_trusted_len_unzip(iterator)?;
308303
Ok(Self::try_new(Self::default_dtype(), offsets, values, validity).unwrap())
309304
}
310305

crates/polars-arrow/src/array/physical_binary.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::bitmap::MutableBitmap;
1+
use crate::bitmap::{BitmapBuilder, MutableBitmap};
22
use crate::offset::{Offset, Offsets};
33

44
/// # Safety
@@ -16,7 +16,7 @@ where
1616
let (_, upper) = iterator.size_hint();
1717
let len = upper.expect("trusted_len_unzip requires an upper limit");
1818

19-
let mut null = MutableBitmap::with_capacity(len);
19+
let mut null = BitmapBuilder::with_capacity(len);
2020
let mut offsets = Vec::<O>::with_capacity(len + 1);
2121
let mut values = Vec::<u8>::new();
2222

@@ -44,7 +44,11 @@ where
4444
);
4545
offsets.set_len(len + 1);
4646

47-
Ok((null.into(), Offsets::new_unchecked(offsets), values))
47+
Ok((
48+
null.into_opt_mut_validity(),
49+
Offsets::new_unchecked(offsets),
50+
values,
51+
))
4852
}
4953

5054
/// Creates [`MutableBitmap`] and two [`Vec`]s from an iterator of `Option`.

crates/polars-arrow/src/bitmap/builder.rs

+122-36
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ use polars_utils::slice::load_padded_le_u64;
22

33
use crate::bitmap::{Bitmap, MutableBitmap};
44
use crate::storage::SharedStorage;
5+
use crate::trusted_len::TrustedLen;
56

67
/// Used to build bitmaps bool-by-bool in sequential order.
78
#[derive(Default, Clone)]
89
pub struct BitmapBuilder {
9-
buf: u64,
10-
len: usize, // Length in bits.
11-
cap: usize, // Capacity in bits.
12-
set_bits: usize,
10+
buf: u64, // A buffer containing the last self.bit_len % 64 bits.
11+
bit_len: usize, // Length in bits.
12+
bit_cap: usize, // Capacity in bits (always multiple of 64).
13+
set_bits_in_bytes: usize, // The number of bits set in self.bytes, not including self.buf.
1314
bytes: Vec<u8>,
1415
}
1516

@@ -18,40 +19,52 @@ impl BitmapBuilder {
1819
Self::default()
1920
}
2021

22+
#[inline(always)]
2123
pub fn len(&self) -> usize {
22-
self.len
24+
self.bit_len
2325
}
2426

27+
#[inline(always)]
2528
pub fn capacity(&self) -> usize {
26-
self.cap
29+
self.bit_cap
30+
}
31+
32+
#[inline(always)]
33+
pub fn set_bits(&self) -> usize {
34+
self.set_bits_in_bytes + self.buf.count_ones() as usize
35+
}
36+
37+
#[inline(always)]
38+
pub fn unset_bits(&self) -> usize {
39+
self.bit_len - self.set_bits()
2740
}
2841

2942
pub fn with_capacity(bits: usize) -> Self {
3043
let bytes = Vec::with_capacity(bits.div_ceil(64) * 8);
3144
let words_available = bytes.capacity() / 8;
3245
Self {
3346
buf: 0,
34-
len: 0,
35-
cap: words_available * 64,
36-
set_bits: 0,
47+
bit_len: 0,
48+
bit_cap: words_available * 64,
49+
set_bits_in_bytes: 0,
3750
bytes,
3851
}
3952
}
4053

4154
#[inline(always)]
4255
pub fn reserve(&mut self, additional: usize) {
43-
if self.len + additional > self.cap {
56+
if self.bit_len + additional > self.bit_cap {
4457
self.reserve_slow(additional)
4558
}
4659
}
4760

4861
#[cold]
4962
#[inline(never)]
5063
fn reserve_slow(&mut self, additional: usize) {
51-
let bytes_needed = (self.len + additional).div_ceil(64) * 8;
64+
let bytes_needed = (self.bit_len + additional).div_ceil(64) * 8;
5265
self.bytes.reserve(bytes_needed - self.bytes.len());
5366
let words_available = self.bytes.capacity() / 8;
54-
self.cap = words_available * 64;
67+
self.bit_cap = words_available * 64;
5568
}
5669

5770
#[inline(always)]
@@ -75,46 +88,52 @@ impl BitmapBuilder {
7588
/// self.len() < self.capacity() must hold.
7689
#[inline(always)]
7790
pub unsafe fn push_unchecked(&mut self, x: bool) {
78-
debug_assert!(self.len < self.cap);
79-
self.buf |= (x as u64) << (self.len % 64);
80-
self.len += 1;
81-
if self.len % 64 == 0 {
91+
debug_assert!(self.bit_len < self.bit_cap);
92+
self.buf |= (x as u64) << (self.bit_len % 64);
93+
self.bit_len += 1;
94+
if self.bit_len % 64 == 0 {
8295
self.flush_word_unchecked(self.buf);
83-
self.set_bits += self.buf.count_ones() as usize;
96+
self.set_bits_in_bytes += self.buf.count_ones() as usize;
8497
self.buf = 0;
8598
}
8699
}
87100

101+
#[inline(always)]
88102
pub fn extend_constant(&mut self, length: usize, value: bool) {
89103
// Fast path if the extension still fits in buf with room left to spare.
90-
let bits_in_buf = self.len % 64;
104+
let bits_in_buf = self.bit_len % 64;
91105
if bits_in_buf + length < 64 {
92106
let bit_block = ((value as u64) << length) - (value as u64);
93107
self.buf |= bit_block << bits_in_buf;
94-
self.len += length;
95-
return;
108+
self.bit_len += length;
109+
} else {
110+
self.extend_constant_slow(length, value);
96111
}
112+
}
97113

114+
#[cold]
115+
fn extend_constant_slow(&mut self, length: usize, value: bool) {
98116
unsafe {
99117
let value_spread = if value { u64::MAX } else { 0 }; // Branchless neg.
100118

101119
// Extend and flush current buf.
102120
self.reserve(length);
121+
let bits_in_buf = self.bit_len % 64;
103122
let ext_buf = self.buf | (value_spread << bits_in_buf);
104123
self.flush_word_unchecked(ext_buf);
105-
self.set_bits += ext_buf.count_ones() as usize;
124+
self.set_bits_in_bytes += ext_buf.count_ones() as usize;
106125

107126
// Write complete words.
108127
let remaining_bits = length - (64 - bits_in_buf);
109128
let remaining_words = remaining_bits / 64;
110129
for _ in 0..remaining_words {
111130
self.flush_word_unchecked(value_spread);
112131
}
113-
self.set_bits += (remaining_words * 64) & value_spread as usize;
132+
self.set_bits_in_bytes += (remaining_words * 64) & value_spread as usize;
114133

115134
// Put remainder in buf and update length.
116135
self.buf = ((value as u64) << (remaining_bits % 64)) - (value as u64);
117-
self.len += length;
136+
self.bit_len += length;
118137
}
119138
}
120139

@@ -123,21 +142,21 @@ impl BitmapBuilder {
123142
/// # Safety
124143
/// self.len + length <= self.cap and length <= 64 must hold.
125144
pub unsafe fn push_word_with_len_unchecked(&mut self, word: u64, length: usize) {
126-
debug_assert!(self.len + length <= self.cap);
145+
debug_assert!(self.bit_len + length <= self.bit_cap);
127146
debug_assert!(length <= 64);
128147
debug_assert!(length == 64 || (word >> length) == 0);
129-
let bits_in_buf = self.len % 64;
148+
let bits_in_buf = self.bit_len % 64;
130149
self.buf |= word << bits_in_buf;
131150
if bits_in_buf + length >= 64 {
132151
self.flush_word_unchecked(self.buf);
133-
self.set_bits += self.buf.count_ones() as usize;
152+
self.set_bits_in_bytes += self.buf.count_ones() as usize;
134153
self.buf = if bits_in_buf > 0 {
135154
word >> (64 - bits_in_buf)
136155
} else {
137156
0
138157
};
139158
}
140-
self.len += length;
159+
self.bit_len += length;
141160
}
142161

143162
/// # Safety
@@ -168,24 +187,24 @@ impl BitmapBuilder {
168187
slice = slice.get_unchecked(offset / 8..);
169188

170189
// Write word-by-word.
171-
let bits_in_buf = self.len % 64;
190+
let bits_in_buf = self.bit_len % 64;
172191
if bits_in_buf > 0 {
173192
while length >= 64 {
174193
let word = u64::from_le_bytes(slice.get_unchecked(0..8).try_into().unwrap());
175194
self.buf |= word << bits_in_buf;
176195
self.flush_word_unchecked(self.buf);
177-
self.set_bits += self.buf.count_ones() as usize;
196+
self.set_bits_in_bytes += self.buf.count_ones() as usize;
178197
self.buf = word >> (64 - bits_in_buf);
179-
self.len += 64;
198+
self.bit_len += 64;
180199
length -= 64;
181200
slice = slice.get_unchecked(8..);
182201
}
183202
} else {
184203
while length >= 64 {
185204
let word = u64::from_le_bytes(slice.get_unchecked(0..8).try_into().unwrap());
186205
self.flush_word_unchecked(word);
187-
self.set_bits += word.count_ones() as usize;
188-
self.len += 64;
206+
self.set_bits_in_bytes += word.count_ones() as usize;
207+
self.bit_len += 64;
189208
length -= 64;
190209
slice = slice.get_unchecked(8..);
191210
}
@@ -206,27 +225,94 @@ impl BitmapBuilder {
206225
}
207226
}
208227

228+
pub fn extend_from_bitmap(&mut self, bitmap: &Bitmap) {
229+
// TODO: we can perhaps use the bitmaps bitcount here instead of
230+
// recomputing it if it has a known bitcount.
231+
let (slice, offset, length) = bitmap.as_slice();
232+
self.extend_from_slice(slice, offset, length);
233+
}
234+
209235
/// # Safety
210236
/// May only be called once at the end.
211237
unsafe fn finish(&mut self) {
212-
if self.len % 64 != 0 {
238+
if self.bit_len % 64 != 0 {
213239
self.bytes.extend_from_slice(&self.buf.to_le_bytes());
214-
self.set_bits += self.buf.count_ones() as usize;
240+
self.set_bits_in_bytes += self.buf.count_ones() as usize;
241+
self.buf = 0;
215242
}
216243
}
217244

245+
/// Converts this BitmapBuilder into a mutable bitmap.
218246
pub fn into_mut(mut self) -> MutableBitmap {
219247
unsafe {
220248
self.finish();
221-
MutableBitmap::from_vec(self.bytes, self.len)
249+
MutableBitmap::from_vec(self.bytes, self.bit_len)
250+
}
251+
}
252+
253+
/// The same as into_mut, but returns None if the bitmap is all-ones.
254+
pub fn into_opt_mut_validity(mut self) -> Option<MutableBitmap> {
255+
unsafe {
256+
self.finish();
257+
if self.set_bits_in_bytes == self.bit_len {
258+
return None;
259+
}
260+
Some(MutableBitmap::from_vec(self.bytes, self.bit_len))
222261
}
223262
}
224263

264+
/// Freezes this BitmapBuilder into an immutable Bitmap.
225265
pub fn freeze(mut self) -> Bitmap {
226266
unsafe {
227267
self.finish();
228268
let storage = SharedStorage::from_vec(self.bytes);
229-
Bitmap::from_inner_unchecked(storage, 0, self.len, Some(self.len - self.set_bits))
269+
Bitmap::from_inner_unchecked(
270+
storage,
271+
0,
272+
self.bit_len,
273+
Some(self.bit_len - self.set_bits_in_bytes),
274+
)
275+
}
276+
}
277+
278+
/// The same as freeze, but returns None if the bitmap is all-ones.
279+
pub fn into_opt_validity(mut self) -> Option<Bitmap> {
280+
unsafe {
281+
self.finish();
282+
if self.set_bits_in_bytes == self.bit_len {
283+
return None;
284+
}
285+
let storage = SharedStorage::from_vec(self.bytes);
286+
let bitmap = Bitmap::from_inner_unchecked(
287+
storage,
288+
0,
289+
self.bit_len,
290+
Some(self.bit_len - self.set_bits_in_bytes),
291+
);
292+
Some(bitmap)
230293
}
231294
}
295+
296+
pub fn extend_trusted_len_iter<I>(&mut self, iterator: I)
297+
where
298+
I: Iterator<Item = bool> + TrustedLen,
299+
{
300+
self.reserve(iterator.size_hint().1.unwrap());
301+
for b in iterator {
302+
// SAFETY: we reserved and the iterator's length is trusted.
303+
unsafe {
304+
self.push_unchecked(b);
305+
}
306+
}
307+
}
308+
309+
#[inline]
310+
pub fn from_trusted_len_iter<I>(iterator: I) -> Self
311+
where
312+
I: Iterator<Item = bool> + TrustedLen,
313+
{
314+
let mut builder = Self::new();
315+
builder.extend_trusted_len_iter(iterator);
316+
builder
317+
}
232318
}

crates/polars-arrow/src/bitmap/immutable.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -422,19 +422,16 @@ impl Bitmap {
422422
/// Initializes an new [`Bitmap`] filled with the given value.
423423
#[inline]
424424
pub fn new_with_value(value: bool, length: usize) -> Self {
425-
// Don't use `MutableBitmap::from_len_zeroed().into()`, it triggers a bitcount.
426-
let bytes = if value {
427-
vec![u8::MAX; length.saturating_add(7) / 8]
428-
} else {
429-
vec![0; length.saturating_add(7) / 8]
430-
};
431-
let unset_bits = if value { 0 } else { length };
425+
if !value {
426+
return Self::new_zeroed(length);
427+
}
428+
432429
unsafe {
433430
Bitmap::from_inner_unchecked(
434-
SharedStorage::from_vec(bytes),
431+
SharedStorage::from_vec(vec![u8::MAX; length.saturating_add(7) / 8]),
435432
0,
436433
length,
437-
Some(unset_bits),
434+
Some(0),
438435
)
439436
}
440437
}

0 commit comments

Comments
 (0)