I think we need a path forward for a way of switching the global scheduler before making one "the defacto" scheduler.
Even when it was impossible to change the global memory allocator, there was a path forward to a solution. IMO both problems look too much alike, and I've also had the need of switching a global "logger" as well (on an MPI application with 100k processes you don't want 100k log files, but that all or some processes write to a single file), so this might be a more general pattern than just something special to allocators.
I also would expect that the parallel iterators are actually invoked with
.par_iter_on(scheduler) that allows the user to select the scheduler that should run the parallel computation. In this world,
.par_iter() would just call
.par_iter_on(global_scheduler). Either that or they always return a computation that one needs to pass to a scheduler to obtain a future.
rayon-like interfaces (e.g., join, scope+spawn) are really kind of a primitive in the language in much the same way as an allocator.
I think that the scheduler is the primitive that is like the allocator (e.g. like a std::collection). OTOH rayon-like interfaces are like algorithms on slices or iterators, they collection (or in this case scheduler) agnostic (up to some point).
So... if rayon would be scheduler agnostic, I would be ok with moving rayon into the nursery, in the same way that futures could be in the nursery, but mio probably won't be anytime soon.
I still would like to have a default scheduler, but I fear that if we offer a defacto one without a way to change it, as more crates depend on it the ecosystem will split into crates that one can use when one needs a custom scheduler and crates that one cannot use because they pull in a possibly very-heavy weight scheduler as a dependency that competes with the one the application wants to use.
Do I make sense?
Ideally, it would also be configurable, but i'm not sure how best to tackle that problem without incurring dynamic overheads.
In particular, I don't want fork to invoke dynamic code or something like that.
Ideally, one would configure it at compile time just like the allocator: there can only be one. We can then go and provide different schedulers, e.g. like a "type-erased" scheduler that could be switched dynamically at run-time (does anybody actually need this?) or something that can be set up at run-time, like what OpenMP does: code against a table of function pointers, and on program initialization, set those function pointers to a particular scheduler once, based on an environment variable. My point being, if we could switch the scheduler, we could play with these alternative solutions while having a default scheduler that works 99% of the time.