Skip to content

Commit c8294df

Browse files
Merge pull request #2 from onecodex/prefetching
Add prefetching support behind prefetching feature flag
2 parents 3471dcb + d5a87e3 commit c8294df

File tree

3 files changed

+25
-48
lines changed

3 files changed

+25
-48
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bfield"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
authors = ["Roderick Bovee <[email protected]>"]
55

66
[dependencies]
@@ -13,5 +13,6 @@ serde_json = { version = "1.0.4", optional = true}
1313

1414
[features]
1515
marker_lookup = []
16+
prefetching = []
1617
legacy = ["mmap-bitvec/backward_bytes", "serde_json"]
1718
wide_markers = ["mmap-bitvec/u128"]

src/bfield_member.rs

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::cmp::Ordering;
22
#[cfg(feature = "legacy")]
33
use std::fs::File;
4+
#[cfg(feature = "prefetching")]
5+
use std::intrinsics;
46
use std::io;
57
use std::ops::Range;
68
use std::path::Path;
@@ -155,6 +157,7 @@ impl<T: Clone + DeserializeOwned + Serialize> BFieldMember<T> {
155157
}
156158
}
157159

160+
#[inline]
158161
pub fn get(&self, key: &[u8]) -> BFieldLookup {
159162
let k = u32::from(self.params.n_marker_bits);
160163
let putative_marker = self.get_raw(key, k);
@@ -169,13 +172,29 @@ impl<T: Clone + DeserializeOwned + Serialize> BFieldMember<T> {
169172
fn get_raw(&self, key: &[u8], k: u32) -> BitVecSlice {
170173
let marker_width = self.params.marker_width as usize;
171174
let hash = murmurhash3_x64_128(key, 0);
172-
173175
let mut merged_marker = BitVecSlice::max_value();
176+
let mut positions: [usize; 16] = [0; 16]; // support up to 16 hashes
174177
for marker_ix in 0usize..self.params.n_hashes as usize {
175178
let pos = marker_pos(hash, marker_ix, self.bitvec.size(), marker_width);
176-
let marker = get_range(&self.bitvec, pos..pos + marker_width);
179+
positions[marker_ix] = pos;
180+
181+
// pre-fetch the memory!
182+
if cfg!(feature = "prefetching") {
183+
unsafe {
184+
let byte_idx_st = (pos >> 3) as usize;
185+
let ptr: *const u8 = self.bitvec.mmap.as_ptr().offset(byte_idx_st as isize);
186+
#[cfg(feature = "prefetching")]
187+
intrinsics::prefetch_read_data(ptr, 3);
188+
}
189+
}
190+
}
191+
192+
assert!(self.params.n_hashes <= 16);
193+
for marker_ix in 0usize..self.params.n_hashes as usize {
194+
let pos = positions[marker_ix];
195+
let marker = self.bitvec.get_range(pos..pos + marker_width);
177196
merged_marker &= marker;
178-
if merged_marker.count_ones().cmp(&k) == Ordering::Less {
197+
if merged_marker.count_ones() < k {
179198
return 0;
180199
}
181200
}
@@ -226,50 +245,6 @@ fn marker_pos(hash: (u64, u64), n: usize, total_size: usize, _: usize) -> usize
226245
i64::abs(mashed_hash % (total_size as i64 - 64)) as usize
227246
}
228247

229-
/// This is totally messed up, but we get a speed bump by doing this
230-
/// instead of using the _exact same_ function on the struct.
231-
#[cfg(not(feature = "legacy"))]
232-
fn get_range(bitvec: &MmapBitVec, r: Range<usize>) -> BitVecSlice {
233-
if r.end - r.start > BIT_VEC_SLICE_SIZE as usize {
234-
panic!(format!("Range too large (>{})", BIT_VEC_SLICE_SIZE))
235-
} else if r.end > bitvec.size {
236-
panic!("Range ends outside of BitVec")
237-
}
238-
let byte_idx_st = (r.start >> 3) as usize;
239-
let byte_idx_en = ((r.end - 1) >> 3) as usize;
240-
let new_size: u8 = (r.end - r.start) as u8;
241-
242-
let mut v;
243-
let ptr: *const u8 = bitvec.mmap.as_ptr();
244-
245-
// read the last byte first
246-
unsafe {
247-
v = BitVecSlice::from(*ptr.offset(byte_idx_en as isize));
248-
}
249-
// align the end of the data with the end of the u64/u128
250-
v >>= 7 - ((r.end - 1) & 7);
251-
252-
let bit_offset = new_size + (r.start & 7) as u8;
253-
// copy over byte by byte
254-
// it would be faster to coerce into a u8 and a u64 (in case it spans 9 bytes) and then
255-
// copy over, but this doesn't work if the start is <8 bytes from the end, so we're doing
256-
// this for now and we can add a special case for that later
257-
for (new_idx, old_idx) in (byte_idx_st..byte_idx_en).enumerate() {
258-
unsafe {
259-
v |= BitVecSlice::from(*ptr.offset(old_idx as isize)) <<
260-
(bit_offset - 8u8 * (new_idx as u8 + 1));
261-
}
262-
}
263-
264-
// mask out the high bits in case we copied extra
265-
v & BitVecSlice::max_value() >> (BIT_VEC_SLICE_SIZE - new_size)
266-
}
267-
268-
#[cfg(feature = "legacy")]
269-
fn get_range(bitvec: &MmapBitVec, r: Range<usize>) -> BitVecSlice {
270-
bitvec.get_range(r)
271-
}
272-
273248
#[test]
274249
fn test_bfield() {
275250
let mut bfield: BFieldMember<usize> = BFieldMember::in_memory(1024, 3, 64, 4).unwrap();

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![cfg_attr(feature = "marker_lookup", feature(const_fn))]
2+
#![cfg_attr(feature = "prefetching", feature(core_intrinsics))]
23
extern crate bincode;
34
extern crate mmap_bitvec;
45
extern crate murmurhash3;

0 commit comments

Comments
 (0)