Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ All significant changes to this project will be documented in this file.

* Improve truncated-input diagnostics across sketch deserializers.
* Improve hash-backed sketch update performance for integer and raw-byte inputs.
* Improve Bloom filter membership-and-insert performance and simplify Theta-family hash table thresholds.

### Bug fixes

Expand Down
20 changes: 20 additions & 0 deletions benchmarks/bloom/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,23 @@ fn bytes_32(bencher: Bencher) {
black_box(filter)
});
}

#[divan::bench]
fn contains_and_insert_present_u64(bencher: Bencher) {
let mut populated = BloomFilterBuilder::with_accuracy(ITEMS as u64, 0.01)
.build()
.unwrap();
for value in 0..ITEMS as u64 {
populated.insert(value);
}

bencher
.counter(ItemsCount::new(ITEMS))
.with_inputs(|| populated.clone())
.bench_local_values(|mut filter| {
for value in 0..ITEMS as u64 {
black_box(filter.contains_and_insert(black_box(&value)));
}
filter
});
}
20 changes: 11 additions & 9 deletions datasketches/src/bloom/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,7 @@ impl BloomFilter {
/// ```
pub fn contains_and_insert<T: Hash>(&mut self, item: &T) -> bool {
let (h0, h1) = self.compute_hash(item);
let was_present = self.check_bits(h0, h1);
self.set_bits(h0, h1);
was_present
self.set_bits(h0, h1)
}

/// Inserts an item into the filter.
Expand Down Expand Up @@ -564,12 +562,14 @@ impl BloomFilter {
true
}

/// Sets all k bits for the given hash values.
fn set_bits(&mut self, h0: u64, h1: u64) {
/// Sets all k bits and returns whether they were already set.
fn set_bits(&mut self, h0: u64, h1: u64) -> bool {
let mut were_all_set = true;
for i in 1..=self.num_hashes {
let bit_index = self.compute_bit_index(h0, h1, i);
self.set_bit(bit_index);
were_all_set &= self.set_bit(bit_index);
}
were_all_set
}

/// Computes a bit index using double hashing (Kirsch-Mitzenmacher).
Expand All @@ -593,16 +593,18 @@ impl BloomFilter {
(self.bit_array[word_index] & mask) != 0
}

/// Sets a single bit and updates the count if it wasn't already set.
fn set_bit(&mut self, bit_index: usize) {
/// Sets a single bit and returns whether it was already set.
fn set_bit(&mut self, bit_index: usize) -> bool {
let word_index = bit_index >> 6; // Equivalent to bit_index / 64
let bit_offset = bit_index & 63; // Equivalent to bit_index % 64
let mask = 1u64 << bit_offset;
let was_set = (self.bit_array[word_index] & mask) != 0;

if (self.bit_array[word_index] & mask) == 0 {
if !was_set {
self.bit_array[word_index] |= mask;
self.num_bits_set += 1;
}
was_set
}

/// Returns the estimated size of the filter in bytes.
Expand Down
2 changes: 0 additions & 2 deletions datasketches/src/thetafamily/common/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ pub const MAX_LG_K: u8 = 26;
/// Default log2 of K.
pub const DEFAULT_LG_K: u8 = 12;

/// Resize threshold (0.5 = 50% load factor).
pub const HASH_TABLE_RESIZE_THRESHOLD: f64 = 0.5;
/// Rebuild threshold (15/16 = 93.75% load factor).
pub const HASH_TABLE_REBUILD_THRESHOLD: f64 = 15.0 / 16.0;

Expand Down
11 changes: 4 additions & 7 deletions datasketches/src/thetafamily/common/hash_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ use crate::error::ErrorKind;
use crate::hash::MurmurHash3X64128;
use crate::hash::compute_seed_hash;
use crate::thetacommon::SketchEntry;
use crate::thetacommon::constants::HASH_TABLE_REBUILD_THRESHOLD;
use crate::thetacommon::constants::HASH_TABLE_RESIZE_THRESHOLD;
use crate::thetacommon::constants::MAX_LG_K;
use crate::thetacommon::constants::MAX_THETA;
use crate::thetacommon::constants::MIN_LG_K;
Expand Down Expand Up @@ -244,12 +242,11 @@ where

/// Return the current resize or rebuild capacity threshold.
pub fn capacity_threshold(&self) -> usize {
let fraction = if self.lg_cur_size <= self.lg_nom_size {
HASH_TABLE_RESIZE_THRESHOLD
if self.lg_cur_size <= self.lg_nom_size {
self.entries.len() / 2
} else {
HASH_TABLE_REBUILD_THRESHOLD
};
(fraction * self.entries.len() as f64) as usize
self.entries.len() - self.entries.len() / 16
}
}

/// Trim the table to nominal size k.
Expand Down