-
Notifications
You must be signed in to change notification settings - Fork 506
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A shim mode for targets without threading support #861
Comments
Hmm. Interesting question. How much does it matter if the code runs fast? I think the question is where is the right place to do this -- I think you could do it in a pretty narrow way at the right spot, but the resulting code might have some extra operations here and there. |
Another way to look at that: even with |
See also #591. |
Mm, yes, that's a good point. |
AIUI, the standard library for wasm still builds as if it has threading support, but it panics at runtime. So one possibility for that case is to use my rayon-cond wrapper for iterators, and always choose the serial mode for wasm builds. As long as you're consistent with that, it won't ever try (and fail) to create the actual thread pool. The other shims you linked are ignoring the fact that there are real API differences between |
Indeed, that's where complexity starts creeping in in bigger apps, and why I think that
Yeah, What I'm more interested in is just iterators themselves as that's probably most popular use-case and would allow to easily use same API for parallel and non-parallel builds. Well, also probably |
Oh yeah, that sounds pretty similar - although the use-cases are quite different, the desired end solution is probably the same. |
I'm glad I found this issue, I was surprised I couldn't find out how to do this, so I ended up defining and implementing Leaving the code here for future searchers who perhaps don't have the rusty chops to build it for themselves without seeing an example first: // put this in a file called `no_rayon.rs`, then you can conditionally pull it in as required, i.e.
//
// #[cfg(not(target_arch = "wasm32"))]
// use rayon::prelude::*;
// #[cfg(target_arch = "wasm32")]
// mod no_rayon;
// use no_rayon::prelude::*;
pub mod prelude {
pub use super::{NoRayonSlice, NoRayonSliceMut};
}
pub trait NoRayonSlice<T> {
fn par_iter(&self) -> core::slice::Iter<'_, T>;
fn par_chunks_exact(&self, chunk_size: usize) -> core::slice::ChunksExact<'_, T>;
}
impl<T> NoRayonSlice<T> for [T] {
fn par_iter(&self) -> core::slice::Iter<'_, T> {
self.iter();
}
fn par_chunks_exact(&self, chunk_size: usize) -> core::slice::ChunksExact<'_, T> {
self.chunks_exact(chunk_size);
}
}
pub trait NoRayonSliceMut<T> {
fn par_chunks_exact_mut(&mut self, chunk_size: usize) -> core::slice::ChunksExactMut<'_, T>;
fn par_iter_mut(&mut self) -> core::slice::IterMut<'_, T>;
}
impl<T> NoRayonSliceMut<T> for [T] {
fn par_chunks_exact_mut(&mut self, chunk_size: usize) -> core::slice::ChunksExactMut<'_, T> {
self.chunks_exact_mut(chunk_size);
}
fn par_iter_mut(&mut self) -> core::slice::IterMut<'_, T> {
self.iter_mut();
}
} Also adding these keywords for searchability: fallback, fall back, polyfill, poly fill |
Yeah I hope there will be some further discussion on this, the conversation above just stopped. |
This would be very useful for us. We have a scenario where we are compiling to webassembly and transient dependencies use However this means that LLVM will enable shared memory and thus Being able to have a shim mode that could completely disable threading would solve the problem for us. |
Light sketch:
|
I second the request. I need to support single-threaded WASM in my libraries (the experimental WASM threading support is too complex and not supported well enough). I've been using a shim like this: https://github.com/ImageOptim/libimagequant/blob/main/src/rayoff.rs#L34 but it would be best if it could be supported as a compile-time Cargo feature in rayon itself, so that I can apply this to the whole crate instead of faking APIs one by one. |
@kornelski Offtop:If by "not supported well enough" you mean browsers, then Wasm threads are actually supported in all major browsers nowadays. If you mean on the Rust side, yeah, there is no built-in support unfortunately. To cover at least Rayon use-cases and work around browser bugs, I created the wasm-bindgen-rayon, but, yeah, it still has the usual Wasm threads caveats in terms of usage (e.g. you can't use it from the main browser thread). |
@cuviper I'm not aware of cases where we'd need to disable multithreading at runtime tbh. Moreover, for cases like Wasm compile-time flag would be preferable because it would allow to completely remove Rayon threading code & stdlib thread panics code from the resulting Wasm module, leading to smaller network sizes. |
The runtime shim would be for cases like #591, where you probably don't want to change the actual codegen, but otherwise need to make sense of your code apart from rayon. Although I'm not sure it would be any better than what you get with I can see the use for compile-time trimming too. That would be even better served by cutting rayon out entirely for those threadless targets, but that's more work on the user side. |
Like I've linked above, its doable to disable rayon per application, but it's a significant hassle. It affects many iterators and requires shims for join, scope, etc. It's not as easy as merely making the dependency optional. It's commonly needed for WASM, and it's a hassle to reinvent shim traits for every project. I've tried creating reusable dummy no-op implementations of rayon's interfaces, but the api surface is large. You probably know best where to cut the code out to keep most of the interface working without any thread use. I'm concerned about WASM, not profiling use-case. If you think they're best served with different solutions, I can open a separate issue for single-threaded WASM builds. |
This would also be very useful to me as well! I have a project that ideally would work on WASM, but it uses a lot of the rayon API. While wasm-bindgen-rayon and rayon-cond are both really cool, unfortunately both have unworkable caveats for me. Were there any additional updates or progress for this? |
This is also extremely important for my usecase, which involves porting an entire codebase full of data-parallel uses of rayon, none of which would be deal-breaking to serialize, to WASM. |
I'm adding another +1 here. Trying to use rayon in
|
For another polyfill, we've defined our own macros that, depending on a feature flag, either invokes the |
+1 |
It seems native threads will be supported in the upcoming Refs: |
See #1019 -- feedback would be appreciated! |
To be clear, #1019 does not try to revert to regular Iterator APIs, as that would be hugely invasive. My change is basically just catching |
Try to make our program running on wasi threads target, but it just keeps throwing problems on rayon. We use this shims. #[cfg(target_family = "wasm")]
mod wasm_shims {
pub use std::iter::Iterator as ParallelIterator;
pub trait ParallelBridge: Sized {
fn par_bridge(self) -> Self;
}
impl<T: Iterator + Send> ParallelBridge for T {
fn par_bridge(self) -> Self {
self
}
}
pub trait IntoParallelIterator: Sized {
type Item;
type Iter: Iterator<Item = Self::Item>;
fn into_par_iter(self) -> Self::Iter;
}
impl<I> IntoParallelIterator for I
where
I: IntoIterator,
{
type Item = I::Item;
type Iter = I::IntoIter;
fn into_par_iter(self) -> Self::Iter {
self.into_iter()
}
}
pub trait IntoParallelRefIterator<'data> {
type Item: 'data;
type Iter: ParallelIterator<Item = Self::Item>;
fn par_iter(&'data self) -> Self::Iter;
}
impl<'data, I: 'data + ?Sized> IntoParallelRefIterator<'data> for I
where
&'data I: IntoParallelIterator,
{
type Iter = <&'data I as IntoParallelIterator>::Iter;
type Item = <&'data I as IntoParallelIterator>::Item;
fn par_iter(&'data self) -> Self::Iter {
self.into_par_iter()
}
}
}
use rayon::iter::IntoParallelRefMutIterator;
#[cfg(target_family = "wasm")]
pub use wasm_shims::{
IntoParallelIterator, IntoParallelRefIterator, ParallelBridge, ParallelIterator,
};
#[cfg(not(target_family = "wasm"))]
pub use rayon::iter::{
IntoParallelIterator, IntoParallelRefIterator, ParallelBridge, ParallelIterator,
};
fn _usages() {
let mut demo = vec![1, 2, 3, 4, 5];
demo.iter().par_bridge().for_each(|_| {});
demo.iter_mut().par_bridge().for_each(|_| {});
demo.clone().into_iter().par_bridge().for_each(|_| {});
demo.par_iter().for_each(|_| {});
demo.par_iter_mut().for_each(|_| {});
demo.clone().into_par_iter().for_each(|_| {});
} |
@hyf0 Please open a new issue if you're having trouble with rayon. However, the way your |
Maybe I haven't looked through issues closely enough, so apologies if this was brought up before.
One thing that comes up often when using Rayon is making sure that code works correctly on targets without threading support (e.g. wasm32-wasi or regular wasm32-unknown-unknown without threads support in general).
Probably the most common way to do this in a way that abstracts away platform differences is to build custom shim for the used
rayon
APIs that uses regularIterator
instead and expose it asmod rayon
that will be included instead ofrayon
crate when corresponding feature is disabled (e.g. 1, 2, 3, 4).However, Rayon itself feels like a lot more appropriate place for such shimming, since it has more control over APIs and would be easier to maintain than custom shims in each separate app that wants to build Rayon-dependant code in a cross-platform manner.
So, the question is: how realistic would it be to make Rayon use regular Iterators when a corresponding Cargo feature is enabled? Given that Rayon can already handle
RAYON_NUM_THREADS=1
case, and the behaviour should be pretty similar, it seems doable but I'm not familiar with internals enough to estimate amount of work this would take.The text was updated successfully, but these errors were encountered: