ferrilab / bitvec

A crate for managing memory bit by bit
https://myrrlyn.net/crates/bitvec
MIT License
1.2k stars 115 forks source link

Feature request: Optional feature for rayon support #143

Open nothingnesses opened 2 years ago

nothingnesses commented 2 years ago

Currently, code like the following don't work:

use bitvec::prelude::*;
use rayon::prelude::*;
use std::sync::atomic::AtomicUsize;

fn main() {
    let mut list = bitvec![Lsb0, AtomicUsize; 0; 100];
    list.par_iter_mut().step_by(2).for_each(|mut bit| {
        *bit = true;
    });
    println!("{:?}", list);
}

But the above works if vec![false; 100] is used instead of bitvec![Lsb0, AtomicUsize; 0; 100] (or bitvec![0; 100]), or if .par_iter_mut() is replaced with .iter_mut().

It would be great if bitvec had support for rayon to allow its data structures to be iterated through in parallel in a convenient manner (i.e., with rayon's par_iter() or par_iter_mut() methods). Afaiu, this would require rayon's ParallelIterator and/or IndexedParallelIterator traits be implemented for bitvec's types.

Fwiw, here is part of a message from @myrrlyn about one way support might be added:

rayon methods require that i directly implement its extension traits. they don't have a blanket impl Iterator -> impl ParallelIterator transform. you can fake it with a much uglier boilerplate by using rayon::scope to drive a serial iterator, then parallelize it by making the loop body spawn a thread containing the rest of the work onto the rayon scope

myrrlyn commented 2 years ago

Sample program that currently works:

use bitvec::prelude::*;

#[test]
fn parallelism() {
  let bits = bits![mut Lsb0, usize; 0; 800];
  rayon::scope(|s| {
    for (idx, chunk) in bits.chunks_mut(20).enumerate() {
      s.spawn(move |_| {
        chunk.store(!0u32);
        println!("{:02}", idx);
      });
    }
  });
  assert!(bits.all());
}

Implementation notes from doing a quick sketch:

Questions:

I probably won't get back to this until, oh, February, so this should serve to restore everything I learned today when I do pick it back up.

nothingnesses commented 2 years ago

It seems rayon has added implementations for RChunks, although there hasn't yet been a version published on crates.io containing these changes.

bonsairobo commented 9 months ago

I found it easiest to just use bits.as_raw_mut_slice().par_iter_mut() and manually set bits of the integer storage.

Anivie commented 2 weeks ago

Is there any progress now? Can we implement traits such as FromParallelIterator<bool> to facilitate more convenient collaboration with Rayon?

let mask = [10.; 100];
let m = mask.par_iter().map(|x| *x > 100.).collect::<BitVec>();
tcerqueira commented 5 days ago

Is there any progress now? Can we implement traits such as FromParallelIterator<bool> to facilitate more convenient collaboration with Rayon?

let mask = [10.; 100];
let m = mask.par_iter().map(|x| *x > 100.).collect::<BitVec>();

This one has been bugging me, I can iterate in parallel but collecting I'm not sure. Maybe collecting into the underlying T and then call BitVec::from_vec?