Bug 1933199 - update Rust rayon crate to v1.10.0 r=kershaw,supply-chain-reviewers,valentin

[mozilla/neqo#2135](https://github.com/mozilla/neqo/pull/2135) adds `mtu` crate
to `neqo-*`. `mtu` crate depends on `windows-bindgen`. `windows-bindgen` depends
on `rayon` `1.7`.

On the other hand mozilla-central depends on [`rayon`
`v1.6.1`](https://searchfox.org/mozilla-central/rev/7987501f2c2ed1914e5c682bd328ace9c4a7c6cd/Cargo.lock#5149-5157).

Given that mozilla-central allows at most one version of each crate, let's
update mozilla-central to `rayon` `1.10.0`, i.e. the most recent version.

See https://github.com/mozilla/neqo/pull/2135#issuecomment-2497077670 for details.

Differential Revision: https://phabricator.services.mozilla.com/D230127
This commit is contained in:
Max Inden
2024-11-28 14:45:20 +00:00
parent 5123d0ecd3
commit 80d40104c4
67 changed files with 3072 additions and 425 deletions

8
Cargo.lock generated
View File

@@ -5158,9 +5158,9 @@ checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544"
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.6.1" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [ dependencies = [
"either", "either",
"rayon-core", "rayon-core",
@@ -5168,9 +5168,9 @@ dependencies = [
[[package]] [[package]]
name = "rayon-core" name = "rayon-core"
version = "1.12.0" version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [ dependencies = [
"crossbeam-deque", "crossbeam-deque",
"crossbeam-utils", "crossbeam-utils",

View File

@@ -1668,15 +1668,35 @@ who = "David Cook <dcook@divviup.org>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
version = "0.6.3" version = "0.6.3"
[[audits.isrg.audits.rayon-core]] [[audits.isrg.audits.rayon]]
who = "Brandon Pitman <bran@bran.land>" who = "Brandon Pitman <bran@bran.land>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "1.10.2 -> 1.11.0" delta = "1.6.1 -> 1.7.0"
[[audits.isrg.audits.rayon-core]] [[audits.isrg.audits.rayon]]
who = "David Cook <dcook@divviup.org>" who = "David Cook <dcook@divviup.org>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "1.11.0 -> 1.12.0" delta = "1.7.0 -> 1.8.0"
[[audits.isrg.audits.rayon]]
who = "Ameer Ghani <inahga@divviup.org>"
criteria = "safe-to-deploy"
delta = "1.8.0 -> 1.8.1"
[[audits.isrg.audits.rayon]]
who = "Brandon Pitman <bran@bran.land>"
criteria = "safe-to-deploy"
delta = "1.8.1 -> 1.9.0"
[[audits.isrg.audits.rayon]]
who = "Brandon Pitman <bran@bran.land>"
criteria = "safe-to-deploy"
delta = "1.9.0 -> 1.10.0"
[[audits.isrg.audits.rayon-core]]
who = "Ameer Ghani <inahga@divviup.org>"
criteria = "safe-to-deploy"
version = "1.12.1"
[[audits.isrg.audits.sha2]] [[audits.isrg.audits.sha2]]
who = "David Cook <dcook@divviup.org>" who = "David Cook <dcook@divviup.org>"

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"c25083c4b0fc46e0f63b88b4bc346a1c034698c16ece8f04ce72dd2af9cc7ffb","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","README.md":"7281273bea1d5fdc57731513cf9f0e3b911d06ac9905b03a8375a1324951c35b","build.rs":"fa31cb198b772600d100a7c403ddedccef637d2e6b2da431fa7f02ca41307fc6","src/broadcast/mod.rs":"2c9a84e7e6e5e8d8e23e28d6f2703825d7d6af59f0a16bc6125d5f0d25bd7598","src/broadcast/test.rs":"fe50fc868e67d855a9f71e078b0c3a7780e789652abb4b586accb4ccf035e872","src/compile_fail/mod.rs":"4d70256295bd64691a8c1994b323559cda1888e85f0b45ca55711541c257dcb6","src/compile_fail/quicksort_race1.rs":"35f498cda38f4eb6e00117f78ed68e0fe5a3fa61c25303d9c08a19bda345bc6c","src/compile_fail/quicksort_race2.rs":"cbb40030c7867cae34bb373b6ec5d97c2ac6de39bc917f47879b30eb423924bc","src/compile_fail/quicksort_race3.rs":"8403643e64c969306b1a9b1243378e6ccdd313b57e1878dbd31393618082fd35","src/compile_fail/rc_return.rs":"197894803d8df58fc8005d90c86b90cd98f1972f1a4b57438516a565df35903f","src/compile_fail/rc_upvar.rs":"42d110429621f407ef0dada1306dab116583d2c782a99894204dd8e0ccd2312f","src/compile_fail/scope_join_bad.rs":"892959949f77cadfc07458473e7a290301182027ca64428df5a8ce887be0892b","src/job.rs":"06de0c2add2e303b6383bf11f5f0d75775c1efe6aa7bc16de3992117f1012f09","src/join/mod.rs":"7638c0fc1da1a2d2b14673c8a2e0f87d26c24232cebee26fd334bdc2caa80886","src/join/test.rs":"157db5306e8df89a8eea19dbba499f26c2f44d9803cb36a796c852a9a695821e","src/latch.rs":"81da563b29b03455cd22073d243eaed081e953873c14ac202f6605cd3dac09a5","src/lib.rs":"53bb01b167d56c6ace035666b570fff648eedf03a5c8c415ec37136a0ef35697","src/private.rs":"152f6d65ce4741616a1dec796b9442f78a018d38bb040f76c4cd85008333a3bb","src/registry.rs":"c464c4fdb36c85cfe2a10d6196802b036bb76985d737ab9a67d708f908877672","src/scope/mod.rs":"421a5561093928b1d0081d34c2bff78377055d8f6de0689088f52fe476d3a56a","src/scope/test.rs":"d4f068cae4ee4483b41bd3054582d96e74ced46eb57361e7510ef62d4318d340","src/sleep/README.md":"e1ac1a5556cf257f38b7654feb0615c208d9186fefbe52a584d4fe6545d7c373","src/sleep/counters.rs":"e9eccc7d76d17415156c12d30cc7bf89a5c64ca5742965bb4e6c1ce23c2782e9","src/sleep/mod.rs":"23a9116f84653a5f68ab21c910f1dea5314a5332fdc9473a87710974f4b2c717","src/spawn/mod.rs":"745494a18fc4901c37ea2f45a1324abf5bd2a4d9c840620956e6633755116d88","src/spawn/test.rs":"a28f8943f28a4cef642b6429c538b1df879c9eb1db9927ce69b97c686bf81173","src/test.rs":"7d0dee06fcf41bddf77449a85cece44133f966a0622a31cf3ed110fbe83e094e","src/thread_pool/mod.rs":"392ad78a209826c4fb7257288dc082ace380220893d44559480045587e279202","src/thread_pool/test.rs":"657b1938993eb98fb5f3fd1d02a77728e37d0e833390b4ba82926b9107ce3170","src/unwind.rs":"7baa4511467d008b14856ea8de7ced92b9251c9df4854f69c81f7efc3cf0cd6c","tests/double_init_fail.rs":"8c208ce45e83ab1dfc5890353d5b2f06fc8005684ae622827a65d05abb35a072","tests/init_zero_threads.rs":"5c7f7e0e13e9ead3733253e30d6b52ac5ee66fd6c105999d096bdf31cfccaf95","tests/scope_join.rs":"56f570c4b6a01704aacf93e7f17f89fe0f40f46ed6f9ede517abfe9adaf91f83","tests/scoped_threadpool.rs":"24d1293fe65ad5f194bbff9d1ef0486c3440d0a3783f04eaaaae4929adef5cb8","tests/simple_panic.rs":"916d40d36c1a0fad3e1dfb31550f0672641feab4b03d480f039143dbe2f2445f","tests/stack_overflow_crash.rs":"87b962c66f301ac44f808d992d4e8b861305db0c282f256761a5075c9f018243","tests/use_current_thread.rs":"fe1b981e77e422e616c09502731a70fb2f1c023d2386ef32c9d47e5a6f5bc162"},"package":"5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"} {"files":{"Cargo.toml":"60f04aab82c6489c779cf19535fddf0ec1a673edc07f523c66c10996806d2fbe","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","README.md":"7281273bea1d5fdc57731513cf9f0e3b911d06ac9905b03a8375a1324951c35b","build.rs":"fa31cb198b772600d100a7c403ddedccef637d2e6b2da431fa7f02ca41307fc6","src/broadcast/mod.rs":"2c9a84e7e6e5e8d8e23e28d6f2703825d7d6af59f0a16bc6125d5f0d25bd7598","src/broadcast/test.rs":"fe50fc868e67d855a9f71e078b0c3a7780e789652abb4b586accb4ccf035e872","src/compile_fail/mod.rs":"4d70256295bd64691a8c1994b323559cda1888e85f0b45ca55711541c257dcb6","src/compile_fail/quicksort_race1.rs":"35f498cda38f4eb6e00117f78ed68e0fe5a3fa61c25303d9c08a19bda345bc6c","src/compile_fail/quicksort_race2.rs":"cbb40030c7867cae34bb373b6ec5d97c2ac6de39bc917f47879b30eb423924bc","src/compile_fail/quicksort_race3.rs":"8403643e64c969306b1a9b1243378e6ccdd313b57e1878dbd31393618082fd35","src/compile_fail/rc_return.rs":"197894803d8df58fc8005d90c86b90cd98f1972f1a4b57438516a565df35903f","src/compile_fail/rc_upvar.rs":"42d110429621f407ef0dada1306dab116583d2c782a99894204dd8e0ccd2312f","src/compile_fail/scope_join_bad.rs":"892959949f77cadfc07458473e7a290301182027ca64428df5a8ce887be0892b","src/job.rs":"06de0c2add2e303b6383bf11f5f0d75775c1efe6aa7bc16de3992117f1012f09","src/join/mod.rs":"7638c0fc1da1a2d2b14673c8a2e0f87d26c24232cebee26fd334bdc2caa80886","src/join/test.rs":"157db5306e8df89a8eea19dbba499f26c2f44d9803cb36a796c852a9a695821e","src/latch.rs":"3f5c73cc6998d7cea177c19a3c7ce0d4a5667c65d77f744a2159872f09e8fba6","src/lib.rs":"d34f6cbaba40293d3b3e095bd2eae989cce3467fe686835a37e80652ca8133df","src/private.rs":"152f6d65ce4741616a1dec796b9442f78a018d38bb040f76c4cd85008333a3bb","src/registry.rs":"bcfedc6d51eade2bedc30271ab8b21e98700e45f74bd8967d02ae6fe17f73a94","src/scope/mod.rs":"421a5561093928b1d0081d34c2bff78377055d8f6de0689088f52fe476d3a56a","src/scope/test.rs":"d4f068cae4ee4483b41bd3054582d96e74ced46eb57361e7510ef62d4318d340","src/sleep/README.md":"e1ac1a5556cf257f38b7654feb0615c208d9186fefbe52a584d4fe6545d7c373","src/sleep/counters.rs":"e9eccc7d76d17415156c12d30cc7bf89a5c64ca5742965bb4e6c1ce23c2782e9","src/sleep/mod.rs":"7576bc0a54c30f35b10c63a6fd23f2e33774ba994352634ea1ca7865818fb2a4","src/spawn/mod.rs":"087d2a3e5fd5dd22a780c14d71509f9eea0bac92ccfa35d9933c0b39b3e269e1","src/spawn/test.rs":"a28f8943f28a4cef642b6429c538b1df879c9eb1db9927ce69b97c686bf81173","src/test.rs":"7d0dee06fcf41bddf77449a85cece44133f966a0622a31cf3ed110fbe83e094e","src/thread_pool/mod.rs":"4c85b46eb8ba05631b658996f0d6c64247a05575c89621e0a54a1fc056cc447f","src/thread_pool/test.rs":"657b1938993eb98fb5f3fd1d02a77728e37d0e833390b4ba82926b9107ce3170","src/unwind.rs":"7baa4511467d008b14856ea8de7ced92b9251c9df4854f69c81f7efc3cf0cd6c","tests/double_init_fail.rs":"8c208ce45e83ab1dfc5890353d5b2f06fc8005684ae622827a65d05abb35a072","tests/init_zero_threads.rs":"5c7f7e0e13e9ead3733253e30d6b52ac5ee66fd6c105999d096bdf31cfccaf95","tests/scope_join.rs":"56f570c4b6a01704aacf93e7f17f89fe0f40f46ed6f9ede517abfe9adaf91f83","tests/scoped_threadpool.rs":"24d1293fe65ad5f194bbff9d1ef0486c3440d0a3783f04eaaaae4929adef5cb8","tests/simple_panic.rs":"916d40d36c1a0fad3e1dfb31550f0672641feab4b03d480f039143dbe2f2445f","tests/stack_overflow_crash.rs":"87b962c66f301ac44f808d992d4e8b861305db0c282f256761a5075c9f018243","tests/use_current_thread.rs":"fe1b981e77e422e616c09502731a70fb2f1c023d2386ef32c9d47e5a6f5bc162"},"package":"1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"}

View File

@@ -13,7 +13,7 @@
edition = "2021" edition = "2021"
rust-version = "1.63" rust-version = "1.63"
name = "rayon-core" name = "rayon-core"
version = "1.12.0" version = "1.12.1"
authors = [ authors = [
"Niko Matsakis <niko@alum.mit.edu>", "Niko Matsakis <niko@alum.mit.edu>",
"Josh Stone <cuviper@gmail.com>", "Josh Stone <cuviper@gmail.com>",
@@ -68,6 +68,10 @@ version = "0.8.1"
[dependencies.crossbeam-utils] [dependencies.crossbeam-utils]
version = "0.8.0" version = "0.8.0"
[dependencies.wasm_sync]
version = "0.1.0"
optional = true
[dev-dependencies.rand] [dev-dependencies.rand]
version = "0.8" version = "0.8"
@@ -77,5 +81,8 @@ version = "0.3"
[dev-dependencies.scoped-tls] [dev-dependencies.scoped-tls]
version = "1.0" version = "1.0"
[features]
web_spin_lock = ["dep:wasm_sync"]
[target."cfg(unix)".dev-dependencies.libc] [target."cfg(unix)".dev-dependencies.libc]
version = "0.2" version = "0.2"

View File

@@ -1,10 +1,11 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Condvar, Mutex}; use std::sync::Arc;
use std::usize; use std::usize;
use crate::registry::{Registry, WorkerThread}; use crate::registry::{Registry, WorkerThread};
use crate::sync::{Condvar, Mutex};
/// We define various kinds of latches, which are all a primitive signaling /// We define various kinds of latches, which are all a primitive signaling
/// mechanism. A latch starts as false. Eventually someone calls `set()` and /// mechanism. A latch starts as false. Eventually someone calls `set()` and

View File

@@ -103,6 +103,12 @@ pub use self::thread_pool::current_thread_index;
pub use self::thread_pool::ThreadPool; pub use self::thread_pool::ThreadPool;
pub use self::thread_pool::{yield_local, yield_now, Yield}; pub use self::thread_pool::{yield_local, yield_now, Yield};
#[cfg(not(feature = "web_spin_lock"))]
use std::sync;
#[cfg(feature = "web_spin_lock")]
use wasm_sync as sync;
use self::registry::{CustomSpawn, DefaultSpawn, ThreadSpawn}; use self::registry::{CustomSpawn, DefaultSpawn, ThreadSpawn};
/// Returns the maximum number of threads that Rayon supports in a single thread-pool. /// Returns the maximum number of threads that Rayon supports in a single thread-pool.

View File

@@ -1,6 +1,7 @@
use crate::job::{JobFifo, JobRef, StackJob}; use crate::job::{JobFifo, JobRef, StackJob};
use crate::latch::{AsCoreLatch, CoreLatch, Latch, LatchRef, LockLatch, OnceLatch, SpinLatch}; use crate::latch::{AsCoreLatch, CoreLatch, Latch, LatchRef, LockLatch, OnceLatch, SpinLatch};
use crate::sleep::Sleep; use crate::sleep::Sleep;
use crate::sync::Mutex;
use crate::unwind; use crate::unwind;
use crate::{ use crate::{
ErrorKind, ExitHandler, PanicHandler, StartHandler, ThreadPoolBuildError, ThreadPoolBuilder, ErrorKind, ExitHandler, PanicHandler, StartHandler, ThreadPoolBuildError, ThreadPoolBuilder,
@@ -15,7 +16,7 @@ use std::io;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, Once}; use std::sync::{Arc, Once};
use std::thread; use std::thread;
use std::usize; use std::usize;

View File

@@ -2,9 +2,9 @@
//! for an overview. //! for an overview.
use crate::latch::CoreLatch; use crate::latch::CoreLatch;
use crate::sync::{Condvar, Mutex};
use crossbeam_utils::CachePadded; use crossbeam_utils::CachePadded;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::{Condvar, Mutex};
use std::thread; use std::thread;
use std::usize; use std::usize;

View File

@@ -4,8 +4,8 @@ use crate::unwind;
use std::mem; use std::mem;
use std::sync::Arc; use std::sync::Arc;
/// Fires off a task into the Rayon threadpool in the "static" or /// Puts the task into the Rayon threadpool's job queue in the "static"
/// "global" scope. Just like a standard thread, this task is not /// or "global" scope. Just like a standard thread, this task is not
/// tied to the current stack frame, and hence it cannot hold any /// tied to the current stack frame, and hence it cannot hold any
/// references other than those with `'static` lifetime. If you want /// references other than those with `'static` lifetime. If you want
/// to spawn a task that references stack data, use [the `scope()` /// to spawn a task that references stack data, use [the `scope()`

View File

@@ -80,6 +80,43 @@ impl ThreadPool {
/// thread-local data from the current thread will not be /// thread-local data from the current thread will not be
/// accessible. /// accessible.
/// ///
/// # Warning: execution order
///
/// If the current thread is part of a different thread pool, it will try to
/// keep busy while the `op` completes in its target pool, similar to
/// calling [`ThreadPool::yield_now()`] in a loop. Therefore, it may
/// potentially schedule other tasks to run on the current thread in the
/// meantime. For example
///
/// ```rust
/// # use rayon_core as rayon;
/// fn main() {
/// rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap();
/// let pool = rayon_core::ThreadPoolBuilder::default().build().unwrap();
/// let do_it = || {
/// print!("one ");
/// pool.install(||{});
/// print!("two ");
/// };
/// rayon::join(|| do_it(), || do_it());
/// }
/// ```
///
/// Since we configured just one thread in the global pool, one might
/// expect `do_it()` to run sequentially, producing:
///
/// ```ascii
/// one two one two
/// ```
///
/// However each call to `install()` yields implicitly, allowing rayon to
/// run multiple instances of `do_it()` concurrently on the single, global
/// thread. The following output would be equally valid:
///
/// ```ascii
/// one one two two
/// ```
///
/// # Panics /// # Panics
/// ///
/// If `op` should panic, that panic will be propagated. /// If `op` should panic, that panic will be propagated.

File diff suppressed because one or more lines are too long

View File

@@ -11,9 +11,9 @@
[package] [package]
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.63"
name = "rayon" name = "rayon"
version = "1.6.1" version = "1.10.0"
authors = [ authors = [
"Niko Matsakis <niko@alum.mit.edu>", "Niko Matsakis <niko@alum.mit.edu>",
"Josh Stone <cuviper@gmail.com>", "Josh Stone <cuviper@gmail.com>",
@@ -22,7 +22,6 @@ exclude = [
"/ci/*", "/ci/*",
"/scripts/*", "/scripts/*",
"/.github/*", "/.github/*",
"/bors.toml",
] ]
description = "Simple work-stealing parallelism for Rust" description = "Simple work-stealing parallelism for Rust"
documentation = "https://docs.rs/rayon/" documentation = "https://docs.rs/rayon/"
@@ -43,13 +42,20 @@ version = "1.0"
default-features = false default-features = false
[dependencies.rayon-core] [dependencies.rayon-core]
version = "1.10.0" version = "1.12.1"
[dev-dependencies.lazy_static] [dependencies.wasm_sync]
version = "1" version = "0.1.0"
optional = true
[dev-dependencies.rand] [dev-dependencies.rand]
version = "0.8" version = "0.8"
[dev-dependencies.rand_xorshift] [dev-dependencies.rand_xorshift]
version = "0.3" version = "0.3"
[features]
web_spin_lock = [
"dep:wasm_sync",
"rayon-core/web_spin_lock",
]

View File

@@ -2,8 +2,8 @@
[![Rayon crate](https://img.shields.io/crates/v/rayon.svg)](https://crates.io/crates/rayon) [![Rayon crate](https://img.shields.io/crates/v/rayon.svg)](https://crates.io/crates/rayon)
[![Rayon documentation](https://docs.rs/rayon/badge.svg)](https://docs.rs/rayon) [![Rayon documentation](https://docs.rs/rayon/badge.svg)](https://docs.rs/rayon)
![minimum rustc 1.56](https://img.shields.io/badge/rustc-1.56+-red.svg) ![minimum rustc 1.63](https://img.shields.io/badge/rustc-1.63+-red.svg)
[![build status](https://github.com/rayon-rs/rayon/workflows/master/badge.svg)](https://github.com/rayon-rs/rayon/actions) [![build status](https://github.com/rayon-rs/rayon/workflows/main/badge.svg)](https://github.com/rayon-rs/rayon/actions)
[![Join the chat at https://gitter.im/rayon-rs/Lobby](https://badges.gitter.im/rayon-rs/Lobby.svg)](https://gitter.im/rayon-rs/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/rayon-rs/Lobby](https://badges.gitter.im/rayon-rs/Lobby.svg)](https://gitter.im/rayon-rs/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Rayon is a data-parallelism library for Rust. It is extremely Rayon is a data-parallelism library for Rust. It is extremely
@@ -13,7 +13,7 @@ enjoy [this blog post][blog] about Rayon, which gives more background
and details about how it works, or [this video][video], from the Rust and details about how it works, or [this video][video], from the Rust
Belt Rust conference.) Rayon is Belt Rust conference.) Rayon is
[available on crates.io](https://crates.io/crates/rayon), and [available on crates.io](https://crates.io/crates/rayon), and
[API Documentation is available on docs.rs](https://docs.rs/rayon/). [API documentation is available on docs.rs](https://docs.rs/rayon).
[blog]: https://smallcultfollowing.com/babysteps/blog/2015/12/18/rayon-data-parallelism-in-rust/ [blog]: https://smallcultfollowing.com/babysteps/blog/2015/12/18/rayon-data-parallelism-in-rust/
[video]: https://www.youtube.com/watch?v=gof_OEv71Aw [video]: https://www.youtube.com/watch?v=gof_OEv71Aw
@@ -71,12 +71,12 @@ as:
```toml ```toml
[dependencies] [dependencies]
rayon = "1.5" rayon = "1.8"
``` ```
To use the Parallel Iterator APIs, a number of traits have to be in To use the parallel iterator APIs, a number of traits have to be in
scope. The easiest way to bring those things into scope is to use the scope. The easiest way to bring those things into scope is to use the
[Rayon prelude](https://docs.rs/rayon/*/rayon/prelude/index.html). In [Rayon prelude](https://docs.rs/rayon/*/rayon/prelude/index.html). In
each module where you would like to use the parallel iterator APIs, each module where you would like to use the parallel iterator APIs,
just add: just add:
@@ -84,26 +84,44 @@ just add:
use rayon::prelude::*; use rayon::prelude::*;
``` ```
Rayon currently requires `rustc 1.56.0` or greater. Rayon currently requires `rustc 1.63.0` or greater.
### Usage with WebAssembly ### Usage with WebAssembly
Rayon can work on the Web via WebAssembly, but requires an adapter By default, when building to WebAssembly, Rayon will treat it as any
and some project configuration to account for differences between other platform without multithreading support and will fall back to
WebAssembly threads and threads on the other platforms. sequential iteration. This allows existing code to compile and run
successfully with no changes necessary, but it will run slower as it
will only use a single CPU core.
Check out [wasm-bindgen-rayon](https://github.com/GoogleChromeLabs/wasm-bindgen-rayon) You can build Rayon-based projects with proper multithreading support
for the Web, but you'll need an adapter and some project configuration
to account for differences between WebAssembly threads and threads
on the other platforms.
Check out the
[wasm-bindgen-rayon](https://github.com/RReverser/wasm-bindgen-rayon)
docs for more details. docs for more details.
## Contribution ## Contribution
Rayon is an open source project! If you'd like to contribute to Rayon, check out [the list of "help wanted" issues](https://github.com/rayon-rs/rayon/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22). These are all (or should be) issues that are suitable for getting started, and they generally include a detailed set of instructions for what to do. Please ask questions if anything is unclear! Also, check out the [Guide to Development](https://github.com/rayon-rs/rayon/wiki/Guide-to-Development) page on the wiki. Note that all code submitted in PRs to Rayon is assumed to [be licensed under Rayon's dual MIT/Apache2 licensing](https://github.com/rayon-rs/rayon/blob/master/README.md#license). Rayon is an open source project! If you'd like to contribute to Rayon,
check out
[the list of "help wanted" issues](https://github.com/rayon-rs/rayon/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22).
These are all (or should be) issues that are suitable for getting
started, and they generally include a detailed set of instructions for
what to do. Please ask questions if anything is unclear! Also, check
out the
[Guide to Development](https://github.com/rayon-rs/rayon/wiki/Guide-to-Development)
page on the wiki. Note that all code submitted in PRs to Rayon is
assumed to
[be licensed under Rayon's dual MIT/Apache 2.0 licensing](https://github.com/rayon-rs/rayon/blob/main/README.md#license).
## Quick demo ## Quick demo
To see Rayon in action, check out the `rayon-demo` directory, which To see Rayon in action, check out the `rayon-demo` directory, which
includes a number of demos of code using Rayon. For example, run this includes a number of demos of code using Rayon. For example, run this
command to get a visualization of an nbody simulation. To see the command to get a visualization of an N-body simulation. To see the
effect of using Rayon, press `s` to run sequentially and `p` to run in effect of using Rayon, press `s` to run sequentially and `p` to run in
parallel. parallel.
@@ -123,7 +141,7 @@ For more information on demos, try:
See [the Rayon FAQ][faq]. See [the Rayon FAQ][faq].
[faq]: https://github.com/rayon-rs/rayon/blob/master/FAQ.md [faq]: https://github.com/rayon-rs/rayon/blob/main/FAQ.md
## License ## License

View File

@@ -1,3 +1,69 @@
# Release rayon 1.10.0 (2024-03-23)
- The new methods `ParallelSlice::par_chunk_by` and
`ParallelSliceMut::par_chunk_by_mut` work like the slice methods `chunk_by`
and `chunk_by_mut` added in Rust 1.77.
# Release rayon 1.9.0 (2024-02-27)
- The new methods `IndexedParallelIterator::by_exponential_blocks` and
`by_uniform_blocks` allow processing items in smaller groups at a time.
- The new `iter::walk_tree`, `walk_tree_prefix`, and `walk_tree_postfix`
functions enable custom parallel iteration over tree-like structures.
- The new method `ParallelIterator::collect_vec_list` returns items as a linked
list of vectors, which is an efficient mode of parallel collection used by
many of the internal implementations of `collect`.
- The new methods `ParallelSliceMut::par_split_inclusive_mut`,
`ParallelSlice::par_split_inclusive`, and
`ParallelString::par_split_inclusive` all work like a normal split but
keeping the separator as part of the left slice.
- The new `ParallelString::par_split_ascii_whitespace` splits only on ASCII
whitespace, which is faster than including Unicode multi-byte whitespace.
- `OsString` now implements `FromParallelIterator<_>` and `ParallelExtend<_>`
for a few item types similar to the standard `FromIterator` and `Extend`.
- The internal `Pattern` trait for string methods is now implemented for
`[char; N]` and `&[char; N]`, matching any of the given characters.
# Release rayon 1.8.1 / rayon-core 1.12.1 (2024-01-17)
- The new `"web_spin_lock"` crate feature makes mutexes spin on the main
browser thread in WebAssembly, rather than suffer an error about forbidden
`atomics.wait` if they were to block in that context. Thanks @RReverser!
# Release rayon 1.8.0 / rayon-core 1.12.0 (2023-09-20)
- The minimum supported `rustc` is now 1.63.
- Added `ThreadPoolBuilder::use_current_thread` to use the builder thread as
part of the new thread pool. That thread does not run the pool's main loop,
but it may participate in work-stealing if it yields to rayon in some way.
- Implemented `FromParallelIterator<T>` for `Box<[T]>`, `Rc<[T]>`, and
`Arc<[T]>`, as well as `FromParallelIterator<Box<str>>` and
`ParallelExtend<Box<str>>` for `String`.
- `ThreadPoolBuilder::build_scoped` now uses `std::thread::scope`.
- The default number of threads is now determined using
`std::thread::available_parallelism` instead of the `num_cpus` crate.
- The internal logging facility has been removed, reducing bloat for all users.
- Many smaller performance tweaks and documentation updates.
# Release rayon 1.7.0 / rayon-core 1.11.0 (2023-03-03)
- The minimum supported `rustc` is now 1.59.
- Added a fallback when threading is unsupported.
- The new `ParallelIterator::take_any` and `skip_any` methods work like
unordered `IndexedParallelIterator::take` and `skip`, counting items in
whatever order they are visited in parallel.
- The new `ParallelIterator::take_any_while` and `skip_any_while` methods work
like unordered `Iterator::take_while` and `skip_while`, which previously had
no parallel equivalent. The "while" condition may be satisfied from anywhere
in the parallel iterator, affecting all future items regardless of position.
- The new `yield_now` and `yield_local` functions will cooperatively yield
execution to Rayon, either trying to execute pending work from the entire
pool or from just the local deques of the current thread, respectively.
# Release rayon-core 1.10.2 (2023-01-22)
- Fixed miri-reported UB for SharedReadOnly tags protected by a call.
# Release rayon 1.6.1 (2022-12-09) # Release rayon 1.6.1 (2022-12-09)
- Simplified `par_bridge` to only pull one item at a time from the iterator, - Simplified `par_bridge` to only pull one item at a time from the iterator,
@@ -303,8 +369,8 @@ Thanks to all of the contributors for this release!
- @seanchen1991 - @seanchen1991
- @yegeun542 - @yegeun542
[RFC 1]: https://github.com/rayon-rs/rfcs/blob/master/accepted/rfc0001-scope-scheduling.md [RFC 1]: https://github.com/rayon-rs/rfcs/blob/main/accepted/rfc0001-scope-scheduling.md
[RFC 3]: https://github.com/rayon-rs/rfcs/blob/master/accepted/rfc0003-minimum-rustc.md [RFC 3]: https://github.com/rayon-rs/rfcs/blob/main/accepted/rfc0003-minimum-rustc.md
# Release rayon 1.0.3 (2018-11-02) # Release rayon 1.0.3 (2018-11-02)

View File

@@ -78,7 +78,8 @@ impl<T: Send, const N: usize> IndexedParallelIterator for IntoIter<T, N> {
unsafe { unsafe {
// Drain every item, and then the local array can just fall out of scope. // Drain every item, and then the local array can just fall out of scope.
let mut array = ManuallyDrop::new(self.array); let mut array = ManuallyDrop::new(self.array);
callback.callback(DrainProducer::new(&mut *array)) let producer = DrainProducer::new(array.as_mut_slice());
callback.callback(producer)
} }
} }
} }

View File

@@ -30,6 +30,8 @@ macro_rules! must_use {
} }
must_use! { must_use! {
by_exponential_blocks /** v.par_iter().by_exponential_blocks(); */
by_uniform_blocks /** v.par_iter().by_uniform_blocks(2); */
step_by /** v.par_iter().step_by(2); */ step_by /** v.par_iter().step_by(2); */
chain /** v.par_iter().chain(&v); */ chain /** v.par_iter().chain(&v); */
chunks /** v.par_iter().chunks(2); */ chunks /** v.par_iter().chunks(2); */

View File

@@ -0,0 +1,131 @@
use super::plumbing::*;
use super::*;
struct BlocksCallback<S, C> {
sizes: S,
consumer: C,
len: usize,
}
impl<T, S, C> ProducerCallback<T> for BlocksCallback<S, C>
where
C: UnindexedConsumer<T>,
S: Iterator<Item = usize>,
{
type Output = C::Result;
fn callback<P: Producer<Item = T>>(mut self, mut producer: P) -> Self::Output {
let mut remaining_len = self.len;
let mut consumer = self.consumer;
// we need a local variable for the accumulated results
// we call the reducer's identity by splitting at 0
let (left_consumer, right_consumer, _) = consumer.split_at(0);
let mut leftmost_res = left_consumer.into_folder().complete();
consumer = right_consumer;
// now we loop on each block size
while remaining_len > 0 && !consumer.full() {
// we compute the next block's size
let size = self.sizes.next().unwrap_or(std::usize::MAX);
let capped_size = remaining_len.min(size);
remaining_len -= capped_size;
// split the producer
let (left_producer, right_producer) = producer.split_at(capped_size);
producer = right_producer;
// split the consumer
let (left_consumer, right_consumer, _) = consumer.split_at(capped_size);
consumer = right_consumer;
leftmost_res = consumer.to_reducer().reduce(
leftmost_res,
bridge_producer_consumer(capped_size, left_producer, left_consumer),
);
}
leftmost_res
}
}
/// `ExponentialBlocks` is a parallel iterator that consumes itself as a sequence
/// of parallel blocks of increasing sizes (exponentially).
///
/// This struct is created by the [`by_exponential_blocks()`] method on [`IndexedParallelIterator`]
///
/// [`by_exponential_blocks()`]: trait.IndexedParallelIterator.html#method.by_exponential_blocks
/// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug, Clone)]
pub struct ExponentialBlocks<I> {
base: I,
}
impl<I> ExponentialBlocks<I> {
pub(super) fn new(base: I) -> Self {
Self { base }
}
}
impl<I> ParallelIterator for ExponentialBlocks<I>
where
I: IndexedParallelIterator,
{
type Item = I::Item;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let first = crate::current_num_threads();
let callback = BlocksCallback {
consumer,
sizes: std::iter::successors(Some(first), exponential_size),
len: self.base.len(),
};
self.base.with_producer(callback)
}
}
fn exponential_size(size: &usize) -> Option<usize> {
Some(size.saturating_mul(2))
}
/// `UniformBlocks` is a parallel iterator that consumes itself as a sequence
/// of parallel blocks of constant sizes.
///
/// This struct is created by the [`by_uniform_blocks()`] method on [`IndexedParallelIterator`]
///
/// [`by_uniform_blocks()`]: trait.IndexedParallelIterator.html#method.by_uniform_blocks
/// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug, Clone)]
pub struct UniformBlocks<I> {
base: I,
block_size: usize,
}
impl<I> UniformBlocks<I> {
pub(super) fn new(base: I, block_size: usize) -> Self {
Self { base, block_size }
}
}
impl<I> ParallelIterator for UniformBlocks<I>
where
I: IndexedParallelIterator,
{
type Item = I::Item;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let callback = BlocksCallback {
consumer,
sizes: std::iter::repeat(self.block_size),
len: self.base.len(),
};
self.base.with_producer(callback)
}
}

View File

@@ -1,7 +1,6 @@
use super::plumbing::*; use super::plumbing::*;
use super::*; use super::*;
use rayon_core::join; use rayon_core::join;
use std::cmp;
use std::iter; use std::iter;
/// `Chain` is an iterator that joins `b` after `a` in one continuous iterator. /// `Chain` is an iterator that joins `b` after `a` in one continuous iterator.
@@ -178,11 +177,11 @@ where
} }
fn min_len(&self) -> usize { fn min_len(&self) -> usize {
cmp::max(self.a.min_len(), self.b.min_len()) Ord::max(self.a.min_len(), self.b.min_len())
} }
fn max_len(&self) -> usize { fn max_len(&self) -> usize {
cmp::min(self.a.max_len(), self.b.max_len()) Ord::min(self.a.max_len(), self.b.max_len())
} }
fn split_at(self, index: usize) -> (Self, Self) { fn split_at(self, index: usize) -> (Self, Self) {

View File

@@ -1,5 +1,3 @@
use std::cmp::min;
use super::plumbing::*; use super::plumbing::*;
use super::*; use super::*;
use crate::math::div_round_up; use crate::math::div_round_up;
@@ -133,7 +131,7 @@ where
} }
fn split_at(self, index: usize) -> (Self, Self) { fn split_at(self, index: usize) -> (Self, Self) {
let elem_index = min(index * self.chunk_size, self.len); let elem_index = Ord::min(index * self.chunk_size, self.len);
let (left, right) = self.base.split_at(elem_index); let (left, right) = self.base.split_at(elem_index);
( (
ChunkProducer { ChunkProducer {

View File

@@ -76,6 +76,7 @@ fn right_produces_items_with_no_complete() {
// Complete is not called by the consumer. Hence,the collection vector is not fully initialized. // Complete is not called by the consumer. Hence,the collection vector is not fully initialized.
#[test] #[test]
#[cfg_attr(not(panic = "unwind"), ignore)]
fn produces_items_with_no_complete() { fn produces_items_with_no_complete() {
let counter = DropCounter::default(); let counter = DropCounter::default();
let mut v = vec![]; let mut v = vec![];
@@ -273,6 +274,7 @@ fn right_panics() {
// The left consumer produces fewer items while the right // The left consumer produces fewer items while the right
// consumer produces correct number; check that created elements are dropped // consumer produces correct number; check that created elements are dropped
#[test] #[test]
#[cfg_attr(not(panic = "unwind"), ignore)]
fn left_produces_fewer_items_drops() { fn left_produces_fewer_items_drops() {
let counter = DropCounter::default(); let counter = DropCounter::default();
let mut v = vec![]; let mut v = vec![];

View File

@@ -2,26 +2,83 @@ use super::noop::NoopConsumer;
use super::plumbing::{Consumer, Folder, Reducer, UnindexedConsumer}; use super::plumbing::{Consumer, Folder, Reducer, UnindexedConsumer};
use super::{IntoParallelIterator, ParallelExtend, ParallelIterator}; use super::{IntoParallelIterator, ParallelExtend, ParallelIterator};
use either::Either;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::LinkedList; use std::collections::LinkedList;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::collections::{BinaryHeap, VecDeque}; use std::collections::{BinaryHeap, VecDeque};
use std::ffi::{OsStr, OsString};
use std::hash::{BuildHasher, Hash}; use std::hash::{BuildHasher, Hash};
/// Performs a generic `par_extend` by collecting to a `LinkedList<Vec<_>>` in /// Performs a generic `par_extend` by collecting to a `LinkedList<Vec<_>>` in
/// parallel, then extending the collection sequentially. /// parallel, then extending the collection sequentially.
macro_rules! extend { macro_rules! extend {
($self:ident, $par_iter:ident, $extend:ident) => { ($self:ident, $par_iter:ident) => {
$extend( extend!($self <- fast_collect($par_iter))
$self, };
$par_iter.into_par_iter().drive_unindexed(ListVecConsumer), ($self:ident <- $vecs:expr) => {
); match $vecs {
Either::Left(vec) => $self.extend(vec),
Either::Right(list) => {
for vec in list {
$self.extend(vec);
}
}
}
};
}
macro_rules! extend_reserved {
($self:ident, $par_iter:ident, $len:ident) => {
let vecs = fast_collect($par_iter);
$self.reserve($len(&vecs));
extend!($self <- vecs)
};
($self:ident, $par_iter:ident) => {
extend_reserved!($self, $par_iter, len)
}; };
} }
/// Computes the total length of a `LinkedList<Vec<_>>`. /// Computes the total length of a `fast_collect` result.
fn len<T>(list: &LinkedList<Vec<T>>) -> usize { fn len<T>(vecs: &Either<Vec<T>, LinkedList<Vec<T>>>) -> usize {
list.iter().map(Vec::len).sum() match vecs {
Either::Left(vec) => vec.len(),
Either::Right(list) => list.iter().map(Vec::len).sum(),
}
}
/// Computes the total string length of a `fast_collect` result.
fn string_len<T: AsRef<str>>(vecs: &Either<Vec<T>, LinkedList<Vec<T>>>) -> usize {
let strs = match vecs {
Either::Left(vec) => Either::Left(vec.iter()),
Either::Right(list) => Either::Right(list.iter().flatten()),
};
strs.map(AsRef::as_ref).map(str::len).sum()
}
/// Computes the total OS-string length of a `fast_collect` result.
fn osstring_len<T: AsRef<OsStr>>(vecs: &Either<Vec<T>, LinkedList<Vec<T>>>) -> usize {
let osstrs = match vecs {
Either::Left(vec) => Either::Left(vec.iter()),
Either::Right(list) => Either::Right(list.iter().flatten()),
};
osstrs.map(AsRef::as_ref).map(OsStr::len).sum()
}
pub(super) fn fast_collect<I, T>(pi: I) -> Either<Vec<T>, LinkedList<Vec<T>>>
where
I: IntoParallelIterator<Item = T>,
T: Send,
{
let par_iter = pi.into_par_iter();
match par_iter.opt_len() {
Some(len) => {
// Pseudo-specialization. See impl of ParallelExtend for Vec for more details.
let mut vec = Vec::new();
super::collect::special_extend(par_iter, len, &mut vec);
Either::Left(vec)
}
None => Either::Right(par_iter.drive_unindexed(ListVecConsumer)),
}
} }
struct ListVecConsumer; struct ListVecConsumer;
@@ -87,16 +144,6 @@ impl<T> Folder<T> for ListVecFolder<T> {
} }
} }
fn heap_extend<T, Item>(heap: &mut BinaryHeap<T>, list: LinkedList<Vec<Item>>)
where
BinaryHeap<T>: Extend<Item>,
{
heap.reserve(len(&list));
for vec in list {
heap.extend(vec);
}
}
/// Extends a binary heap with items from a parallel iterator. /// Extends a binary heap with items from a parallel iterator.
impl<T> ParallelExtend<T> for BinaryHeap<T> impl<T> ParallelExtend<T> for BinaryHeap<T>
where where
@@ -106,7 +153,7 @@ where
where where
I: IntoParallelIterator<Item = T>, I: IntoParallelIterator<Item = T>,
{ {
extend!(self, par_iter, heap_extend); extend_reserved!(self, par_iter);
} }
} }
@@ -119,16 +166,7 @@ where
where where
I: IntoParallelIterator<Item = &'a T>, I: IntoParallelIterator<Item = &'a T>,
{ {
extend!(self, par_iter, heap_extend); extend_reserved!(self, par_iter);
}
}
fn btree_map_extend<K, V, Item>(map: &mut BTreeMap<K, V>, list: LinkedList<Vec<Item>>)
where
BTreeMap<K, V>: Extend<Item>,
{
for vec in list {
map.extend(vec);
} }
} }
@@ -142,7 +180,7 @@ where
where where
I: IntoParallelIterator<Item = (K, V)>, I: IntoParallelIterator<Item = (K, V)>,
{ {
extend!(self, par_iter, btree_map_extend); extend!(self, par_iter);
} }
} }
@@ -156,16 +194,7 @@ where
where where
I: IntoParallelIterator<Item = (&'a K, &'a V)>, I: IntoParallelIterator<Item = (&'a K, &'a V)>,
{ {
extend!(self, par_iter, btree_map_extend); extend!(self, par_iter);
}
}
fn btree_set_extend<T, Item>(set: &mut BTreeSet<T>, list: LinkedList<Vec<Item>>)
where
BTreeSet<T>: Extend<Item>,
{
for vec in list {
set.extend(vec);
} }
} }
@@ -178,7 +207,7 @@ where
where where
I: IntoParallelIterator<Item = T>, I: IntoParallelIterator<Item = T>,
{ {
extend!(self, par_iter, btree_set_extend); extend!(self, par_iter);
} }
} }
@@ -191,19 +220,7 @@ where
where where
I: IntoParallelIterator<Item = &'a T>, I: IntoParallelIterator<Item = &'a T>,
{ {
extend!(self, par_iter, btree_set_extend); extend!(self, par_iter);
}
}
fn hash_map_extend<K, V, S, Item>(map: &mut HashMap<K, V, S>, list: LinkedList<Vec<Item>>)
where
HashMap<K, V, S>: Extend<Item>,
K: Eq + Hash,
S: BuildHasher,
{
map.reserve(len(&list));
for vec in list {
map.extend(vec);
} }
} }
@@ -219,7 +236,7 @@ where
I: IntoParallelIterator<Item = (K, V)>, I: IntoParallelIterator<Item = (K, V)>,
{ {
// See the map_collect benchmarks in rayon-demo for different strategies. // See the map_collect benchmarks in rayon-demo for different strategies.
extend!(self, par_iter, hash_map_extend); extend_reserved!(self, par_iter);
} }
} }
@@ -234,19 +251,7 @@ where
where where
I: IntoParallelIterator<Item = (&'a K, &'a V)>, I: IntoParallelIterator<Item = (&'a K, &'a V)>,
{ {
extend!(self, par_iter, hash_map_extend); extend_reserved!(self, par_iter);
}
}
fn hash_set_extend<T, S, Item>(set: &mut HashSet<T, S>, list: LinkedList<Vec<Item>>)
where
HashSet<T, S>: Extend<Item>,
T: Eq + Hash,
S: BuildHasher,
{
set.reserve(len(&list));
for vec in list {
set.extend(vec);
} }
} }
@@ -260,7 +265,7 @@ where
where where
I: IntoParallelIterator<Item = T>, I: IntoParallelIterator<Item = T>,
{ {
extend!(self, par_iter, hash_set_extend); extend_reserved!(self, par_iter);
} }
} }
@@ -274,7 +279,7 @@ where
where where
I: IntoParallelIterator<Item = &'a T>, I: IntoParallelIterator<Item = &'a T>,
{ {
extend!(self, par_iter, hash_set_extend); extend_reserved!(self, par_iter);
} }
} }
@@ -375,9 +380,34 @@ impl<T> Reducer<LinkedList<T>> for ListReducer {
} }
} }
fn flat_string_extend(string: &mut String, list: LinkedList<String>) { /// Extends an OS-string with string slices from a parallel iterator.
string.reserve(list.iter().map(String::len).sum()); impl<'a> ParallelExtend<&'a OsStr> for OsString {
string.extend(list); fn par_extend<I>(&mut self, par_iter: I)
where
I: IntoParallelIterator<Item = &'a OsStr>,
{
extend_reserved!(self, par_iter, osstring_len);
}
}
/// Extends an OS-string with strings from a parallel iterator.
impl ParallelExtend<OsString> for OsString {
fn par_extend<I>(&mut self, par_iter: I)
where
I: IntoParallelIterator<Item = OsString>,
{
extend_reserved!(self, par_iter, osstring_len);
}
}
/// Extends an OS-string with string slices from a parallel iterator.
impl<'a> ParallelExtend<Cow<'a, OsStr>> for OsString {
fn par_extend<I>(&mut self, par_iter: I)
where
I: IntoParallelIterator<Item = Cow<'a, OsStr>>,
{
extend_reserved!(self, par_iter, osstring_len);
}
} }
/// Extends a string with characters from a parallel iterator. /// Extends a string with characters from a parallel iterator.
@@ -389,7 +419,8 @@ impl ParallelExtend<char> for String {
// This is like `extend`, but `Vec<char>` is less efficient to deal // This is like `extend`, but `Vec<char>` is less efficient to deal
// with than `String`, so instead collect to `LinkedList<String>`. // with than `String`, so instead collect to `LinkedList<String>`.
let list = par_iter.into_par_iter().drive_unindexed(ListStringConsumer); let list = par_iter.into_par_iter().drive_unindexed(ListStringConsumer);
flat_string_extend(self, list); self.reserve(list.iter().map(String::len).sum());
self.extend(list);
} }
} }
@@ -468,25 +499,13 @@ impl Folder<char> for ListStringFolder {
} }
} }
fn string_extend<Item>(string: &mut String, list: LinkedList<Vec<Item>>)
where
String: Extend<Item>,
Item: AsRef<str>,
{
let len = list.iter().flatten().map(Item::as_ref).map(str::len).sum();
string.reserve(len);
for vec in list {
string.extend(vec);
}
}
/// Extends a string with string slices from a parallel iterator. /// Extends a string with string slices from a parallel iterator.
impl<'a> ParallelExtend<&'a str> for String { impl<'a> ParallelExtend<&'a str> for String {
fn par_extend<I>(&mut self, par_iter: I) fn par_extend<I>(&mut self, par_iter: I)
where where
I: IntoParallelIterator<Item = &'a str>, I: IntoParallelIterator<Item = &'a str>,
{ {
extend!(self, par_iter, string_extend); extend_reserved!(self, par_iter, string_len);
} }
} }
@@ -496,7 +515,17 @@ impl ParallelExtend<String> for String {
where where
I: IntoParallelIterator<Item = String>, I: IntoParallelIterator<Item = String>,
{ {
extend!(self, par_iter, string_extend); extend_reserved!(self, par_iter, string_len);
}
}
/// Extends a string with boxed strings from a parallel iterator.
impl ParallelExtend<Box<str>> for String {
fn par_extend<I>(&mut self, par_iter: I)
where
I: IntoParallelIterator<Item = Box<str>>,
{
extend_reserved!(self, par_iter, string_len);
} }
} }
@@ -506,17 +535,7 @@ impl<'a> ParallelExtend<Cow<'a, str>> for String {
where where
I: IntoParallelIterator<Item = Cow<'a, str>>, I: IntoParallelIterator<Item = Cow<'a, str>>,
{ {
extend!(self, par_iter, string_extend); extend_reserved!(self, par_iter, string_len);
}
}
fn deque_extend<T, Item>(deque: &mut VecDeque<T>, list: LinkedList<Vec<Item>>)
where
VecDeque<T>: Extend<Item>,
{
deque.reserve(len(&list));
for vec in list {
deque.extend(vec);
} }
} }
@@ -529,7 +548,7 @@ where
where where
I: IntoParallelIterator<Item = T>, I: IntoParallelIterator<Item = T>,
{ {
extend!(self, par_iter, deque_extend); extend_reserved!(self, par_iter);
} }
} }
@@ -542,14 +561,7 @@ where
where where
I: IntoParallelIterator<Item = &'a T>, I: IntoParallelIterator<Item = &'a T>,
{ {
extend!(self, par_iter, deque_extend); extend_reserved!(self, par_iter);
}
}
fn vec_append<T>(vec: &mut Vec<T>, list: LinkedList<Vec<T>>) {
vec.reserve(len(&list));
for mut other in list {
vec.append(&mut other);
} }
} }
@@ -574,7 +586,10 @@ where
None => { None => {
// This works like `extend`, but `Vec::append` is more efficient. // This works like `extend`, but `Vec::append` is more efficient.
let list = par_iter.drive_unindexed(ListVecConsumer); let list = par_iter.drive_unindexed(ListVecConsumer);
vec_append(self, list); self.reserve(list.iter().map(Vec::len).sum());
for mut other in list {
self.append(&mut other);
}
} }
} }
} }

View File

@@ -181,25 +181,17 @@ impl<'p, P: 'p + Fn(&T) -> bool, T> Folder<T> for FindFolder<'p, T, P> {
}; };
if !found_best_in_range && (self.find_op)(&item) { if !found_best_in_range && (self.find_op)(&item) {
// Continuously try to set best_found until we succeed or we // Update the best found index if ours is better.
// discover a better match was already found. let update =
let mut current = self.best_found.load(Ordering::Relaxed); self.best_found
loop { .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
if better_position(current, self.boundary, self.match_position) { better_position(self.boundary, current, self.match_position)
break; .then_some(self.boundary)
} });
match self.best_found.compare_exchange_weak(
current, // Save this item if our index was better or equal.
self.boundary, if update.is_ok() || update == Err(self.boundary) {
Ordering::Relaxed, self.item = Some(item);
Ordering::Relaxed,
) {
Ok(_) => {
self.item = Some(item);
break;
}
Err(v) => current = v,
}
} }
} }
self self

View File

@@ -1,5 +1,4 @@
use super::*; use super::*;
use std::sync::atomic::AtomicUsize;
#[test] #[test]
fn same_range_first_consumers_return_correct_answer() { fn same_range_first_consumers_return_correct_answer() {

View File

@@ -201,16 +201,16 @@ mod test {
assert_eq!(4, (0..8).into_par_iter().fold_chunks(2, id, sum).len()); assert_eq!(4, (0..8).into_par_iter().fold_chunks(2, id, sum).len());
assert_eq!(3, (0..9).into_par_iter().fold_chunks(3, id, sum).len()); assert_eq!(3, (0..9).into_par_iter().fold_chunks(3, id, sum).len());
assert_eq!(3, (0..8).into_par_iter().fold_chunks(3, id, sum).len()); assert_eq!(3, (0..8).into_par_iter().fold_chunks(3, id, sum).len());
assert_eq!(1, (&[1]).par_iter().fold_chunks(3, id, sum).len()); assert_eq!(1, [1].par_iter().fold_chunks(3, id, sum).len());
assert_eq!(0, (0..0).into_par_iter().fold_chunks(3, id, sum).len()); assert_eq!(0, (0..0).into_par_iter().fold_chunks(3, id, sum).len());
} }
#[test] #[test]
fn check_fold_chunks_uneven() { fn check_fold_chunks_uneven() {
let cases: Vec<(Vec<u32>, usize, Vec<u32>)> = vec![ let cases: Vec<(Vec<u32>, usize, Vec<u32>)> = vec![
((0..5).collect(), 3, vec![0 + 1 + 2, 3 + 4]), ((0..5).collect(), 3, vec![1 + 2, 3 + 4]),
(vec![1], 5, vec![1]), (vec![1], 5, vec![1]),
((0..4).collect(), 3, vec![0 + 1 + 2, 3]), ((0..4).collect(), 3, vec![1 + 2, 3]),
]; ];
for (i, (v, n, expected)) in cases.into_iter().enumerate() { for (i, (v, n, expected)) in cases.into_iter().enumerate() {

View File

@@ -196,16 +196,16 @@ mod test {
assert_eq!(4, (0..8).into_par_iter().fold_chunks_with(2, 0, sum).len()); assert_eq!(4, (0..8).into_par_iter().fold_chunks_with(2, 0, sum).len());
assert_eq!(3, (0..9).into_par_iter().fold_chunks_with(3, 0, sum).len()); assert_eq!(3, (0..9).into_par_iter().fold_chunks_with(3, 0, sum).len());
assert_eq!(3, (0..8).into_par_iter().fold_chunks_with(3, 0, sum).len()); assert_eq!(3, (0..8).into_par_iter().fold_chunks_with(3, 0, sum).len());
assert_eq!(1, (&[1]).par_iter().fold_chunks_with(3, 0, sum).len()); assert_eq!(1, [1].par_iter().fold_chunks_with(3, 0, sum).len());
assert_eq!(0, (0..0).into_par_iter().fold_chunks_with(3, 0, sum).len()); assert_eq!(0, (0..0).into_par_iter().fold_chunks_with(3, 0, sum).len());
} }
#[test] #[test]
fn check_fold_chunks_uneven() { fn check_fold_chunks_uneven() {
let cases: Vec<(Vec<u32>, usize, Vec<u32>)> = vec![ let cases: Vec<(Vec<u32>, usize, Vec<u32>)> = vec![
((0..5).collect(), 3, vec![0 + 1 + 2, 3 + 4]), ((0..5).collect(), 3, vec![1 + 2, 3 + 4]),
(vec![1], 5, vec![1]), (vec![1], 5, vec![1]),
((0..4).collect(), 3, vec![0 + 1 + 2, 3]), ((0..4).collect(), 3, vec![1 + 2, 3]),
]; ];
for (i, (v, n, expected)) in cases.into_iter().enumerate() { for (i, (v, n, expected)) in cases.into_iter().enumerate() {

View File

@@ -5,7 +5,10 @@ use std::borrow::Cow;
use std::collections::LinkedList; use std::collections::LinkedList;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::collections::{BinaryHeap, VecDeque}; use std::collections::{BinaryHeap, VecDeque};
use std::ffi::{OsStr, OsString};
use std::hash::{BuildHasher, Hash}; use std::hash::{BuildHasher, Hash};
use std::rc::Rc;
use std::sync::Arc;
/// Creates an empty default collection and extends it. /// Creates an empty default collection and extends it.
fn collect_extended<C, I>(par_iter: I) -> C fn collect_extended<C, I>(par_iter: I) -> C
@@ -31,6 +34,45 @@ where
} }
} }
/// Collects items from a parallel iterator into a boxed slice.
impl<T> FromParallelIterator<T> for Box<[T]>
where
T: Send,
{
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = T>,
{
Vec::from_par_iter(par_iter).into()
}
}
/// Collects items from a parallel iterator into a reference-counted slice.
impl<T> FromParallelIterator<T> for Rc<[T]>
where
T: Send,
{
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = T>,
{
Vec::from_par_iter(par_iter).into()
}
}
/// Collects items from a parallel iterator into an atomically-reference-counted slice.
impl<T> FromParallelIterator<T> for Arc<[T]>
where
T: Send,
{
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = T>,
{
Vec::from_par_iter(par_iter).into()
}
}
/// Collects items from a parallel iterator into a vecdeque. /// Collects items from a parallel iterator into a vecdeque.
impl<T> FromParallelIterator<T> for VecDeque<T> impl<T> FromParallelIterator<T> for VecDeque<T>
where where
@@ -174,6 +216,16 @@ impl FromParallelIterator<String> for String {
} }
} }
/// Collects boxed strings from a parallel iterator into one large string.
impl FromParallelIterator<Box<str>> for String {
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = Box<str>>,
{
collect_extended(par_iter)
}
}
/// Collects string slices from a parallel iterator into a string. /// Collects string slices from a parallel iterator into a string.
impl<'a> FromParallelIterator<Cow<'a, str>> for String { impl<'a> FromParallelIterator<Cow<'a, str>> for String {
fn from_par_iter<I>(par_iter: I) -> Self fn from_par_iter<I>(par_iter: I) -> Self
@@ -184,6 +236,36 @@ impl<'a> FromParallelIterator<Cow<'a, str>> for String {
} }
} }
/// Collects OS-string slices from a parallel iterator into an OS-string.
impl<'a> FromParallelIterator<&'a OsStr> for OsString {
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = &'a OsStr>,
{
collect_extended(par_iter)
}
}
/// Collects OS-strings from a parallel iterator into one large OS-string.
impl FromParallelIterator<OsString> for OsString {
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = OsString>,
{
collect_extended(par_iter)
}
}
/// Collects OS-string slices from a parallel iterator into an OS-string.
impl<'a> FromParallelIterator<Cow<'a, OsStr>> for OsString {
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = Cow<'a, OsStr>>,
{
collect_extended(par_iter)
}
}
/// Collects an arbitrary `Cow` collection. /// Collects an arbitrary `Cow` collection.
/// ///
/// Note, the standard library only has `FromIterator` for `Cow<'a, str>` and /// Note, the standard library only has `FromIterator` for `Cow<'a, str>` and

View File

@@ -1,6 +1,5 @@
use super::plumbing::*; use super::plumbing::*;
use super::*; use super::*;
use std::cmp;
use std::iter::Fuse; use std::iter::Fuse;
/// `Interleave` is an iterator that interleaves elements of iterators /// `Interleave` is an iterator that interleaves elements of iterators
@@ -185,11 +184,11 @@ where
} }
fn min_len(&self) -> usize { fn min_len(&self) -> usize {
cmp::max(self.i.min_len(), self.j.min_len()) Ord::max(self.i.min_len(), self.j.min_len())
} }
fn max_len(&self) -> usize { fn max_len(&self) -> usize {
cmp::min(self.i.max_len(), self.j.max_len()) Ord::min(self.i.max_len(), self.j.max_len())
} }
/// We know 0 < index <= self.i_len + self.j_len /// We know 0 < index <= self.i_len + self.j_len

View File

@@ -1,6 +1,5 @@
use super::plumbing::*; use super::plumbing::*;
use super::*; use super::*;
use std::cmp;
/// `MinLen` is an iterator that imposes a minimum length on iterator splits. /// `MinLen` is an iterator that imposes a minimum length on iterator splits.
/// This struct is created by the [`with_min_len()`] method on [`IndexedParallelIterator`] /// This struct is created by the [`with_min_len()`] method on [`IndexedParallelIterator`]
@@ -107,7 +106,7 @@ where
} }
fn min_len(&self) -> usize { fn min_len(&self) -> usize {
cmp::max(self.min, self.base.min_len()) Ord::max(self.min, self.base.min_len())
} }
fn max_len(&self) -> usize { fn max_len(&self) -> usize {
@@ -245,7 +244,7 @@ where
} }
fn max_len(&self) -> usize { fn max_len(&self) -> usize {
cmp::min(self.max, self.base.max_len()) Ord::min(self.max, self.base.max_len())
} }
fn split_at(self, index: usize) -> (Self, Self) { fn split_at(self, index: usize) -> (Self, Self) {

View File

@@ -82,7 +82,8 @@
use self::plumbing::*; use self::plumbing::*;
use self::private::Try; use self::private::Try;
pub use either::Either; pub use either::Either;
use std::cmp::{self, Ordering}; use std::cmp::Ordering;
use std::collections::LinkedList;
use std::iter::{Product, Sum}; use std::iter::{Product, Sum};
use std::ops::{Fn, RangeBounds}; use std::ops::{Fn, RangeBounds};
@@ -102,6 +103,7 @@ mod test;
// e.g. `find::find()`, are always used **prefixed**, so that they // e.g. `find::find()`, are always used **prefixed**, so that they
// can be readily distinguished. // can be readily distinguished.
mod blocks;
mod chain; mod chain;
mod chunks; mod chunks;
mod cloned; mod cloned;
@@ -141,20 +143,26 @@ mod reduce;
mod repeat; mod repeat;
mod rev; mod rev;
mod skip; mod skip;
mod skip_any;
mod skip_any_while;
mod splitter; mod splitter;
mod step_by; mod step_by;
mod sum; mod sum;
mod take; mod take;
mod take_any;
mod take_any_while;
mod try_fold; mod try_fold;
mod try_reduce; mod try_reduce;
mod try_reduce_with; mod try_reduce_with;
mod unzip; mod unzip;
mod update; mod update;
mod walk_tree;
mod while_some; mod while_some;
mod zip; mod zip;
mod zip_eq; mod zip_eq;
pub use self::{ pub use self::{
blocks::{ExponentialBlocks, UniformBlocks},
chain::Chain, chain::Chain,
chunks::Chunks, chunks::Chunks,
cloned::Cloned, cloned::Cloned,
@@ -185,11 +193,18 @@ pub use self::{
repeat::{repeat, repeatn, Repeat, RepeatN}, repeat::{repeat, repeatn, Repeat, RepeatN},
rev::Rev, rev::Rev,
skip::Skip, skip::Skip,
skip_any::SkipAny,
skip_any_while::SkipAnyWhile,
splitter::{split, Split}, splitter::{split, Split},
step_by::StepBy, step_by::StepBy,
take::Take, take::Take,
take_any::TakeAny,
take_any_while::TakeAnyWhile,
try_fold::{TryFold, TryFoldWith}, try_fold::{TryFold, TryFoldWith},
update::Update, update::Update,
walk_tree::{
walk_tree, walk_tree_postfix, walk_tree_prefix, WalkTree, WalkTreePostfix, WalkTreePrefix,
},
while_some::WhileSome, while_some::WhileSome,
zip::Zip, zip::Zip,
zip_eq::ZipEq, zip_eq::ZipEq,
@@ -1417,7 +1432,7 @@ pub trait ParallelIterator: Sized + Send {
/// specified, so if the `Ord` impl is not truly associative, then /// specified, so if the `Ord` impl is not truly associative, then
/// the results are not deterministic. /// the results are not deterministic.
/// ///
/// Basically equivalent to `self.reduce_with(|a, b| cmp::min(a, b))`. /// Basically equivalent to `self.reduce_with(|a, b| Ord::min(a, b))`.
/// ///
/// # Examples /// # Examples
/// ///
@@ -1436,7 +1451,7 @@ pub trait ParallelIterator: Sized + Send {
where where
Self::Item: Ord, Self::Item: Ord,
{ {
self.reduce_with(cmp::min) self.reduce_with(Ord::min)
} }
/// Computes the minimum of all the items in the iterator with respect to /// Computes the minimum of all the items in the iterator with respect to
@@ -1515,7 +1530,7 @@ pub trait ParallelIterator: Sized + Send {
/// specified, so if the `Ord` impl is not truly associative, then /// specified, so if the `Ord` impl is not truly associative, then
/// the results are not deterministic. /// the results are not deterministic.
/// ///
/// Basically equivalent to `self.reduce_with(|a, b| cmp::max(a, b))`. /// Basically equivalent to `self.reduce_with(|a, b| Ord::max(a, b))`.
/// ///
/// # Examples /// # Examples
/// ///
@@ -1534,12 +1549,12 @@ pub trait ParallelIterator: Sized + Send {
where where
Self::Item: Ord, Self::Item: Ord,
{ {
self.reduce_with(cmp::max) self.reduce_with(Ord::max)
} }
/// Computes the maximum of all the items in the iterator with respect to /// Computes the maximum of all the items in the iterator with respect to
/// the given comparison function. If the iterator is empty, `None` is /// the given comparison function. If the iterator is empty, `None` is
/// returned; otherwise, `Some(min)` is returned. /// returned; otherwise, `Some(max)` is returned.
/// ///
/// Note that the order in which the items will be reduced is not /// Note that the order in which the items will be reduced is not
/// specified, so if the comparison function is not associative, then /// specified, so if the comparison function is not associative, then
@@ -1665,6 +1680,9 @@ pub trait ParallelIterator: Sized + Send {
/// will be stopped, while attempts to the left must continue in case /// will be stopped, while attempts to the left must continue in case
/// an earlier match is found. /// an earlier match is found.
/// ///
/// For added performance, you might consider using `find_first` in conjunction with
/// [`by_exponential_blocks()`][IndexedParallelIterator::by_exponential_blocks].
///
/// Note that not all parallel iterators have a useful order, much like /// Note that not all parallel iterators have a useful order, much like
/// sequential `HashMap` iteration, so "first" may be nebulous. If you /// sequential `HashMap` iteration, so "first" may be nebulous. If you
/// just want the first match that discovered anywhere in the iterator, /// just want the first match that discovered anywhere in the iterator,
@@ -1953,6 +1971,9 @@ pub trait ParallelIterator: Sized + Send {
/// of how many elements the iterator contains, and even allows you to reuse /// of how many elements the iterator contains, and even allows you to reuse
/// an existing vector's backing store rather than allocating a fresh vector. /// an existing vector's backing store rather than allocating a fresh vector.
/// ///
/// See also [`collect_vec_list()`][Self::collect_vec_list] for collecting
/// into a `LinkedList<Vec<T>>`.
///
/// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html /// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html
/// [`collect_into_vec()`]: /// [`collect_into_vec()`]:
/// trait.IndexedParallelIterator.html#method.collect_into_vec /// trait.IndexedParallelIterator.html#method.collect_into_vec
@@ -2194,6 +2215,188 @@ pub trait ParallelIterator: Sized + Send {
Intersperse::new(self, element) Intersperse::new(self, element)
} }
/// Creates an iterator that yields `n` elements from *anywhere* in the original iterator.
///
/// This is similar to [`IndexedParallelIterator::take`] without being
/// constrained to the "first" `n` of the original iterator order. The
/// taken items will still maintain their relative order where that is
/// visible in `collect`, `reduce`, and similar outputs.
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
///
/// let result: Vec<_> = (0..100)
/// .into_par_iter()
/// .filter(|&x| x % 2 == 0)
/// .take_any(5)
/// .collect();
///
/// assert_eq!(result.len(), 5);
/// assert!(result.windows(2).all(|w| w[0] < w[1]));
/// ```
fn take_any(self, n: usize) -> TakeAny<Self> {
TakeAny::new(self, n)
}
/// Creates an iterator that skips `n` elements from *anywhere* in the original iterator.
///
/// This is similar to [`IndexedParallelIterator::skip`] without being
/// constrained to the "first" `n` of the original iterator order. The
/// remaining items will still maintain their relative order where that is
/// visible in `collect`, `reduce`, and similar outputs.
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
///
/// let result: Vec<_> = (0..100)
/// .into_par_iter()
/// .filter(|&x| x % 2 == 0)
/// .skip_any(5)
/// .collect();
///
/// assert_eq!(result.len(), 45);
/// assert!(result.windows(2).all(|w| w[0] < w[1]));
/// ```
fn skip_any(self, n: usize) -> SkipAny<Self> {
SkipAny::new(self, n)
}
/// Creates an iterator that takes elements from *anywhere* in the original iterator
/// until the given `predicate` returns `false`.
///
/// The `predicate` may be anything -- e.g. it could be checking a fact about the item, a
/// global condition unrelated to the item itself, or some combination thereof.
///
/// If parallel calls to the `predicate` race and give different results, then the
/// `true` results will still take those particular items, while respecting the `false`
/// result from elsewhere to skip any further items.
///
/// This is similar to [`Iterator::take_while`] without being constrained to the original
/// iterator order. The taken items will still maintain their relative order where that is
/// visible in `collect`, `reduce`, and similar outputs.
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
///
/// let result: Vec<_> = (0..100)
/// .into_par_iter()
/// .take_any_while(|x| *x < 50)
/// .collect();
///
/// assert!(result.len() <= 50);
/// assert!(result.windows(2).all(|w| w[0] < w[1]));
/// ```
///
/// ```
/// use rayon::prelude::*;
/// use std::sync::atomic::AtomicUsize;
/// use std::sync::atomic::Ordering::Relaxed;
///
/// // Collect any group of items that sum <= 1000
/// let quota = AtomicUsize::new(1000);
/// let result: Vec<_> = (0_usize..100)
/// .into_par_iter()
/// .take_any_while(|&x| {
/// quota.fetch_update(Relaxed, Relaxed, |q| q.checked_sub(x))
/// .is_ok()
/// })
/// .collect();
///
/// let sum = result.iter().sum::<usize>();
/// assert!(matches!(sum, 902..=1000));
/// ```
fn take_any_while<P>(self, predicate: P) -> TakeAnyWhile<Self, P>
where
P: Fn(&Self::Item) -> bool + Sync + Send,
{
TakeAnyWhile::new(self, predicate)
}
/// Creates an iterator that skips elements from *anywhere* in the original iterator
/// until the given `predicate` returns `false`.
///
/// The `predicate` may be anything -- e.g. it could be checking a fact about the item, a
/// global condition unrelated to the item itself, or some combination thereof.
///
/// If parallel calls to the `predicate` race and give different results, then the
/// `true` results will still skip those particular items, while respecting the `false`
/// result from elsewhere to skip any further items.
///
/// This is similar to [`Iterator::skip_while`] without being constrained to the original
/// iterator order. The remaining items will still maintain their relative order where that is
/// visible in `collect`, `reduce`, and similar outputs.
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
///
/// let result: Vec<_> = (0..100)
/// .into_par_iter()
/// .skip_any_while(|x| *x < 50)
/// .collect();
///
/// assert!(result.len() >= 50);
/// assert!(result.windows(2).all(|w| w[0] < w[1]));
/// ```
fn skip_any_while<P>(self, predicate: P) -> SkipAnyWhile<Self, P>
where
P: Fn(&Self::Item) -> bool + Sync + Send,
{
SkipAnyWhile::new(self, predicate)
}
/// Collects this iterator into a linked list of vectors.
///
/// This is useful when you need to condense a parallel iterator into a collection,
/// but have no specific requirements for what that collection should be. If you
/// plan to store the collection longer-term, `Vec<T>` is, as always, likely the
/// best default choice, despite the overhead that comes from concatenating each
/// vector. Or, if this is an `IndexedParallelIterator`, you should also prefer to
/// just collect to a `Vec<T>`.
///
/// Internally, most [`FromParallelIterator`]/[`ParallelExtend`] implementations
/// use this strategy; each job collecting their chunk of the iterator to a `Vec<T>`
/// and those chunks getting merged into a `LinkedList`, before then extending the
/// collection with each vector. This is a very efficient way to collect an
/// unindexed parallel iterator, without much intermediate data movement.
///
/// # Examples
///
/// ```
/// # use std::collections::LinkedList;
/// use rayon::prelude::*;
///
/// let result: LinkedList<Vec<_>> = (0..=100)
/// .into_par_iter()
/// .filter(|x| x % 2 == 0)
/// .flat_map(|x| 0..x)
/// .collect_vec_list();
///
/// // `par_iter.collect_vec_list().into_iter().flatten()` turns
/// // a parallel iterator into a serial one
/// let total_len = result.into_iter().flatten().count();
/// assert_eq!(total_len, 2550);
/// ```
fn collect_vec_list(self) -> LinkedList<Vec<Self::Item>> {
match extend::fast_collect(self) {
Either::Left(vec) => {
let mut list = LinkedList::new();
if !vec.is_empty() {
list.push_back(vec);
}
list
}
Either::Right(list) => list,
}
}
/// Internal method used to define the behavior of this parallel /// Internal method used to define the behavior of this parallel
/// iterator. You should not need to call this directly. /// iterator. You should not need to call this directly.
/// ///
@@ -2205,7 +2408,7 @@ pub trait ParallelIterator: Sized + Send {
/// See the [README] for more details on the internals of parallel /// See the [README] for more details on the internals of parallel
/// iterators. /// iterators.
/// ///
/// [README]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md /// [README]: https://github.com/rayon-rs/rayon/blob/main/src/iter/plumbing/README.md
fn drive_unindexed<C>(self, consumer: C) -> C::Result fn drive_unindexed<C>(self, consumer: C) -> C::Result
where where
C: UnindexedConsumer<Self::Item>; C: UnindexedConsumer<Self::Item>;
@@ -2246,8 +2449,72 @@ impl<T: ParallelIterator> IntoParallelIterator for T {
// Waiting for `ExactSizeIterator::is_empty` to be stabilized. See rust-lang/rust#35428 // Waiting for `ExactSizeIterator::is_empty` to be stabilized. See rust-lang/rust#35428
#[allow(clippy::len_without_is_empty)] #[allow(clippy::len_without_is_empty)]
pub trait IndexedParallelIterator: ParallelIterator { pub trait IndexedParallelIterator: ParallelIterator {
/// Divides an iterator into sequential blocks of exponentially-increasing size.
///
/// Normally, parallel iterators are recursively divided into tasks in parallel.
/// This adaptor changes the default behavior by splitting the iterator into a **sequence**
/// of parallel iterators of increasing sizes.
/// Sizes grow exponentially in order to avoid creating
/// too many blocks. This also allows to balance the current block with all previous ones.
///
/// This can have many applications but the most notable ones are:
/// - better performance with [`find_first()`][ParallelIterator::find_first]
/// - more predictable performance with [`find_any()`][ParallelIterator::find_any]
/// or any interruptible computation
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
/// assert_eq!((0..10_000).into_par_iter()
/// .by_exponential_blocks()
/// .find_first(|&e| e==4_999), Some(4_999))
/// ```
///
/// In this example, without blocks, rayon will split the initial range into two but all work
/// on the right hand side (from 5,000 onwards) is **useless** since the sequential algorithm
/// never goes there. This means that if two threads are used there will be **no** speedup **at
/// all**.
///
/// `by_exponential_blocks` on the other hand will start with the leftmost range from 0
/// to `p` (threads number), continue with p to 3p, the 3p to 7p...
///
/// Each subrange is treated in parallel, while all subranges are treated sequentially.
/// We therefore ensure a logarithmic number of blocks (and overhead) while guaranteeing
/// we stop at the first block containing the searched data.
fn by_exponential_blocks(self) -> ExponentialBlocks<Self> {
ExponentialBlocks::new(self)
}
/// Divides an iterator into sequential blocks of the given size.
///
/// Normally, parallel iterators are recursively divided into tasks in parallel.
/// This adaptor changes the default behavior by splitting the iterator into a **sequence**
/// of parallel iterators of given `block_size`.
/// The main application is to obtain better
/// memory locality (especially if the reduce operation re-use folded data).
///
/// **Panics** if `block_size` is 0.
///
/// # Example
/// ```
/// use rayon::prelude::*;
/// // during most reductions v1 and v2 fit the cache
/// let v = (0u32..10_000_000)
/// .into_par_iter()
/// .by_uniform_blocks(1_000_000)
/// .fold(Vec::new, |mut v, e| { v.push(e); v})
/// .reduce(Vec::new, |mut v1, mut v2| { v1.append(&mut v2); v1});
/// assert_eq!(v, (0u32..10_000_000).collect::<Vec<u32>>());
/// ```
#[track_caller]
fn by_uniform_blocks(self, block_size: usize) -> UniformBlocks<Self> {
assert!(block_size != 0, "block_size must not be zero");
UniformBlocks::new(self, block_size)
}
/// Collects the results of the iterator into the specified /// Collects the results of the iterator into the specified
/// vector. The vector is always truncated before execution /// vector. The vector is always cleared before execution
/// begins. If possible, reusing the vector across calls can lead /// begins. If possible, reusing the vector across calls can lead
/// to better performance since it reuses the same backing buffer. /// to better performance since it reuses the same backing buffer.
/// ///
@@ -2256,7 +2523,7 @@ pub trait IndexedParallelIterator: ParallelIterator {
/// ``` /// ```
/// use rayon::prelude::*; /// use rayon::prelude::*;
/// ///
/// // any prior data will be truncated /// // any prior data will be cleared
/// let mut vec = vec![-1, -2, -3]; /// let mut vec = vec![-1, -2, -3];
/// ///
/// (0..5).into_par_iter() /// (0..5).into_par_iter()
@@ -2269,7 +2536,7 @@ pub trait IndexedParallelIterator: ParallelIterator {
} }
/// Unzips the results of the iterator into the specified /// Unzips the results of the iterator into the specified
/// vectors. The vectors are always truncated before execution /// vectors. The vectors are always cleared before execution
/// begins. If possible, reusing the vectors across calls can lead /// begins. If possible, reusing the vectors across calls can lead
/// to better performance since they reuse the same backing buffer. /// to better performance since they reuse the same backing buffer.
/// ///
@@ -2278,7 +2545,7 @@ pub trait IndexedParallelIterator: ParallelIterator {
/// ``` /// ```
/// use rayon::prelude::*; /// use rayon::prelude::*;
/// ///
/// // any prior data will be truncated /// // any prior data will be cleared
/// let mut left = vec![42; 10]; /// let mut left = vec![42; 10];
/// let mut right = vec![-1; 10]; /// let mut right = vec![-1; 10];
/// ///
@@ -2411,6 +2678,8 @@ pub trait IndexedParallelIterator: ParallelIterator {
/// [`par_chunks()`]: ../slice/trait.ParallelSlice.html#method.par_chunks /// [`par_chunks()`]: ../slice/trait.ParallelSlice.html#method.par_chunks
/// [`par_chunks_mut()`]: ../slice/trait.ParallelSliceMut.html#method.par_chunks_mut /// [`par_chunks_mut()`]: ../slice/trait.ParallelSliceMut.html#method.par_chunks_mut
/// ///
/// **Panics** if `chunk_size` is 0.
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
@@ -2419,6 +2688,7 @@ pub trait IndexedParallelIterator: ParallelIterator {
/// let r: Vec<Vec<i32>> = a.into_par_iter().chunks(3).collect(); /// let r: Vec<Vec<i32>> = a.into_par_iter().chunks(3).collect();
/// assert_eq!(r, vec![vec![1,2,3], vec![4,5,6], vec![7,8,9], vec![10]]); /// assert_eq!(r, vec![vec![1,2,3], vec![4,5,6], vec![7,8,9], vec![10]]);
/// ``` /// ```
#[track_caller]
fn chunks(self, chunk_size: usize) -> Chunks<Self> { fn chunks(self, chunk_size: usize) -> Chunks<Self> {
assert!(chunk_size != 0, "chunk_size must not be zero"); assert!(chunk_size != 0, "chunk_size must not be zero");
Chunks::new(self, chunk_size) Chunks::new(self, chunk_size)
@@ -2977,7 +3247,7 @@ pub trait IndexedParallelIterator: ParallelIterator {
/// See the [README] for more details on the internals of parallel /// See the [README] for more details on the internals of parallel
/// iterators. /// iterators.
/// ///
/// [README]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md /// [README]: https://github.com/rayon-rs/rayon/blob/main/src/iter/plumbing/README.md
fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result; fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> C::Result;
/// Internal method used to define the behavior of this parallel /// Internal method used to define the behavior of this parallel
@@ -2994,7 +3264,7 @@ pub trait IndexedParallelIterator: ParallelIterator {
/// See the [README] for more details on the internals of parallel /// See the [README] for more details on the internals of parallel
/// iterators. /// iterators.
/// ///
/// [README]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md /// [README]: https://github.com/rayon-rs/rayon/blob/main/src/iter/plumbing/README.md
fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output; fn with_producer<CB: ProducerCallback<Self::Item>>(self, callback: CB) -> CB::Output;
} }
@@ -3042,14 +3312,15 @@ where
/// ///
/// If your collection is not naturally parallel, the easiest (and /// If your collection is not naturally parallel, the easiest (and
/// fastest) way to do this is often to collect `par_iter` into a /// fastest) way to do this is often to collect `par_iter` into a
/// [`LinkedList`] or other intermediate data structure and then /// [`LinkedList`] (via [`collect_vec_list`]) or another intermediate
/// sequentially extend your collection. However, a more 'native' /// data structure and then sequentially extend your collection. However,
/// technique is to use the [`par_iter.fold`] or /// a more 'native' technique is to use the [`par_iter.fold`] or
/// [`par_iter.fold_with`] methods to create the collection. /// [`par_iter.fold_with`] methods to create the collection.
/// Alternatively, if your collection is 'natively' parallel, you /// Alternatively, if your collection is 'natively' parallel, you
/// can use `par_iter.for_each` to process each element in turn. /// can use `par_iter.for_each` to process each element in turn.
/// ///
/// [`LinkedList`]: https://doc.rust-lang.org/std/collections/struct.LinkedList.html /// [`LinkedList`]: https://doc.rust-lang.org/std/collections/struct.LinkedList.html
/// [`collect_vec_list`]: ParallelIterator::collect_vec_list
/// [`par_iter.fold`]: trait.ParallelIterator.html#method.fold /// [`par_iter.fold`]: trait.ParallelIterator.html#method.fold
/// [`par_iter.fold_with`]: trait.ParallelIterator.html#method.fold_with /// [`par_iter.fold_with`]: trait.ParallelIterator.html#method.fold_with
/// [`par_iter.for_each`]: trait.ParallelIterator.html#method.for_each /// [`par_iter.for_each`]: trait.ParallelIterator.html#method.for_each

View File

@@ -1,6 +1,11 @@
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; #[cfg(not(feature = "web_spin_lock"))]
use std::sync::Mutex; use std::sync::Mutex;
#[cfg(feature = "web_spin_lock")]
use wasm_sync::Mutex;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use crate::iter::plumbing::{bridge_unindexed, Folder, UnindexedConsumer, UnindexedProducer}; use crate::iter::plumbing::{bridge_unindexed, Folder, UnindexedConsumer, UnindexedProducer};
use crate::iter::ParallelIterator; use crate::iter::ParallelIterator;
use crate::{current_num_threads, current_thread_index}; use crate::{current_num_threads, current_thread_index};
@@ -13,6 +18,11 @@ use crate::{current_num_threads, current_thread_index};
/// `par_iter` instead. However, it can still be useful for iterators that are difficult to /// `par_iter` instead. However, it can still be useful for iterators that are difficult to
/// parallelize by other means, like channels or file or network I/O. /// parallelize by other means, like channels or file or network I/O.
/// ///
/// Iterator items are pulled by `next()` one at a time, synchronized from each thread that is
/// ready for work, so this may become a bottleneck if the serial iterator can't keep up with the
/// parallel demand. The items are not buffered by `IterBridge`, so it's fine to use this with
/// large or even unbounded iterators.
///
/// The resulting iterator is not guaranteed to keep the order of the original iterator. /// The resulting iterator is not guaranteed to keep the order of the original iterator.
/// ///
/// # Examples /// # Examples
@@ -99,24 +109,11 @@ impl<Iter: Iterator + Send> UnindexedProducer for &IterParallelProducer<'_, Iter
type Item = Iter::Item; type Item = Iter::Item;
fn split(self) -> (Self, Option<Self>) { fn split(self) -> (Self, Option<Self>) {
let mut count = self.split_count.load(Ordering::SeqCst); // Check if the iterator is exhausted
let update = self
loop { .split_count
// Check if the iterator is exhausted .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |c| c.checked_sub(1));
if let Some(new_count) = count.checked_sub(1) { (self, update.is_ok().then_some(self))
match self.split_count.compare_exchange_weak(
count,
new_count,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => return (self, Some(self)),
Err(last_count) => count = last_count,
}
} else {
return (self, None);
}
}
} }
fn fold_with<F>(self, mut folder: F) -> F fn fold_with<F>(self, mut folder: F) -> F

View File

@@ -35,8 +35,8 @@ modes (which is why there are two):
more like a `for_each` call: each time a new item is produced, the more like a `for_each` call: each time a new item is produced, the
`consume` method is called with that item. (The traits themselves are `consume` method is called with that item. (The traits themselves are
a bit more complex, as they support state that can be threaded a bit more complex, as they support state that can be threaded
through and ultimately reduced.) Unlike producers, there are two through and ultimately reduced.) Like producers, there are two
variants of consumers. The difference is how the split is performed: variants of consumers which differ in how the split is performed:
- in the `Consumer` trait, splitting is done with `split_at`, which - in the `Consumer` trait, splitting is done with `split_at`, which
accepts an index where the split should be performed. All accepts an index where the split should be performed. All
iterators can work in this mode. The resulting halves thus have an iterators can work in this mode. The resulting halves thus have an

View File

@@ -2,20 +2,19 @@
//! low-level details -- users of parallel iterators should not need to //! low-level details -- users of parallel iterators should not need to
//! interact with them directly. See [the `plumbing` README][r] for a general overview. //! interact with them directly. See [the `plumbing` README][r] for a general overview.
//! //!
//! [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md //! [r]: https://github.com/rayon-rs/rayon/blob/main/src/iter/plumbing/README.md
use crate::join_context; use crate::join_context;
use super::IndexedParallelIterator; use super::IndexedParallelIterator;
use std::cmp;
use std::usize; use std::usize;
/// The `ProducerCallback` trait is a kind of generic closure, /// The `ProducerCallback` trait is a kind of generic closure,
/// [analogous to `FnOnce`][FnOnce]. See [the corresponding section in /// [analogous to `FnOnce`][FnOnce]. See [the corresponding section in
/// the plumbing README][r] for more details. /// the plumbing README][r] for more details.
/// ///
/// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback /// [r]: https://github.com/rayon-rs/rayon/blob/main/src/iter/plumbing/README.md#producer-callback
/// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html /// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html
pub trait ProducerCallback<T> { pub trait ProducerCallback<T> {
/// The type of value returned by this callback. Analogous to /// The type of value returned by this callback. Analogous to
@@ -54,7 +53,7 @@ pub trait ProducerCallback<T> {
/// constraints on a required IntoIterator trait, so we inline /// constraints on a required IntoIterator trait, so we inline
/// IntoIterator here until that issue is fixed. /// IntoIterator here until that issue is fixed.
/// ///
/// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md /// [r]: https://github.com/rayon-rs/rayon/blob/main/src/iter/plumbing/README.md
/// [20671]: https://github.com/rust-lang/rust/issues/20671 /// [20671]: https://github.com/rust-lang/rust/issues/20671
pub trait Producer: Send + Sized { pub trait Producer: Send + Sized {
/// The type of item that will be produced by this producer once /// The type of item that will be produced by this producer once
@@ -121,7 +120,7 @@ pub trait Producer: Send + Sized {
/// combine their two results into one. See [the `plumbing` /// combine their two results into one. See [the `plumbing`
/// README][r] for further details. /// README][r] for further details.
/// ///
/// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md /// [r]: https://github.com/rayon-rs/rayon/blob/main/src/iter/plumbing/README.md
/// [fold]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold /// [fold]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold
/// [`Folder`]: trait.Folder.html /// [`Folder`]: trait.Folder.html
/// [`Producer`]: trait.Producer.html /// [`Producer`]: trait.Producer.html
@@ -198,7 +197,7 @@ pub trait Folder<Item>: Sized {
/// used to combine those two results into one. See [the `plumbing` /// used to combine those two results into one. See [the `plumbing`
/// README][r] for further details. /// README][r] for further details.
/// ///
/// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md /// [r]: https://github.com/rayon-rs/rayon/blob/main/src/iter/plumbing/README.md
pub trait Reducer<Result> { pub trait Reducer<Result> {
/// Reduce two final results into one; this is executed after a /// Reduce two final results into one; this is executed after a
/// split. /// split.
@@ -275,7 +274,7 @@ impl Splitter {
if stolen { if stolen {
// This job was stolen! Reset the number of desired splits to the // This job was stolen! Reset the number of desired splits to the
// thread count, if that's more than we had remaining anyway. // thread count, if that's more than we had remaining anyway.
self.splits = cmp::max(crate::current_num_threads(), self.splits / 2); self.splits = Ord::max(crate::current_num_threads(), self.splits / 2);
true true
} else if splits > 0 { } else if splits > 0 {
// We have splits remaining, make it so. // We have splits remaining, make it so.
@@ -313,14 +312,14 @@ impl LengthSplitter {
fn new(min: usize, max: usize, len: usize) -> LengthSplitter { fn new(min: usize, max: usize, len: usize) -> LengthSplitter {
let mut splitter = LengthSplitter { let mut splitter = LengthSplitter {
inner: Splitter::new(), inner: Splitter::new(),
min: cmp::max(min, 1), min: Ord::max(min, 1),
}; };
// Divide the given length by the max working length to get the minimum // Divide the given length by the max working length to get the minimum
// number of splits we need to get under that max. This rounds down, // number of splits we need to get under that max. This rounds down,
// but the splitter actually gives `next_power_of_two()` pieces anyway. // but the splitter actually gives `next_power_of_two()` pieces anyway.
// e.g. len 12345 / max 100 = 123 min_splits -> 128 pieces. // e.g. len 12345 / max 100 = 123 min_splits -> 128 pieces.
let min_splits = len / cmp::max(max, 1); let min_splits = len / Ord::max(max, 1);
// Only update the value if it's not splitting enough already. // Only update the value if it's not splitting enough already.
if min_splits > splitter.inner.splits { if min_splits > splitter.inner.splits {

View File

@@ -13,7 +13,7 @@ where
} }
fn mul<T: Product>(left: T, right: T) -> T { fn mul<T: Product>(left: T, right: T) -> T {
iter::once(left).chain(iter::once(right)).product() [left, right].into_iter().product()
} }
struct ProductConsumer<P: Send> { struct ProductConsumer<P: Send> {

View File

@@ -1,7 +1,6 @@
use super::noop::NoopConsumer; use super::noop::NoopConsumer;
use super::plumbing::*; use super::plumbing::*;
use super::*; use super::*;
use std::cmp::min;
/// `Skip` is an iterator that skips over the first `n` elements. /// `Skip` is an iterator that skips over the first `n` elements.
/// This struct is created by the [`skip()`] method on [`IndexedParallelIterator`] /// This struct is created by the [`skip()`] method on [`IndexedParallelIterator`]
@@ -21,7 +20,7 @@ where
{ {
/// Creates a new `Skip` iterator. /// Creates a new `Skip` iterator.
pub(super) fn new(base: I, n: usize) -> Self { pub(super) fn new(base: I, n: usize) -> Self {
let n = min(base.len(), n); let n = Ord::min(base.len(), n);
Skip { base, n } Skip { base, n }
} }
} }

View File

@@ -0,0 +1,144 @@
use super::plumbing::*;
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
/// `SkipAny` is an iterator that skips over `n` elements from anywhere in `I`.
/// This struct is created by the [`skip_any()`] method on [`ParallelIterator`]
///
/// [`skip_any()`]: trait.ParallelIterator.html#method.skip_any
/// [`ParallelIterator`]: trait.ParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct SkipAny<I: ParallelIterator> {
base: I,
count: usize,
}
impl<I> SkipAny<I>
where
I: ParallelIterator,
{
/// Creates a new `SkipAny` iterator.
pub(super) fn new(base: I, count: usize) -> Self {
SkipAny { base, count }
}
}
impl<I> ParallelIterator for SkipAny<I>
where
I: ParallelIterator,
{
type Item = I::Item;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let consumer1 = SkipAnyConsumer {
base: consumer,
count: &AtomicUsize::new(self.count),
};
self.base.drive_unindexed(consumer1)
}
}
/// ////////////////////////////////////////////////////////////////////////
/// Consumer implementation
struct SkipAnyConsumer<'f, C> {
base: C,
count: &'f AtomicUsize,
}
impl<'f, T, C> Consumer<T> for SkipAnyConsumer<'f, C>
where
C: Consumer<T>,
T: Send,
{
type Folder = SkipAnyFolder<'f, C::Folder>;
type Reducer = C::Reducer;
type Result = C::Result;
fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) {
let (left, right, reducer) = self.base.split_at(index);
(
SkipAnyConsumer { base: left, ..self },
SkipAnyConsumer {
base: right,
..self
},
reducer,
)
}
fn into_folder(self) -> Self::Folder {
SkipAnyFolder {
base: self.base.into_folder(),
count: self.count,
}
}
fn full(&self) -> bool {
self.base.full()
}
}
impl<'f, T, C> UnindexedConsumer<T> for SkipAnyConsumer<'f, C>
where
C: UnindexedConsumer<T>,
T: Send,
{
fn split_off_left(&self) -> Self {
SkipAnyConsumer {
base: self.base.split_off_left(),
..*self
}
}
fn to_reducer(&self) -> Self::Reducer {
self.base.to_reducer()
}
}
struct SkipAnyFolder<'f, C> {
base: C,
count: &'f AtomicUsize,
}
fn checked_decrement(u: &AtomicUsize) -> bool {
u.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |u| u.checked_sub(1))
.is_ok()
}
impl<'f, T, C> Folder<T> for SkipAnyFolder<'f, C>
where
C: Folder<T>,
{
type Result = C::Result;
fn consume(mut self, item: T) -> Self {
if !checked_decrement(self.count) {
self.base = self.base.consume(item);
}
self
}
fn consume_iter<I>(mut self, iter: I) -> Self
where
I: IntoIterator<Item = T>,
{
self.base = self.base.consume_iter(
iter.into_iter()
.skip_while(move |_| checked_decrement(self.count)),
);
self
}
fn complete(self) -> C::Result {
self.base.complete()
}
fn full(&self) -> bool {
self.base.full()
}
}

View File

@@ -0,0 +1,166 @@
use super::plumbing::*;
use super::*;
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
/// `SkipAnyWhile` is an iterator that skips over elements from anywhere in `I`
/// until the callback returns `false`.
/// This struct is created by the [`skip_any_while()`] method on [`ParallelIterator`]
///
/// [`skip_any_while()`]: trait.ParallelIterator.html#method.skip_any_while
/// [`ParallelIterator`]: trait.ParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct SkipAnyWhile<I: ParallelIterator, P> {
base: I,
predicate: P,
}
impl<I: ParallelIterator + fmt::Debug, P> fmt::Debug for SkipAnyWhile<I, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SkipAnyWhile")
.field("base", &self.base)
.finish()
}
}
impl<I, P> SkipAnyWhile<I, P>
where
I: ParallelIterator,
{
/// Creates a new `SkipAnyWhile` iterator.
pub(super) fn new(base: I, predicate: P) -> Self {
SkipAnyWhile { base, predicate }
}
}
impl<I, P> ParallelIterator for SkipAnyWhile<I, P>
where
I: ParallelIterator,
P: Fn(&I::Item) -> bool + Sync + Send,
{
type Item = I::Item;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let consumer1 = SkipAnyWhileConsumer {
base: consumer,
predicate: &self.predicate,
skipping: &AtomicBool::new(true),
};
self.base.drive_unindexed(consumer1)
}
}
/// ////////////////////////////////////////////////////////////////////////
/// Consumer implementation
struct SkipAnyWhileConsumer<'p, C, P> {
base: C,
predicate: &'p P,
skipping: &'p AtomicBool,
}
impl<'p, T, C, P> Consumer<T> for SkipAnyWhileConsumer<'p, C, P>
where
C: Consumer<T>,
P: Fn(&T) -> bool + Sync,
{
type Folder = SkipAnyWhileFolder<'p, C::Folder, P>;
type Reducer = C::Reducer;
type Result = C::Result;
fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) {
let (left, right, reducer) = self.base.split_at(index);
(
SkipAnyWhileConsumer { base: left, ..self },
SkipAnyWhileConsumer {
base: right,
..self
},
reducer,
)
}
fn into_folder(self) -> Self::Folder {
SkipAnyWhileFolder {
base: self.base.into_folder(),
predicate: self.predicate,
skipping: self.skipping,
}
}
fn full(&self) -> bool {
self.base.full()
}
}
impl<'p, T, C, P> UnindexedConsumer<T> for SkipAnyWhileConsumer<'p, C, P>
where
C: UnindexedConsumer<T>,
P: Fn(&T) -> bool + Sync,
{
fn split_off_left(&self) -> Self {
SkipAnyWhileConsumer {
base: self.base.split_off_left(),
..*self
}
}
fn to_reducer(&self) -> Self::Reducer {
self.base.to_reducer()
}
}
struct SkipAnyWhileFolder<'p, C, P> {
base: C,
predicate: &'p P,
skipping: &'p AtomicBool,
}
fn skip<T>(item: &T, skipping: &AtomicBool, predicate: &impl Fn(&T) -> bool) -> bool {
if !skipping.load(Ordering::Relaxed) {
return false;
}
if predicate(item) {
return true;
}
skipping.store(false, Ordering::Relaxed);
false
}
impl<'p, T, C, P> Folder<T> for SkipAnyWhileFolder<'p, C, P>
where
C: Folder<T>,
P: Fn(&T) -> bool + 'p,
{
type Result = C::Result;
fn consume(mut self, item: T) -> Self {
if !skip(&item, self.skipping, self.predicate) {
self.base = self.base.consume(item);
}
self
}
fn consume_iter<I>(mut self, iter: I) -> Self
where
I: IntoIterator<Item = T>,
{
self.base = self.base.consume_iter(
iter.into_iter()
.skip_while(move |x| skip(x, self.skipping, self.predicate)),
);
self
}
fn complete(self) -> C::Result {
self.base.complete()
}
fn full(&self) -> bool {
self.base.full()
}
}

View File

@@ -1,5 +1,3 @@
use std::cmp::min;
use super::plumbing::*; use super::plumbing::*;
use super::*; use super::*;
use crate::math::div_round_up; use crate::math::div_round_up;
@@ -116,7 +114,7 @@ where
} }
fn split_at(self, index: usize) -> (Self, Self) { fn split_at(self, index: usize) -> (Self, Self) {
let elem_index = min(index * self.step, self.len); let elem_index = Ord::min(index * self.step, self.len);
let (left, right) = self.base.split_at(elem_index); let (left, right) = self.base.split_at(elem_index);
( (

View File

@@ -13,7 +13,7 @@ where
} }
fn add<T: Sum>(left: T, right: T) -> T { fn add<T: Sum>(left: T, right: T) -> T {
iter::once(left).chain(iter::once(right)).sum() [left, right].into_iter().sum()
} }
struct SumConsumer<S: Send> { struct SumConsumer<S: Send> {

View File

@@ -1,6 +1,5 @@
use super::plumbing::*; use super::plumbing::*;
use super::*; use super::*;
use std::cmp::min;
/// `Take` is an iterator that iterates over the first `n` elements. /// `Take` is an iterator that iterates over the first `n` elements.
/// This struct is created by the [`take()`] method on [`IndexedParallelIterator`] /// This struct is created by the [`take()`] method on [`IndexedParallelIterator`]
@@ -20,7 +19,7 @@ where
{ {
/// Creates a new `Take` iterator. /// Creates a new `Take` iterator.
pub(super) fn new(base: I, n: usize) -> Self { pub(super) fn new(base: I, n: usize) -> Self {
let n = min(base.len(), n); let n = Ord::min(base.len(), n);
Take { base, n } Take { base, n }
} }
} }

View File

@@ -0,0 +1,144 @@
use super::plumbing::*;
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
/// `TakeAny` is an iterator that iterates over `n` elements from anywhere in `I`.
/// This struct is created by the [`take_any()`] method on [`ParallelIterator`]
///
/// [`take_any()`]: trait.ParallelIterator.html#method.take_any
/// [`ParallelIterator`]: trait.ParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct TakeAny<I: ParallelIterator> {
base: I,
count: usize,
}
impl<I> TakeAny<I>
where
I: ParallelIterator,
{
/// Creates a new `TakeAny` iterator.
pub(super) fn new(base: I, count: usize) -> Self {
TakeAny { base, count }
}
}
impl<I> ParallelIterator for TakeAny<I>
where
I: ParallelIterator,
{
type Item = I::Item;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let consumer1 = TakeAnyConsumer {
base: consumer,
count: &AtomicUsize::new(self.count),
};
self.base.drive_unindexed(consumer1)
}
}
/// ////////////////////////////////////////////////////////////////////////
/// Consumer implementation
struct TakeAnyConsumer<'f, C> {
base: C,
count: &'f AtomicUsize,
}
impl<'f, T, C> Consumer<T> for TakeAnyConsumer<'f, C>
where
C: Consumer<T>,
T: Send,
{
type Folder = TakeAnyFolder<'f, C::Folder>;
type Reducer = C::Reducer;
type Result = C::Result;
fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) {
let (left, right, reducer) = self.base.split_at(index);
(
TakeAnyConsumer { base: left, ..self },
TakeAnyConsumer {
base: right,
..self
},
reducer,
)
}
fn into_folder(self) -> Self::Folder {
TakeAnyFolder {
base: self.base.into_folder(),
count: self.count,
}
}
fn full(&self) -> bool {
self.count.load(Ordering::Relaxed) == 0 || self.base.full()
}
}
impl<'f, T, C> UnindexedConsumer<T> for TakeAnyConsumer<'f, C>
where
C: UnindexedConsumer<T>,
T: Send,
{
fn split_off_left(&self) -> Self {
TakeAnyConsumer {
base: self.base.split_off_left(),
..*self
}
}
fn to_reducer(&self) -> Self::Reducer {
self.base.to_reducer()
}
}
struct TakeAnyFolder<'f, C> {
base: C,
count: &'f AtomicUsize,
}
fn checked_decrement(u: &AtomicUsize) -> bool {
u.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |u| u.checked_sub(1))
.is_ok()
}
impl<'f, T, C> Folder<T> for TakeAnyFolder<'f, C>
where
C: Folder<T>,
{
type Result = C::Result;
fn consume(mut self, item: T) -> Self {
if checked_decrement(self.count) {
self.base = self.base.consume(item);
}
self
}
fn consume_iter<I>(mut self, iter: I) -> Self
where
I: IntoIterator<Item = T>,
{
self.base = self.base.consume_iter(
iter.into_iter()
.take_while(move |_| checked_decrement(self.count)),
);
self
}
fn complete(self) -> C::Result {
self.base.complete()
}
fn full(&self) -> bool {
self.count.load(Ordering::Relaxed) == 0 || self.base.full()
}
}

View File

@@ -0,0 +1,166 @@
use super::plumbing::*;
use super::*;
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
/// `TakeAnyWhile` is an iterator that iterates over elements from anywhere in `I`
/// until the callback returns `false`.
/// This struct is created by the [`take_any_while()`] method on [`ParallelIterator`]
///
/// [`take_any_while()`]: trait.ParallelIterator.html#method.take_any_while
/// [`ParallelIterator`]: trait.ParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct TakeAnyWhile<I: ParallelIterator, P> {
base: I,
predicate: P,
}
impl<I: ParallelIterator + fmt::Debug, P> fmt::Debug for TakeAnyWhile<I, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TakeAnyWhile")
.field("base", &self.base)
.finish()
}
}
impl<I, P> TakeAnyWhile<I, P>
where
I: ParallelIterator,
{
/// Creates a new `TakeAnyWhile` iterator.
pub(super) fn new(base: I, predicate: P) -> Self {
TakeAnyWhile { base, predicate }
}
}
impl<I, P> ParallelIterator for TakeAnyWhile<I, P>
where
I: ParallelIterator,
P: Fn(&I::Item) -> bool + Sync + Send,
{
type Item = I::Item;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let consumer1 = TakeAnyWhileConsumer {
base: consumer,
predicate: &self.predicate,
taking: &AtomicBool::new(true),
};
self.base.drive_unindexed(consumer1)
}
}
/// ////////////////////////////////////////////////////////////////////////
/// Consumer implementation
struct TakeAnyWhileConsumer<'p, C, P> {
base: C,
predicate: &'p P,
taking: &'p AtomicBool,
}
impl<'p, T, C, P> Consumer<T> for TakeAnyWhileConsumer<'p, C, P>
where
C: Consumer<T>,
P: Fn(&T) -> bool + Sync,
{
type Folder = TakeAnyWhileFolder<'p, C::Folder, P>;
type Reducer = C::Reducer;
type Result = C::Result;
fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) {
let (left, right, reducer) = self.base.split_at(index);
(
TakeAnyWhileConsumer { base: left, ..self },
TakeAnyWhileConsumer {
base: right,
..self
},
reducer,
)
}
fn into_folder(self) -> Self::Folder {
TakeAnyWhileFolder {
base: self.base.into_folder(),
predicate: self.predicate,
taking: self.taking,
}
}
fn full(&self) -> bool {
!self.taking.load(Ordering::Relaxed) || self.base.full()
}
}
impl<'p, T, C, P> UnindexedConsumer<T> for TakeAnyWhileConsumer<'p, C, P>
where
C: UnindexedConsumer<T>,
P: Fn(&T) -> bool + Sync,
{
fn split_off_left(&self) -> Self {
TakeAnyWhileConsumer {
base: self.base.split_off_left(),
..*self
}
}
fn to_reducer(&self) -> Self::Reducer {
self.base.to_reducer()
}
}
struct TakeAnyWhileFolder<'p, C, P> {
base: C,
predicate: &'p P,
taking: &'p AtomicBool,
}
fn take<T>(item: &T, taking: &AtomicBool, predicate: &impl Fn(&T) -> bool) -> bool {
if !taking.load(Ordering::Relaxed) {
return false;
}
if predicate(item) {
return true;
}
taking.store(false, Ordering::Relaxed);
false
}
impl<'p, T, C, P> Folder<T> for TakeAnyWhileFolder<'p, C, P>
where
C: Folder<T>,
P: Fn(&T) -> bool + 'p,
{
type Result = C::Result;
fn consume(mut self, item: T) -> Self {
if take(&item, self.taking, self.predicate) {
self.base = self.base.consume(item);
}
self
}
fn consume_iter<I>(mut self, iter: I) -> Self
where
I: IntoIterator<Item = T>,
{
self.base = self.base.consume_iter(
iter.into_iter()
.take_while(move |x| take(x, self.taking, self.predicate)),
);
self
}
fn complete(self) -> C::Result {
self.base.complete()
}
fn full(&self) -> bool {
!self.taking.load(Ordering::Relaxed) || self.base.full()
}
}

View File

@@ -7,10 +7,10 @@ use rayon_core::*;
use rand::distributions::Standard; use rand::distributions::Standard;
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use std::collections::LinkedList;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::collections::{BinaryHeap, VecDeque}; use std::collections::{BinaryHeap, VecDeque};
use std::f64; use std::f64;
use std::ffi::OsStr;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::mpsc; use std::sync::mpsc;
use std::usize; use std::usize;
@@ -60,8 +60,7 @@ fn execute_unindexed_range() {
#[test] #[test]
fn execute_pseudo_indexed_range() { fn execute_pseudo_indexed_range() {
use std::i128::MAX; let range = i128::MAX - 1024..i128::MAX;
let range = MAX - 1024..MAX;
// Given `Some` length, collecting `Vec` will try to act indexed. // Given `Some` length, collecting `Vec` will try to act indexed.
let a = range.clone().into_par_iter(); let a = range.clone().into_par_iter();
@@ -278,6 +277,7 @@ fn check_skip() {
let mut v1 = Vec::new(); let mut v1 = Vec::new();
a.par_iter().skip(0).collect_into_vec(&mut v1); a.par_iter().skip(0).collect_into_vec(&mut v1);
#[allow(clippy::iter_skip_zero)]
let v2 = a.iter().skip(0).collect::<Vec<_>>(); let v2 = a.iter().skip(0).collect::<Vec<_>>();
assert_eq!(v1, v2); assert_eq!(v1, v2);
@@ -468,6 +468,7 @@ fn check_cmp_gt_to_seq() {
} }
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn check_cmp_short_circuit() { fn check_cmp_short_circuit() {
// We only use a single thread in order to make the short-circuit behavior deterministic. // We only use a single thread in order to make the short-circuit behavior deterministic.
let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap(); let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
@@ -497,6 +498,7 @@ fn check_cmp_short_circuit() {
} }
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn check_partial_cmp_short_circuit() { fn check_partial_cmp_short_circuit() {
// We only use a single thread to make the short-circuit behavior deterministic. // We only use a single thread to make the short-circuit behavior deterministic.
let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap(); let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
@@ -526,6 +528,7 @@ fn check_partial_cmp_short_circuit() {
} }
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn check_partial_cmp_nan_short_circuit() { fn check_partial_cmp_nan_short_circuit() {
// We only use a single thread to make the short-circuit behavior deterministic. // We only use a single thread to make the short-circuit behavior deterministic.
let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap(); let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
@@ -626,7 +629,7 @@ fn check_partial_cmp_none_direct() {
let result = a.par_iter().partial_cmp(b.par_iter()); let result = a.par_iter().partial_cmp(b.par_iter());
assert!(result == None); assert!(result.is_none());
} }
#[test] #[test]
@@ -651,7 +654,7 @@ fn check_partial_cmp_late_nan_direct() {
} }
#[test] #[test]
fn check_partial_cmp_late_nane_to_seq() { fn check_partial_cmp_late_nan_to_seq() {
let a = vec![0.0, f64::NAN]; let a = vec![0.0, f64::NAN];
let b = vec![1.0, 1.0]; let b = vec![1.0, 1.0];
@@ -980,6 +983,25 @@ fn check_slice_split() {
assert_eq!(v, &[&slice[..1], &slice[..0], &slice[3..]]); assert_eq!(v, &[&slice[..1], &slice[..0], &slice[3..]]);
} }
#[test]
fn check_slice_split_inclusive() {
let v: Vec<_> = (0..1000).collect();
for m in 1..100 {
let a: Vec<_> = v.split_inclusive(|x| x % m == 0).collect();
let b: Vec<_> = v.par_split_inclusive(|x| x % m == 0).collect();
assert_eq!(a, b);
}
// same as std::slice::split_inclusive() examples
let slice = [10, 40, 33, 20];
let v: Vec<_> = slice.par_split_inclusive(|num| num % 3 == 0).collect();
assert_eq!(v, &[&slice[..3], &slice[3..]]);
let slice = [3, 10, 40, 33];
let v: Vec<_> = slice.par_split_inclusive(|num| num % 3 == 0).collect();
assert_eq!(v, &[&slice[..1], &slice[1..]]);
}
#[test] #[test]
fn check_slice_split_mut() { fn check_slice_split_mut() {
let mut v1: Vec<_> = (0..1000).collect(); let mut v1: Vec<_> = (0..1000).collect();
@@ -998,6 +1020,26 @@ fn check_slice_split_mut() {
assert_eq!(v, [1, 40, 30, 1, 60, 1]); assert_eq!(v, [1, 40, 30, 1, 60, 1]);
} }
#[test]
fn check_slice_split_inclusive_mut() {
let mut v1: Vec<_> = (0..1000).collect();
let mut v2 = v1.clone();
for m in 1..100 {
let a: Vec<_> = v1.split_inclusive_mut(|x| x % m == 0).collect();
let b: Vec<_> = v2.par_split_inclusive_mut(|x| x % m == 0).collect();
assert_eq!(a, b);
}
// same as std::slice::split_inclusive_mut() example
let mut v = [10, 40, 30, 20, 60, 50];
v.par_split_inclusive_mut(|num| num % 3 == 0)
.for_each(|group| {
let terminator_idx = group.len() - 1;
group[terminator_idx] = 1;
});
assert_eq!(v, [10, 40, 1, 20, 1, 1]);
}
#[test] #[test]
fn check_chunks() { fn check_chunks() {
let a: Vec<i32> = vec![1, 5, 10, 4, 100, 3, 1000, 2, 10000, 1]; let a: Vec<i32> = vec![1, 5, 10, 4, 100, 3, 1000, 2, 10000, 1];
@@ -1526,13 +1568,27 @@ fn par_iter_collect_cows() {
assert_eq!(a, b); assert_eq!(a, b);
// Collects `str` into a `String` // Collects `str` into a `String`
let a: Cow<'_, str> = s.split_whitespace().collect(); let sw = s.split_whitespace();
let b: Cow<'_, str> = s.par_split_whitespace().collect(); let psw = s.par_split_whitespace();
let a: Cow<'_, str> = sw.clone().collect();
let b: Cow<'_, str> = psw.clone().collect();
assert_eq!(a, b); assert_eq!(a, b);
// Collects `String` into a `String` // Collects `String` into a `String`
let a: Cow<'_, str> = s.split_whitespace().map(str::to_owned).collect(); let a: Cow<'_, str> = sw.map(str::to_owned).collect();
let b: Cow<'_, str> = s.par_split_whitespace().map(str::to_owned).collect(); let b: Cow<'_, str> = psw.map(str::to_owned).collect();
assert_eq!(a, b);
// Collects `OsStr` into a `OsString`
let sw = s.split_whitespace().map(OsStr::new);
let psw = s.par_split_whitespace().map(OsStr::new);
let a: Cow<'_, OsStr> = Cow::Owned(sw.clone().collect());
let b: Cow<'_, OsStr> = psw.clone().collect();
assert_eq!(a, b);
// Collects `OsString` into a `OsString`
let a: Cow<'_, OsStr> = Cow::Owned(sw.map(OsStr::to_owned).collect());
let b: Cow<'_, OsStr> = psw.map(OsStr::to_owned).collect();
assert_eq!(a, b); assert_eq!(a, b);
} }
@@ -1651,8 +1707,8 @@ fn check_lengths() {
let range = 0..1024 * 1024; let range = 0..1024 * 1024;
// Check against normalized values. // Check against normalized values.
let min_check = cmp::min(cmp::max(min, 1), range.len()); let min_check = Ord::min(Ord::max(min, 1), range.len());
let max_check = cmp::max(max, min_check.saturating_add(min_check - 1)); let max_check = Ord::max(max, min_check.saturating_add(min_check - 1));
assert!( assert!(
range range
@@ -2183,3 +2239,95 @@ fn check_update() {
assert_eq!(v, vec![vec![1, 0], vec![3, 2, 1, 0]]); assert_eq!(v, vec![vec![1, 0], vec![3, 2, 1, 0]]);
} }
#[test]
fn walk_tree_prefix() {
let v: Vec<u32> = crate::iter::walk_tree_prefix(0u32..100, |r| {
// root is smallest
let mid = (r.start + 1 + r.end) / 2;
// small indices to the left, large to the right
std::iter::once((r.start + 1)..mid)
.chain(std::iter::once(mid..r.end))
.filter(|r| !r.is_empty())
})
.map(|r| r.start)
.collect();
assert!(v.into_iter().eq(0..100));
}
#[test]
fn walk_tree_postfix() {
let v: Vec<_> = crate::iter::walk_tree_postfix(0u64..100, |r| {
// root is largest
let mid = (r.start + r.end - 1) / 2;
// small indices to the left, large to the right
std::iter::once(r.start..mid)
.chain(std::iter::once(mid..(r.end - 1)))
.filter(|r| !r.is_empty())
})
.map(|r| r.end - 1)
.collect();
assert!(v.into_iter().eq(0..100));
}
#[test]
fn walk_flat_tree_prefix() {
let v: Vec<_> =
crate::iter::walk_tree_prefix(0, |&e| if e < 99 { Some(e + 1) } else { None }).collect();
assert!(v.into_iter().eq(0..100));
}
#[test]
fn walk_flat_tree_postfix() {
let v: Vec<_> =
crate::iter::walk_tree_postfix(99, |&e| if e > 0 { Some(e - 1) } else { None }).collect();
assert!(v.into_iter().eq(0..100));
}
#[test]
fn walk_tree_prefix_degree5() {
let depth = 5;
let nodes_number = (1 - 5i32.pow(depth)) / (1 - 5);
let nodes = (0..nodes_number).collect::<Vec<_>>();
let v: Vec<i32> = crate::iter::walk_tree_prefix(nodes.as_slice(), |&r| {
r.split_first()
.into_iter()
.filter_map(|(_, r)| if r.is_empty() { None } else { Some(r) })
.flat_map(|r| r.chunks(r.len() / 5))
})
.filter_map(|r| r.first().copied())
.collect();
assert_eq!(v, nodes);
}
#[test]
fn walk_tree_postfix_degree5() {
let depth = 5;
let nodes_number = (1 - 5i32.pow(depth)) / (1 - 5);
let nodes = (0..nodes_number).collect::<Vec<_>>();
let v: Vec<i32> = crate::iter::walk_tree_postfix(nodes.as_slice(), |&r| {
r.split_last()
.into_iter()
.filter_map(|(_, r)| if r.is_empty() { None } else { Some(r) })
.flat_map(|r| r.chunks(r.len() / 5))
})
.filter_map(|r| r.last().copied())
.collect();
assert_eq!(v, nodes)
}
#[test]
fn blocks() {
let count = AtomicUsize::new(0);
let v: Vec<usize> = (0..1000)
.into_par_iter()
.map(|_| count.fetch_add(1, Ordering::Relaxed))
.by_uniform_blocks(100)
.collect();
let m = v
.chunks(100)
.map(|c| c.iter().max().copied().unwrap())
.collect::<Vec<usize>>();
assert!(m.windows(2).all(|w| w[0].lt(&w[1])));
assert_eq!(v.len(), 1000);
}

View File

@@ -0,0 +1,529 @@
use crate::iter::plumbing::{bridge_unindexed, Folder, UnindexedConsumer, UnindexedProducer};
use crate::prelude::*;
use std::iter::once;
#[derive(Debug)]
struct WalkTreePrefixProducer<'b, S, B> {
to_explore: Vec<S>, // nodes (and subtrees) we have to process
seen: Vec<S>, // nodes which have already been explored
children_of: &'b B, // function generating children
}
impl<S, B, I> UnindexedProducer for WalkTreePrefixProducer<'_, S, B>
where
S: Send,
B: Fn(&S) -> I + Send + Sync,
I: IntoIterator<Item = S>,
I::IntoIter: DoubleEndedIterator,
{
type Item = S;
fn split(mut self) -> (Self, Option<Self>) {
// explore while front is of size one.
while self.to_explore.len() == 1 {
let front_node = self.to_explore.pop().unwrap();
self.to_explore
.extend((self.children_of)(&front_node).into_iter().rev());
self.seen.push(front_node);
}
// now take half of the front.
let right_children = split_vec(&mut self.to_explore);
let right = right_children
.map(|mut c| {
std::mem::swap(&mut c, &mut self.to_explore);
WalkTreePrefixProducer {
to_explore: c,
seen: Vec::new(),
children_of: self.children_of,
}
})
.or_else(|| {
// we can still try to divide 'seen'
let right_seen = split_vec(&mut self.seen);
right_seen.map(|s| WalkTreePrefixProducer {
to_explore: Default::default(),
seen: s,
children_of: self.children_of,
})
});
(self, right)
}
fn fold_with<F>(mut self, mut folder: F) -> F
where
F: Folder<Self::Item>,
{
// start by consuming everything seen
folder = folder.consume_iter(self.seen);
if folder.full() {
return folder;
}
// now do all remaining explorations
while let Some(e) = self.to_explore.pop() {
self.to_explore
.extend((self.children_of)(&e).into_iter().rev());
folder = folder.consume(e);
if folder.full() {
return folder;
}
}
folder
}
}
/// ParallelIterator for arbitrary tree-shaped patterns.
/// Returned by the [`walk_tree_prefix()`] function.
#[derive(Debug)]
pub struct WalkTreePrefix<S, B> {
initial_state: S,
children_of: B,
}
impl<S, B, I> ParallelIterator for WalkTreePrefix<S, B>
where
S: Send,
B: Fn(&S) -> I + Send + Sync,
I: IntoIterator<Item = S>,
I::IntoIter: DoubleEndedIterator,
{
type Item = S;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let producer = WalkTreePrefixProducer {
to_explore: once(self.initial_state).collect(),
seen: Vec::new(),
children_of: &self.children_of,
};
bridge_unindexed(producer, consumer)
}
}
/// Create a tree-like prefix parallel iterator from an initial root node.
/// The `children_of` function should take a node and return an iterator over its child nodes.
/// The best parallelization is obtained when the tree is balanced
/// but we should also be able to handle harder cases.
///
/// # Ordering
///
/// This function guarantees a prefix ordering. See also [`walk_tree_postfix`],
/// which guarantees a postfix order.
/// If you don't care about ordering, you should use [`walk_tree`],
/// which will use whatever is believed to be fastest.
/// For example a perfect binary tree of 7 nodes will reduced in the following order:
///
/// ```text
/// a
/// / \
/// / \
/// b c
/// / \ / \
/// d e f g
///
/// reduced as a,b,d,e,c,f,g
///
/// ```
///
/// # Example
///
/// ```text
/// 4
/// / \
/// / \
/// 2 3
/// / \
/// 1 2
/// ```
///
/// ```
/// use rayon::iter::walk_tree_prefix;
/// use rayon::prelude::*;
///
/// let par_iter = walk_tree_prefix(4, |&e| {
/// if e <= 2 {
/// Vec::new()
/// } else {
/// vec![e / 2, e / 2 + 1]
/// }
/// });
/// assert_eq!(par_iter.sum::<u32>(), 12);
/// ```
///
/// # Example
///
/// ```
/// use rayon::prelude::*;
/// use rayon::iter::walk_tree_prefix;
///
/// struct Node {
/// content: u32,
/// left: Option<Box<Node>>,
/// right: Option<Box<Node>>,
/// }
///
/// // Here we loop on the following tree:
/// //
/// // 10
/// // / \
/// // / \
/// // 3 14
/// // \
/// // \
/// // 18
///
/// let root = Node {
/// content: 10,
/// left: Some(Box::new(Node {
/// content: 3,
/// left: None,
/// right: None,
/// })),
/// right: Some(Box::new(Node {
/// content: 14,
/// left: None,
/// right: Some(Box::new(Node {
/// content: 18,
/// left: None,
/// right: None,
/// })),
/// })),
/// };
///
/// let mut v: Vec<u32> = walk_tree_prefix(&root, |r| {
/// r.left
/// .as_ref()
/// .into_iter()
/// .chain(r.right.as_ref())
/// .map(|n| &**n)
/// })
/// .map(|node| node.content)
/// .collect();
/// assert_eq!(v, vec![10, 3, 14, 18]);
/// ```
///
pub fn walk_tree_prefix<S, B, I>(root: S, children_of: B) -> WalkTreePrefix<S, B>
where
S: Send,
B: Fn(&S) -> I + Send + Sync,
I: IntoIterator<Item = S>,
I::IntoIter: DoubleEndedIterator,
{
WalkTreePrefix {
initial_state: root,
children_of,
}
}
// post fix
#[derive(Debug)]
struct WalkTreePostfixProducer<'b, S, B> {
to_explore: Vec<S>, // nodes (and subtrees) we have to process
seen: Vec<S>, // nodes which have already been explored
children_of: &'b B, // function generating children
}
impl<S, B, I> UnindexedProducer for WalkTreePostfixProducer<'_, S, B>
where
S: Send,
B: Fn(&S) -> I + Send + Sync,
I: IntoIterator<Item = S>,
{
type Item = S;
fn split(mut self) -> (Self, Option<Self>) {
// explore while front is of size one.
while self.to_explore.len() == 1 {
let front_node = self.to_explore.pop().unwrap();
self.to_explore
.extend((self.children_of)(&front_node).into_iter());
self.seen.push(front_node);
}
// now take half of the front.
let right_children = split_vec(&mut self.to_explore);
let right = right_children
.map(|c| {
let right_seen = std::mem::take(&mut self.seen); // postfix -> upper nodes are processed last
WalkTreePostfixProducer {
to_explore: c,
seen: right_seen,
children_of: self.children_of,
}
})
.or_else(|| {
// we can still try to divide 'seen'
let right_seen = split_vec(&mut self.seen);
right_seen.map(|mut s| {
std::mem::swap(&mut self.seen, &mut s);
WalkTreePostfixProducer {
to_explore: Default::default(),
seen: s,
children_of: self.children_of,
}
})
});
(self, right)
}
fn fold_with<F>(self, mut folder: F) -> F
where
F: Folder<Self::Item>,
{
// now do all remaining explorations
for e in self.to_explore {
folder = consume_rec_postfix(&self.children_of, e, folder);
if folder.full() {
return folder;
}
}
// end by consuming everything seen
folder.consume_iter(self.seen.into_iter().rev())
}
}
fn consume_rec_postfix<F, S, B, I>(children_of: &B, s: S, mut folder: F) -> F
where
F: Folder<S>,
B: Fn(&S) -> I,
I: IntoIterator<Item = S>,
{
let children = (children_of)(&s).into_iter();
for child in children {
folder = consume_rec_postfix(children_of, child, folder);
if folder.full() {
return folder;
}
}
folder.consume(s)
}
/// ParallelIterator for arbitrary tree-shaped patterns.
/// Returned by the [`walk_tree_postfix()`] function.
#[derive(Debug)]
pub struct WalkTreePostfix<S, B> {
initial_state: S,
children_of: B,
}
impl<S, B, I> ParallelIterator for WalkTreePostfix<S, B>
where
S: Send,
B: Fn(&S) -> I + Send + Sync,
I: IntoIterator<Item = S>,
{
type Item = S;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let producer = WalkTreePostfixProducer {
to_explore: once(self.initial_state).collect(),
seen: Vec::new(),
children_of: &self.children_of,
};
bridge_unindexed(producer, consumer)
}
}
/// Divide given vector in two equally sized vectors.
/// Return `None` if initial size is <=1.
/// We return the first half and keep the last half in `v`.
fn split_vec<T>(v: &mut Vec<T>) -> Option<Vec<T>> {
if v.len() <= 1 {
None
} else {
let n = v.len() / 2;
Some(v.split_off(n))
}
}
/// Create a tree like postfix parallel iterator from an initial root node.
/// The `children_of` function should take a node and iterate on all of its child nodes.
/// The best parallelization is obtained when the tree is balanced
/// but we should also be able to handle harder cases.
///
/// # Ordering
///
/// This function guarantees a postfix ordering. See also [`walk_tree_prefix`] which guarantees a
/// prefix order. If you don't care about ordering, you should use [`walk_tree`], which will use
/// whatever is believed to be fastest.
///
/// Between siblings, children are reduced in order -- that is first children are reduced first.
///
/// For example a perfect binary tree of 7 nodes will reduced in the following order:
///
/// ```text
/// a
/// / \
/// / \
/// b c
/// / \ / \
/// d e f g
///
/// reduced as d,e,b,f,g,c,a
///
/// ```
///
/// # Example
///
/// ```text
/// 4
/// / \
/// / \
/// 2 3
/// / \
/// 1 2
/// ```
///
/// ```
/// use rayon::iter::walk_tree_postfix;
/// use rayon::prelude::*;
///
/// let par_iter = walk_tree_postfix(4, |&e| {
/// if e <= 2 {
/// Vec::new()
/// } else {
/// vec![e / 2, e / 2 + 1]
/// }
/// });
/// assert_eq!(par_iter.sum::<u32>(), 12);
/// ```
///
/// # Example
///
/// ```
/// use rayon::prelude::*;
/// use rayon::iter::walk_tree_postfix;
///
/// struct Node {
/// content: u32,
/// left: Option<Box<Node>>,
/// right: Option<Box<Node>>,
/// }
///
/// // Here we loop on the following tree:
/// //
/// // 10
/// // / \
/// // / \
/// // 3 14
/// // \
/// // \
/// // 18
///
/// let root = Node {
/// content: 10,
/// left: Some(Box::new(Node {
/// content: 3,
/// left: None,
/// right: None,
/// })),
/// right: Some(Box::new(Node {
/// content: 14,
/// left: None,
/// right: Some(Box::new(Node {
/// content: 18,
/// left: None,
/// right: None,
/// })),
/// })),
/// };
///
/// let mut v: Vec<u32> = walk_tree_postfix(&root, |r| {
/// r.left
/// .as_ref()
/// .into_iter()
/// .chain(r.right.as_ref())
/// .map(|n| &**n)
/// })
/// .map(|node| node.content)
/// .collect();
/// assert_eq!(v, vec![3, 18, 14, 10]);
/// ```
///
pub fn walk_tree_postfix<S, B, I>(root: S, children_of: B) -> WalkTreePostfix<S, B>
where
S: Send,
B: Fn(&S) -> I + Send + Sync,
I: IntoIterator<Item = S>,
{
WalkTreePostfix {
initial_state: root,
children_of,
}
}
/// ParallelIterator for arbitrary tree-shaped patterns.
/// Returned by the [`walk_tree()`] function.
#[derive(Debug)]
pub struct WalkTree<S, B>(WalkTreePostfix<S, B>);
/// Create a tree like parallel iterator from an initial root node.
/// The `children_of` function should take a node and iterate on all of its child nodes.
/// The best parallelization is obtained when the tree is balanced
/// but we should also be able to handle harder cases.
///
/// # Ordering
///
/// This function does not guarantee any ordering but will
/// use whatever algorithm is thought to achieve the fastest traversal.
/// See also [`walk_tree_prefix`] which guarantees a
/// prefix order and [`walk_tree_postfix`] which guarantees a postfix order.
///
/// # Example
///
/// ```text
/// 4
/// / \
/// / \
/// 2 3
/// / \
/// 1 2
/// ```
///
/// ```
/// use rayon::iter::walk_tree;
/// use rayon::prelude::*;
///
/// let par_iter = walk_tree(4, |&e| {
/// if e <= 2 {
/// Vec::new()
/// } else {
/// vec![e / 2, e / 2 + 1]
/// }
/// });
/// assert_eq!(par_iter.sum::<u32>(), 12);
/// ```
pub fn walk_tree<S, B, I>(root: S, children_of: B) -> WalkTree<S, B>
where
S: Send,
B: Fn(&S) -> I + Send + Sync,
I: IntoIterator<Item = S>,
I::IntoIter: DoubleEndedIterator,
{
let walker = WalkTreePostfix {
initial_state: root,
children_of,
};
WalkTree(walker)
}
impl<S, B, I> ParallelIterator for WalkTree<S, B>
where
S: Send,
B: Fn(&S) -> I + Send + Sync,
I: IntoIterator<Item = S> + Send,
I::IntoIter: DoubleEndedIterator,
{
type Item = S;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
self.0.drive_unindexed(consumer)
}
}

View File

@@ -1,6 +1,5 @@
use super::plumbing::*; use super::plumbing::*;
use super::*; use super::*;
use std::cmp;
use std::iter; use std::iter;
/// `Zip` is an iterator that zips up `a` and `b` into a single iterator /// `Zip` is an iterator that zips up `a` and `b` into a single iterator
@@ -59,7 +58,7 @@ where
} }
fn len(&self) -> usize { fn len(&self) -> usize {
cmp::min(self.a.len(), self.b.len()) Ord::min(self.a.len(), self.b.len())
} }
fn with_producer<CB>(self, callback: CB) -> CB::Output fn with_producer<CB>(self, callback: CB) -> CB::Output
@@ -135,11 +134,11 @@ impl<A: Producer, B: Producer> Producer for ZipProducer<A, B> {
} }
fn min_len(&self) -> usize { fn min_len(&self) -> usize {
cmp::max(self.a.min_len(), self.b.min_len()) Ord::max(self.a.min_len(), self.b.min_len())
} }
fn max_len(&self) -> usize { fn max_len(&self) -> usize {
cmp::min(self.a.max_len(), self.b.max_len()) Ord::min(self.a.max_len(), self.b.max_len())
} }
fn split_at(self, index: usize) -> (Self, Self) { fn split_at(self, index: usize) -> (Self, Self) {

View File

@@ -3,10 +3,10 @@
#![deny(unreachable_pub)] #![deny(unreachable_pub)]
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
//! Data-parallelism library that makes it easy to convert sequential //! Rayon is a data-parallelism library that makes it easy to convert sequential
//! computations into parallel //! computations into parallel.
//! //!
//! Rayon is lightweight and convenient for introducing parallelism into existing //! It is lightweight and convenient for introducing parallelism into existing
//! code. It guarantees data-race free executions and takes advantage of //! code. It guarantees data-race free executions and takes advantage of
//! parallelism when sensible, based on work-load at runtime. //! parallelism when sensible, based on work-load at runtime.
//! //!
@@ -76,11 +76,16 @@
//! [the `collections` from `std`]: https://doc.rust-lang.org/std/collections/index.html //! [the `collections` from `std`]: https://doc.rust-lang.org/std/collections/index.html
//! [`std`]: https://doc.rust-lang.org/std/ //! [`std`]: https://doc.rust-lang.org/std/
//! //!
//! # Targets without threading
//!
//! Rayon has limited support for targets without `std` threading implementations.
//! See the [`rayon_core`] documentation for more information about its global fallback.
//!
//! # Other questions? //! # Other questions?
//! //!
//! See [the Rayon FAQ][faq]. //! See [the Rayon FAQ][faq].
//! //!
//! [faq]: https://github.com/rayon-rs/rayon/blob/master/FAQ.md //! [faq]: https://github.com/rayon-rs/rayon/blob/main/FAQ.md
#[macro_use] #[macro_use]
mod delegate; mod delegate;
@@ -119,6 +124,7 @@ pub use rayon_core::{in_place_scope, scope, Scope};
pub use rayon_core::{in_place_scope_fifo, scope_fifo, ScopeFifo}; pub use rayon_core::{in_place_scope_fifo, scope_fifo, ScopeFifo};
pub use rayon_core::{join, join_context}; pub use rayon_core::{join, join_context};
pub use rayon_core::{spawn, spawn_fifo}; pub use rayon_core::{spawn, spawn_fifo};
pub use rayon_core::{yield_local, yield_now, Yield};
/// We need to transmit raw pointers across threads. It is possible to do this /// We need to transmit raw pointers across threads. It is possible to do this
/// without any unsafe code by converting pointers to usize or to AtomicPtr<T> /// without any unsafe code by converting pointers to usize or to AtomicPtr<T>
@@ -146,7 +152,7 @@ impl<T> SendPtr<T> {
// Implement Clone without the T: Clone bound from the derive // Implement Clone without the T: Clone bound from the derive
impl<T> Clone for SendPtr<T> { impl<T> Clone for SendPtr<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(self.0) *self
} }
} }

View File

@@ -18,10 +18,7 @@
use crate::iter::plumbing::*; use crate::iter::plumbing::*;
use crate::iter::*; use crate::iter::*;
use std::char;
use std::convert::TryFrom;
use std::ops::Range; use std::ops::Range;
use std::usize;
/// Parallel iterator over a range, implemented for all integer types and `char`. /// Parallel iterator over a range, implemented for all integer types and `char`.
/// ///
@@ -379,8 +376,6 @@ fn check_range_split_at_overflow() {
#[test] #[test]
fn test_i128_len_doesnt_overflow() { fn test_i128_len_doesnt_overflow() {
use std::{i128, u128};
// Using parse because some versions of rust don't allow long literals // Using parse because some versions of rust don't allow long literals
let octillion: i128 = "1000000000000000000000000000".parse().unwrap(); let octillion: i128 = "1000000000000000000000000000".parse().unwrap();
let producer = IterProducer { let producer = IterProducer {
@@ -396,7 +391,6 @@ fn test_i128_len_doesnt_overflow() {
#[test] #[test]
fn test_u64_opt_len() { fn test_u64_opt_len() {
use std::{u64, usize};
assert_eq!(Some(100), (0..100u64).into_par_iter().opt_len()); assert_eq!(Some(100), (0..100u64).into_par_iter().opt_len());
assert_eq!( assert_eq!(
Some(usize::MAX), Some(usize::MAX),
@@ -415,7 +409,6 @@ fn test_u64_opt_len() {
#[test] #[test]
fn test_u128_opt_len() { fn test_u128_opt_len() {
use std::{u128, usize};
assert_eq!(Some(100), (0..100u128).into_par_iter().opt_len()); assert_eq!(Some(100), (0..100u128).into_par_iter().opt_len());
assert_eq!( assert_eq!(
Some(usize::MAX), Some(usize::MAX),
@@ -431,7 +424,6 @@ fn test_u128_opt_len() {
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
fn test_usize_i64_overflow() { fn test_usize_i64_overflow() {
use crate::ThreadPoolBuilder; use crate::ThreadPoolBuilder;
use std::i64;
let iter = (-2..i64::MAX).into_par_iter(); let iter = (-2..i64::MAX).into_par_iter();
assert_eq!(iter.opt_len(), Some(i64::MAX as usize + 2)); assert_eq!(iter.opt_len(), Some(i64::MAX as usize + 2));

View File

@@ -18,7 +18,6 @@
use crate::iter::plumbing::*; use crate::iter::plumbing::*;
use crate::iter::*; use crate::iter::*;
use std::char;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
/// Parallel iterator over an inclusive range, implemented for all integer types and `char`. /// Parallel iterator over an inclusive range, implemented for all integer types and `char`.
@@ -313,7 +312,6 @@ impl IndexedParallelIterator for Iter<char> {
#[test] #[test]
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
fn test_u32_opt_len() { fn test_u32_opt_len() {
use std::u32;
assert_eq!(Some(101), (0..=100u32).into_par_iter().opt_len()); assert_eq!(Some(101), (0..=100u32).into_par_iter().opt_len());
assert_eq!( assert_eq!(
Some(u32::MAX as usize), Some(u32::MAX as usize),
@@ -327,7 +325,6 @@ fn test_u32_opt_len() {
#[test] #[test]
fn test_u64_opt_len() { fn test_u64_opt_len() {
use std::{u64, usize};
assert_eq!(Some(101), (0..=100u64).into_par_iter().opt_len()); assert_eq!(Some(101), (0..=100u64).into_par_iter().opt_len());
assert_eq!( assert_eq!(
Some(usize::MAX), Some(usize::MAX),
@@ -339,7 +336,6 @@ fn test_u64_opt_len() {
#[test] #[test]
fn test_u128_opt_len() { fn test_u128_opt_len() {
use std::{u128, usize};
assert_eq!(Some(101), (0..=100u128).into_par_iter().opt_len()); assert_eq!(Some(101), (0..=100u128).into_par_iter().opt_len());
assert_eq!( assert_eq!(
Some(usize::MAX), Some(usize::MAX),
@@ -355,7 +351,6 @@ fn test_u128_opt_len() {
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
fn test_usize_i64_overflow() { fn test_usize_i64_overflow() {
use crate::ThreadPoolBuilder; use crate::ThreadPoolBuilder;
use std::i64;
let iter = (-2..=i64::MAX).into_par_iter(); let iter = (-2..=i64::MAX).into_par_iter();
assert_eq!(iter.opt_len(), Some(i64::MAX as usize + 3)); assert_eq!(iter.opt_len(), Some(i64::MAX as usize + 3));

View File

@@ -0,0 +1,244 @@
use crate::iter::plumbing::*;
use crate::iter::*;
use std::marker::PhantomData;
use std::{fmt, mem};
trait ChunkBySlice<T>: AsRef<[T]> + Default + Send {
fn split(self, index: usize) -> (Self, Self);
fn find(&self, pred: &impl Fn(&T, &T) -> bool, start: usize, end: usize) -> Option<usize> {
self.as_ref()[start..end]
.windows(2)
.position(move |w| !pred(&w[0], &w[1]))
.map(|i| i + 1)
}
fn rfind(&self, pred: &impl Fn(&T, &T) -> bool, end: usize) -> Option<usize> {
self.as_ref()[..end]
.windows(2)
.rposition(move |w| !pred(&w[0], &w[1]))
.map(|i| i + 1)
}
}
impl<T: Sync> ChunkBySlice<T> for &[T] {
fn split(self, index: usize) -> (Self, Self) {
self.split_at(index)
}
}
impl<T: Send> ChunkBySlice<T> for &mut [T] {
fn split(self, index: usize) -> (Self, Self) {
self.split_at_mut(index)
}
}
struct ChunkByProducer<'p, T, Slice, Pred> {
slice: Slice,
pred: &'p Pred,
tail: usize,
marker: PhantomData<fn(&T)>,
}
// Note: this implementation is very similar to `SplitProducer`.
impl<T, Slice, Pred> UnindexedProducer for ChunkByProducer<'_, T, Slice, Pred>
where
Slice: ChunkBySlice<T>,
Pred: Fn(&T, &T) -> bool + Send + Sync,
{
type Item = Slice;
fn split(self) -> (Self, Option<Self>) {
if self.tail < 2 {
return (Self { tail: 0, ..self }, None);
}
// Look forward for the separator, and failing that look backward.
let mid = self.tail / 2;
let index = match self.slice.find(self.pred, mid, self.tail) {
Some(i) => Some(mid + i),
None => self.slice.rfind(self.pred, mid + 1),
};
if let Some(index) = index {
let (left, right) = self.slice.split(index);
let (left_tail, right_tail) = if index <= mid {
// If we scanned backwards to find the separator, everything in
// the right side is exhausted, with no separators left to find.
(index, 0)
} else {
(mid + 1, self.tail - index)
};
// Create the left split before the separator.
let left = Self {
slice: left,
tail: left_tail,
..self
};
// Create the right split following the separator.
let right = Self {
slice: right,
tail: right_tail,
..self
};
(left, Some(right))
} else {
// The search is exhausted, no more separators...
(Self { tail: 0, ..self }, None)
}
}
fn fold_with<F>(self, mut folder: F) -> F
where
F: Folder<Self::Item>,
{
let Self {
slice, pred, tail, ..
} = self;
let (slice, tail) = if tail == slice.as_ref().len() {
// No tail section, so just let `consume_iter` do it all.
(Some(slice), None)
} else if let Some(index) = slice.rfind(pred, tail) {
// We found the last separator to complete the tail, so
// end with that slice after `consume_iter` finds the rest.
let (left, right) = slice.split(index);
(Some(left), Some(right))
} else {
// We know there are no separators at all, so it's all "tail".
(None, Some(slice))
};
if let Some(mut slice) = slice {
// TODO (MSRV 1.77) use either:
// folder.consume_iter(slice.chunk_by(pred))
// folder.consume_iter(slice.chunk_by_mut(pred))
folder = folder.consume_iter(std::iter::from_fn(move || {
let len = slice.as_ref().len();
if len > 0 {
let i = slice.find(pred, 0, len).unwrap_or(len);
let (head, tail) = mem::take(&mut slice).split(i);
slice = tail;
Some(head)
} else {
None
}
}));
}
if let Some(tail) = tail {
folder = folder.consume(tail);
}
folder
}
}
/// Parallel iterator over slice in (non-overlapping) chunks separated by a predicate.
///
/// This struct is created by the [`par_chunk_by`] method on `&[T]`.
///
/// [`par_chunk_by`]: trait.ParallelSlice.html#method.par_chunk_by
pub struct ChunkBy<'data, T, P> {
pred: P,
slice: &'data [T],
}
impl<'data, T, P: Clone> Clone for ChunkBy<'data, T, P> {
fn clone(&self) -> Self {
ChunkBy {
pred: self.pred.clone(),
slice: self.slice,
}
}
}
impl<'data, T: fmt::Debug, P> fmt::Debug for ChunkBy<'data, T, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChunkBy")
.field("slice", &self.slice)
.finish()
}
}
impl<'data, T, P> ChunkBy<'data, T, P> {
pub(super) fn new(slice: &'data [T], pred: P) -> Self {
Self { pred, slice }
}
}
impl<'data, T, P> ParallelIterator for ChunkBy<'data, T, P>
where
T: Sync,
P: Fn(&T, &T) -> bool + Send + Sync,
{
type Item = &'data [T];
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
bridge_unindexed(
ChunkByProducer {
tail: self.slice.len(),
slice: self.slice,
pred: &self.pred,
marker: PhantomData,
},
consumer,
)
}
}
/// Parallel iterator over slice in (non-overlapping) mutable chunks
/// separated by a predicate.
///
/// This struct is created by the [`par_chunk_by_mut`] method on `&mut [T]`.
///
/// [`par_chunk_by_mut`]: trait.ParallelSliceMut.html#method.par_chunk_by_mut
pub struct ChunkByMut<'data, T, P> {
pred: P,
slice: &'data mut [T],
}
impl<'data, T: fmt::Debug, P> fmt::Debug for ChunkByMut<'data, T, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChunkByMut")
.field("slice", &self.slice)
.finish()
}
}
impl<'data, T, P> ChunkByMut<'data, T, P> {
pub(super) fn new(slice: &'data mut [T], pred: P) -> Self {
Self { pred, slice }
}
}
impl<'data, T, P> ParallelIterator for ChunkByMut<'data, T, P>
where
T: Send,
P: Fn(&T, &T) -> bool + Send + Sync,
{
type Item = &'data mut [T];
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
bridge_unindexed(
ChunkByProducer {
tail: self.slice.len(),
slice: self.slice,
pred: &self.pred,
marker: PhantomData,
},
consumer,
)
}
}

View File

@@ -1,7 +1,6 @@
use crate::iter::plumbing::*; use crate::iter::plumbing::*;
use crate::iter::*; use crate::iter::*;
use crate::math::div_round_up; use crate::math::div_round_up;
use std::cmp;
/// Parallel iterator over immutable non-overlapping chunks of a slice /// Parallel iterator over immutable non-overlapping chunks of a slice
#[derive(Debug)] #[derive(Debug)]
@@ -74,7 +73,7 @@ impl<'data, T: 'data + Sync> Producer for ChunksProducer<'data, T> {
} }
fn split_at(self, index: usize) -> (Self, Self) { fn split_at(self, index: usize) -> (Self, Self) {
let elem_index = cmp::min(index * self.chunk_size, self.slice.len()); let elem_index = Ord::min(index * self.chunk_size, self.slice.len());
let (left, right) = self.slice.split_at(elem_index); let (left, right) = self.slice.split_at(elem_index);
( (
ChunksProducer { ChunksProducer {
@@ -255,7 +254,7 @@ impl<'data, T: 'data + Send> Producer for ChunksMutProducer<'data, T> {
} }
fn split_at(self, index: usize) -> (Self, Self) { fn split_at(self, index: usize) -> (Self, Self) {
let elem_index = cmp::min(index * self.chunk_size, self.slice.len()); let elem_index = Ord::min(index * self.chunk_size, self.slice.len());
let (left, right) = self.slice.split_at_mut(elem_index); let (left, right) = self.slice.split_at_mut(elem_index);
( (
ChunksMutProducer { ChunksMutProducer {

View File

@@ -5,6 +5,7 @@
//! //!
//! [std::slice]: https://doc.rust-lang.org/stable/std/slice/ //! [std::slice]: https://doc.rust-lang.org/stable/std/slice/
mod chunk_by;
mod chunks; mod chunks;
mod mergesort; mod mergesort;
mod quicksort; mod quicksort;
@@ -17,11 +18,12 @@ use self::quicksort::par_quicksort;
use crate::iter::plumbing::*; use crate::iter::plumbing::*;
use crate::iter::*; use crate::iter::*;
use crate::split_producer::*; use crate::split_producer::*;
use std::cmp;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
use std::mem; use std::mem;
pub use self::chunk_by::{ChunkBy, ChunkByMut};
pub use self::chunks::{Chunks, ChunksExact, ChunksExactMut, ChunksMut}; pub use self::chunks::{Chunks, ChunksExact, ChunksExactMut, ChunksMut};
pub use self::rchunks::{RChunks, RChunksExact, RChunksExactMut, RChunksMut}; pub use self::rchunks::{RChunks, RChunksExact, RChunksExactMut, RChunksMut};
@@ -38,11 +40,11 @@ pub trait ParallelSlice<T: Sync> {
/// ///
/// ``` /// ```
/// use rayon::prelude::*; /// use rayon::prelude::*;
/// let smallest = [1, 2, 3, 0, 2, 4, 8, 0, 3, 6, 9] /// let products: Vec<_> = [1, 2, 3, 0, 2, 4, 8, 0, 3, 6, 9]
/// .par_split(|i| *i == 0) /// .par_split(|i| *i == 0)
/// .map(|numbers| numbers.iter().min().unwrap()) /// .map(|numbers| numbers.iter().product::<i32>())
/// .min(); /// .collect();
/// assert_eq!(Some(&1), smallest); /// assert_eq!(products, [6, 64, 162]);
/// ``` /// ```
fn par_split<P>(&self, separator: P) -> Split<'_, T, P> fn par_split<P>(&self, separator: P) -> Split<'_, T, P>
where where
@@ -54,6 +56,29 @@ pub trait ParallelSlice<T: Sync> {
} }
} }
/// Returns a parallel iterator over subslices separated by elements that
/// match the separator, including the matched part as a terminator.
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
/// let lengths: Vec<_> = [1, 2, 3, 0, 2, 4, 8, 0, 3, 6, 9]
/// .par_split_inclusive(|i| *i == 0)
/// .map(|numbers| numbers.len())
/// .collect();
/// assert_eq!(lengths, [4, 4, 3]);
/// ```
fn par_split_inclusive<P>(&self, separator: P) -> SplitInclusive<'_, T, P>
where
P: Fn(&T) -> bool + Sync + Send,
{
SplitInclusive {
slice: self.as_parallel_slice(),
separator,
}
}
/// Returns a parallel iterator over all contiguous windows of length /// Returns a parallel iterator over all contiguous windows of length
/// `window_size`. The windows overlap. /// `window_size`. The windows overlap.
/// ///
@@ -150,6 +175,29 @@ pub trait ParallelSlice<T: Sync> {
assert!(chunk_size != 0, "chunk_size must not be zero"); assert!(chunk_size != 0, "chunk_size must not be zero");
RChunksExact::new(chunk_size, self.as_parallel_slice()) RChunksExact::new(chunk_size, self.as_parallel_slice())
} }
/// Returns a parallel iterator over the slice producing non-overlapping runs
/// of elements using the predicate to separate them.
///
/// The predicate is called on two elements following themselves,
/// it means the predicate is called on `slice[0]` and `slice[1]`
/// then on `slice[1]` and `slice[2]` and so on.
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
/// let chunks: Vec<_> = [1, 2, 2, 3, 3, 3].par_chunk_by(|&x, &y| x == y).collect();
/// assert_eq!(chunks[0], &[1]);
/// assert_eq!(chunks[1], &[2, 2]);
/// assert_eq!(chunks[2], &[3, 3, 3]);
/// ```
fn par_chunk_by<F>(&self, pred: F) -> ChunkBy<'_, T, F>
where
F: Fn(&T, &T) -> bool + Send + Sync,
{
ChunkBy::new(self.as_parallel_slice(), pred)
}
} }
impl<T: Sync> ParallelSlice<T> for [T] { impl<T: Sync> ParallelSlice<T> for [T] {
@@ -187,6 +235,28 @@ pub trait ParallelSliceMut<T: Send> {
} }
} }
/// Returns a parallel iterator over mutable subslices separated by elements
/// that match the separator, including the matched part as a terminator.
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
/// let mut array = [1, 2, 3, 0, 2, 4, 8, 0, 3, 6, 9];
/// array.par_split_inclusive_mut(|i| *i == 0)
/// .for_each(|slice| slice.reverse());
/// assert_eq!(array, [0, 3, 2, 1, 0, 8, 4, 2, 9, 6, 3]);
/// ```
fn par_split_inclusive_mut<P>(&mut self, separator: P) -> SplitInclusiveMut<'_, T, P>
where
P: Fn(&T) -> bool + Sync + Send,
{
SplitInclusiveMut {
slice: self.as_parallel_slice_mut(),
separator,
}
}
/// Returns a parallel iterator over at most `chunk_size` elements of /// Returns a parallel iterator over at most `chunk_size` elements of
/// `self` at a time. The chunks are mutable and do not overlap. /// `self` at a time. The chunks are mutable and do not overlap.
/// ///
@@ -659,6 +729,30 @@ pub trait ParallelSliceMut<T: Send> {
{ {
par_quicksort(self.as_parallel_slice_mut(), |a, b| f(a).lt(&f(b))); par_quicksort(self.as_parallel_slice_mut(), |a, b| f(a).lt(&f(b)));
} }
/// Returns a parallel iterator over the slice producing non-overlapping mutable
/// runs of elements using the predicate to separate them.
///
/// The predicate is called on two elements following themselves,
/// it means the predicate is called on `slice[0]` and `slice[1]`
/// then on `slice[1]` and `slice[2]` and so on.
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
/// let mut xs = [1, 2, 2, 3, 3, 3];
/// let chunks: Vec<_> = xs.par_chunk_by_mut(|&x, &y| x == y).collect();
/// assert_eq!(chunks[0], &mut [1]);
/// assert_eq!(chunks[1], &mut [2, 2]);
/// assert_eq!(chunks[2], &mut [3, 3, 3]);
/// ```
fn par_chunk_by_mut<F>(&mut self, pred: F) -> ChunkByMut<'_, T, F>
where
F: Fn(&T, &T) -> bool + Send + Sync,
{
ChunkByMut::new(self.as_parallel_slice_mut(), pred)
}
} }
impl<T: Send> ParallelSliceMut<T> for [T] { impl<T: Send> ParallelSliceMut<T> for [T] {
@@ -817,7 +911,7 @@ impl<'data, T: 'data + Sync> Producer for WindowsProducer<'data, T> {
} }
fn split_at(self, index: usize) -> (Self, Self) { fn split_at(self, index: usize) -> (Self, Self) {
let left_index = cmp::min(self.slice.len(), index + (self.window_size - 1)); let left_index = Ord::min(self.slice.len(), index + (self.window_size - 1));
let left = &self.slice[..left_index]; let left = &self.slice[..left_index];
let right = &self.slice[index..]; let right = &self.slice[index..];
( (
@@ -932,6 +1026,46 @@ where
} }
} }
/// Parallel iterator over slices separated by a predicate,
/// including the matched part as a terminator.
pub struct SplitInclusive<'data, T, P> {
slice: &'data [T],
separator: P,
}
impl<'data, T, P: Clone> Clone for SplitInclusive<'data, T, P> {
fn clone(&self) -> Self {
SplitInclusive {
separator: self.separator.clone(),
..*self
}
}
}
impl<'data, T: Debug, P> Debug for SplitInclusive<'data, T, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SplitInclusive")
.field("slice", &self.slice)
.finish()
}
}
impl<'data, T, P> ParallelIterator for SplitInclusive<'data, T, P>
where
P: Fn(&T) -> bool + Sync + Send,
T: Sync,
{
type Item = &'data [T];
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let producer = SplitInclusiveProducer::new_incl(self.slice, &self.separator);
bridge_unindexed(producer, consumer)
}
}
/// Implement support for `SplitProducer`. /// Implement support for `SplitProducer`.
impl<'data, T, P> Fissile<P> for &'data [T] impl<'data, T, P> Fissile<P> for &'data [T]
where where
@@ -953,21 +1087,31 @@ where
self[..end].iter().rposition(separator) self[..end].iter().rposition(separator)
} }
fn split_once(self, index: usize) -> (Self, Self) { fn split_once<const INCL: bool>(self, index: usize) -> (Self, Self) {
let (left, right) = self.split_at(index); if INCL {
(left, &right[1..]) // skip the separator // include the separator in the left side
self.split_at(index + 1)
} else {
let (left, right) = self.split_at(index);
(left, &right[1..]) // skip the separator
}
} }
fn fold_splits<F>(self, separator: &P, folder: F, skip_last: bool) -> F fn fold_splits<F, const INCL: bool>(self, separator: &P, folder: F, skip_last: bool) -> F
where where
F: Folder<Self>, F: Folder<Self>,
Self: Send, Self: Send,
{ {
let mut split = self.split(separator); if INCL {
if skip_last { debug_assert!(!skip_last);
split.next_back(); folder.consume_iter(self.split_inclusive(separator))
} else {
let mut split = self.split(separator);
if skip_last {
split.next_back();
}
folder.consume_iter(split)
} }
folder.consume_iter(split)
} }
} }
@@ -1001,6 +1145,37 @@ where
} }
} }
/// Parallel iterator over mutable slices separated by a predicate,
/// including the matched part as a terminator.
pub struct SplitInclusiveMut<'data, T, P> {
slice: &'data mut [T],
separator: P,
}
impl<'data, T: Debug, P> Debug for SplitInclusiveMut<'data, T, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SplitInclusiveMut")
.field("slice", &self.slice)
.finish()
}
}
impl<'data, T, P> ParallelIterator for SplitInclusiveMut<'data, T, P>
where
P: Fn(&T) -> bool + Sync + Send,
T: Send,
{
type Item = &'data mut [T];
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let producer = SplitInclusiveProducer::new_incl(self.slice, &self.separator);
bridge_unindexed(producer, consumer)
}
}
/// Implement support for `SplitProducer`. /// Implement support for `SplitProducer`.
impl<'data, T, P> Fissile<P> for &'data mut [T] impl<'data, T, P> Fissile<P> for &'data mut [T]
where where
@@ -1022,20 +1197,30 @@ where
self[..end].iter().rposition(separator) self[..end].iter().rposition(separator)
} }
fn split_once(self, index: usize) -> (Self, Self) { fn split_once<const INCL: bool>(self, index: usize) -> (Self, Self) {
let (left, right) = self.split_at_mut(index); if INCL {
(left, &mut right[1..]) // skip the separator // include the separator in the left side
self.split_at_mut(index + 1)
} else {
let (left, right) = self.split_at_mut(index);
(left, &mut right[1..]) // skip the separator
}
} }
fn fold_splits<F>(self, separator: &P, folder: F, skip_last: bool) -> F fn fold_splits<F, const INCL: bool>(self, separator: &P, folder: F, skip_last: bool) -> F
where where
F: Folder<Self>, F: Folder<Self>,
Self: Send, Self: Send,
{ {
let mut split = self.split_mut(separator); if INCL {
if skip_last { debug_assert!(!skip_last);
split.next_back(); folder.consume_iter(self.split_inclusive_mut(separator))
} else {
let mut split = self.split_mut(separator);
if skip_last {
split.next_back();
}
folder.consume_iter(split)
} }
folder.consume_iter(split)
} }
} }

View File

@@ -4,17 +4,34 @@
//! The only difference from the original is that calls to `recurse` are executed in parallel using //! The only difference from the original is that calls to `recurse` are executed in parallel using
//! `rayon_core::join`. //! `rayon_core::join`.
use std::cmp; use std::marker::PhantomData;
use std::mem::{self, MaybeUninit}; use std::mem::{self, MaybeUninit};
use std::ptr; use std::ptr;
/// When dropped, copies from `src` into `dest`. /// When dropped, copies from `src` into `dest`.
struct CopyOnDrop<T> { #[must_use]
struct CopyOnDrop<'a, T> {
src: *const T, src: *const T,
dest: *mut T, dest: *mut T,
/// `src` is often a local pointer here, make sure we have appropriate
/// PhantomData so that dropck can protect us.
marker: PhantomData<&'a mut T>,
} }
impl<T> Drop for CopyOnDrop<T> { impl<'a, T> CopyOnDrop<'a, T> {
/// Construct from a source pointer and a destination
/// Assumes dest lives longer than src, since there is no easy way to
/// copy down lifetime information from another pointer
unsafe fn new(src: &'a T, dest: *mut T) -> Self {
CopyOnDrop {
src,
dest,
marker: PhantomData,
}
}
}
impl<T> Drop for CopyOnDrop<'_, T> {
fn drop(&mut self) { fn drop(&mut self) {
// SAFETY: This is a helper class. // SAFETY: This is a helper class.
// Please refer to its usage for correctness. // Please refer to its usage for correctness.
@@ -54,10 +71,7 @@ where
// into the slice. // into the slice.
let tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(0))); let tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(0)));
let v = v.as_mut_ptr(); let v = v.as_mut_ptr();
let mut hole = CopyOnDrop { let mut hole = CopyOnDrop::new(&*tmp, v.add(1));
src: &*tmp,
dest: v.add(1),
};
ptr::copy_nonoverlapping(v.add(1), v.add(0), 1); ptr::copy_nonoverlapping(v.add(1), v.add(0), 1);
for i in 2..len { for i in 2..len {
@@ -103,10 +117,7 @@ where
// into the slice. // into the slice.
let tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(len - 1))); let tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(len - 1)));
let v = v.as_mut_ptr(); let v = v.as_mut_ptr();
let mut hole = CopyOnDrop { let mut hole = CopyOnDrop::new(&*tmp, v.add(len - 2));
src: &*tmp,
dest: v.add(len - 2),
};
ptr::copy_nonoverlapping(v.add(len - 2), v.add(len - 1), 1); ptr::copy_nonoverlapping(v.add(len - 2), v.add(len - 1), 1);
for i in (0..len - 2).rev() { for i in (0..len - 2).rev() {
@@ -366,7 +377,7 @@ where
} }
// Number of out-of-order elements to swap between the left and right side. // Number of out-of-order elements to swap between the left and right side.
let count = cmp::min(width(start_l, end_l), width(start_r, end_r)); let count = Ord::min(width(start_l, end_l), width(start_r, end_r));
if count > 0 { if count > 0 {
macro_rules! left { macro_rules! left {
@@ -510,10 +521,7 @@ where
// SAFETY: `pivot` is a reference to the first element of `v`, so `ptr::read` is safe. // SAFETY: `pivot` is a reference to the first element of `v`, so `ptr::read` is safe.
let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) });
let _pivot_guard = CopyOnDrop { let _pivot_guard = unsafe { CopyOnDrop::new(&*tmp, pivot) };
src: &*tmp,
dest: pivot,
};
let pivot = &*tmp; let pivot = &*tmp;
// Find the first pair of out-of-order elements. // Find the first pair of out-of-order elements.
@@ -569,10 +577,7 @@ where
// operation panics, the pivot will be automatically written back into the slice. // operation panics, the pivot will be automatically written back into the slice.
// SAFETY: The pointer here is valid because it is obtained from a reference to a slice. // SAFETY: The pointer here is valid because it is obtained from a reference to a slice.
let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) });
let _pivot_guard = CopyOnDrop { let _pivot_guard = unsafe { CopyOnDrop::new(&*tmp, pivot) };
src: &*tmp,
dest: pivot,
};
let pivot = &*tmp; let pivot = &*tmp;
// Now partition the slice. // Now partition the slice.
@@ -805,7 +810,7 @@ where
// Partition the slice. // Partition the slice.
let (mid, was_p) = partition(v, pivot, is_less); let (mid, was_p) = partition(v, pivot, is_less);
was_balanced = cmp::min(mid, len - mid) >= len / 8; was_balanced = Ord::min(mid, len - mid) >= len / 8;
was_partitioned = was_p; was_partitioned = was_p;
// Split the slice into `left`, `pivot`, and `right`. // Split the slice into `left`, `pivot`, and `right`.
@@ -813,7 +818,7 @@ where
let (pivot, right) = right.split_at_mut(1); let (pivot, right) = right.split_at_mut(1);
let pivot = &mut pivot[0]; let pivot = &mut pivot[0];
if cmp::max(left.len(), right.len()) <= MAX_SEQUENTIAL { if Ord::max(left.len(), right.len()) <= MAX_SEQUENTIAL {
// Recurse into the shorter side only in order to minimize the total number of recursive // Recurse into the shorter side only in order to minimize the total number of recursive
// calls and consume less stack space. Then just continue with the longer side (this is // calls and consume less stack space. Then just continue with the longer side (this is
// akin to tail recursion). // akin to tail recursion).

View File

@@ -5,6 +5,7 @@ use rand::distributions::Uniform;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use std::cmp::Ordering::{Equal, Greater, Less}; use std::cmp::Ordering::{Equal, Greater, Less};
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
macro_rules! sort { macro_rules! sort {
($f:ident, $name:ident) => { ($f:ident, $name:ident) => {
@@ -168,3 +169,48 @@ fn test_par_rchunks_exact_mut_remainder() {
assert_eq!(c.take_remainder(), &[]); assert_eq!(c.take_remainder(), &[]);
assert_eq!(c.len(), 2); assert_eq!(c.len(), 2);
} }
#[test]
fn slice_chunk_by() {
let v: Vec<_> = (0..1000).collect();
assert_eq!(v[..0].par_chunk_by(|_, _| todo!()).count(), 0);
assert_eq!(v[..1].par_chunk_by(|_, _| todo!()).count(), 1);
assert_eq!(v[..2].par_chunk_by(|_, _| true).count(), 1);
assert_eq!(v[..2].par_chunk_by(|_, _| false).count(), 2);
let count = AtomicUsize::new(0);
let par: Vec<_> = v
.par_chunk_by(|x, y| {
count.fetch_add(1, Relaxed);
(x % 10 < 3) == (y % 10 < 3)
})
.collect();
assert_eq!(count.into_inner(), v.len() - 1);
let seq: Vec<_> = v.chunk_by(|x, y| (x % 10 < 3) == (y % 10 < 3)).collect();
assert_eq!(par, seq);
}
#[test]
fn slice_chunk_by_mut() {
let mut v: Vec<_> = (0..1000).collect();
assert_eq!(v[..0].par_chunk_by_mut(|_, _| todo!()).count(), 0);
assert_eq!(v[..1].par_chunk_by_mut(|_, _| todo!()).count(), 1);
assert_eq!(v[..2].par_chunk_by_mut(|_, _| true).count(), 1);
assert_eq!(v[..2].par_chunk_by_mut(|_, _| false).count(), 2);
let mut v2 = v.clone();
let count = AtomicUsize::new(0);
let par: Vec<_> = v
.par_chunk_by_mut(|x, y| {
count.fetch_add(1, Relaxed);
(x % 10 < 3) == (y % 10 < 3)
})
.collect();
assert_eq!(count.into_inner(), v2.len() - 1);
let seq: Vec<_> = v2
.chunk_by_mut(|x, y| (x % 10 < 3) == (y % 10 < 3))
.collect();
assert_eq!(par, seq);
}

View File

@@ -5,7 +5,7 @@
use crate::iter::plumbing::{Folder, UnindexedProducer}; use crate::iter::plumbing::{Folder, UnindexedProducer};
/// Common producer for splitting on a predicate. /// Common producer for splitting on a predicate.
pub(super) struct SplitProducer<'p, P, V> { pub(super) struct SplitProducer<'p, P, V, const INCL: bool = false> {
data: V, data: V,
separator: &'p P, separator: &'p P,
@@ -13,14 +13,16 @@ pub(super) struct SplitProducer<'p, P, V> {
tail: usize, tail: usize,
} }
pub(super) type SplitInclusiveProducer<'p, P, V> = SplitProducer<'p, P, V, true>;
/// Helper trait so `&str`, `&[T]`, and `&mut [T]` can share `SplitProducer`. /// Helper trait so `&str`, `&[T]`, and `&mut [T]` can share `SplitProducer`.
pub(super) trait Fissile<P>: Sized { pub(super) trait Fissile<P>: Sized {
fn length(&self) -> usize; fn length(&self) -> usize;
fn midpoint(&self, end: usize) -> usize; fn midpoint(&self, end: usize) -> usize;
fn find(&self, separator: &P, start: usize, end: usize) -> Option<usize>; fn find(&self, separator: &P, start: usize, end: usize) -> Option<usize>;
fn rfind(&self, separator: &P, end: usize) -> Option<usize>; fn rfind(&self, separator: &P, end: usize) -> Option<usize>;
fn split_once(self, index: usize) -> (Self, Self); fn split_once<const INCL: bool>(self, index: usize) -> (Self, Self);
fn fold_splits<F>(self, separator: &P, folder: F, skip_last: bool) -> F fn fold_splits<F, const INCL: bool>(self, separator: &P, folder: F, skip_last: bool) -> F
where where
F: Folder<Self>, F: Folder<Self>,
Self: Send; Self: Send;
@@ -37,7 +39,25 @@ where
separator, separator,
} }
} }
}
impl<'p, P, V> SplitInclusiveProducer<'p, P, V>
where
V: Fissile<P> + Send,
{
pub(super) fn new_incl(data: V, separator: &'p P) -> Self {
SplitProducer {
tail: data.length(),
data,
separator,
}
}
}
impl<'p, P, V, const INCL: bool> SplitProducer<'p, P, V, INCL>
where
V: Fissile<P> + Send,
{
/// Common `fold_with` implementation, integrating `SplitTerminator`'s /// Common `fold_with` implementation, integrating `SplitTerminator`'s
/// need to sometimes skip its final empty item. /// need to sometimes skip its final empty item.
pub(super) fn fold_with<F>(self, folder: F, skip_last: bool) -> F pub(super) fn fold_with<F>(self, folder: F, skip_last: bool) -> F
@@ -52,12 +72,12 @@ where
if tail == data.length() { if tail == data.length() {
// No tail section, so just let `fold_splits` handle it. // No tail section, so just let `fold_splits` handle it.
data.fold_splits(separator, folder, skip_last) data.fold_splits::<F, INCL>(separator, folder, skip_last)
} else if let Some(index) = data.rfind(separator, tail) { } else if let Some(index) = data.rfind(separator, tail) {
// We found the last separator to complete the tail, so // We found the last separator to complete the tail, so
// end with that slice after `fold_splits` finds the rest. // end with that slice after `fold_splits` finds the rest.
let (left, right) = data.split_once(index); let (left, right) = data.split_once::<INCL>(index);
let folder = left.fold_splits(separator, folder, false); let folder = left.fold_splits::<F, INCL>(separator, folder, false);
if skip_last || folder.full() { if skip_last || folder.full() {
folder folder
} else { } else {
@@ -74,7 +94,7 @@ where
} }
} }
impl<'p, P, V> UnindexedProducer for SplitProducer<'p, P, V> impl<'p, P, V, const INCL: bool> UnindexedProducer for SplitProducer<'p, P, V, INCL>
where where
V: Fissile<P> + Send, V: Fissile<P> + Send,
P: Sync, P: Sync,
@@ -91,7 +111,7 @@ where
if let Some(index) = index { if let Some(index) = index {
let len = self.data.length(); let len = self.data.length();
let (left, right) = self.data.split_once(index); let (left, right) = self.data.split_once::<INCL>(index);
let (left_tail, right_tail) = if index < mid { let (left_tail, right_tail) = if index < mid {
// If we scanned backwards to find the separator, everything in // If we scanned backwards to find the separator, everything in

View File

@@ -6,8 +6,8 @@
//! Note: [`ParallelString::par_split()`] and [`par_split_terminator()`] //! Note: [`ParallelString::par_split()`] and [`par_split_terminator()`]
//! reference a `Pattern` trait which is not visible outside this crate. //! reference a `Pattern` trait which is not visible outside this crate.
//! This trait is intentionally kept private, for use only by Rayon itself. //! This trait is intentionally kept private, for use only by Rayon itself.
//! It is implemented for `char`, `&[char]`, and any function or closure //! It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
//! `F: Fn(char) -> bool + Sync + Send`. //! and any function or closure `F: Fn(char) -> bool + Sync + Send`.
//! //!
//! [`ParallelString::par_split()`]: trait.ParallelString.html#method.par_split //! [`ParallelString::par_split()`]: trait.ParallelString.html#method.par_split
//! [`par_split_terminator()`]: trait.ParallelString.html#method.par_split_terminator //! [`par_split_terminator()`]: trait.ParallelString.html#method.par_split_terminator
@@ -140,8 +140,8 @@ pub trait ParallelString {
/// given character or predicate, similar to `str::split`. /// given character or predicate, similar to `str::split`.
/// ///
/// Note: the `Pattern` trait is private, for use only by Rayon itself. /// Note: the `Pattern` trait is private, for use only by Rayon itself.
/// It is implemented for `char`, `&[char]`, and any function or closure /// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
/// `F: Fn(char) -> bool + Sync + Send`. /// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
/// ///
/// # Examples /// # Examples
/// ///
@@ -157,14 +157,35 @@ pub trait ParallelString {
Split::new(self.as_parallel_string(), separator) Split::new(self.as_parallel_string(), separator)
} }
/// Returns a parallel iterator over substrings separated by a
/// given character or predicate, keeping the matched part as a terminator
/// of the substring similar to `str::split_inclusive`.
///
/// Note: the `Pattern` trait is private, for use only by Rayon itself.
/// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
/// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
/// let lines: Vec<_> = "Mary had a little lamb\nlittle lamb\nlittle lamb."
/// .par_split_inclusive('\n')
/// .collect();
/// assert_eq!(lines, ["Mary had a little lamb\n", "little lamb\n", "little lamb."]);
/// ```
fn par_split_inclusive<P: Pattern>(&self, separator: P) -> SplitInclusive<'_, P> {
SplitInclusive::new(self.as_parallel_string(), separator)
}
/// Returns a parallel iterator over substrings terminated by a /// Returns a parallel iterator over substrings terminated by a
/// given character or predicate, similar to `str::split_terminator`. /// given character or predicate, similar to `str::split_terminator`.
/// It's equivalent to `par_split`, except it doesn't produce an empty /// It's equivalent to `par_split`, except it doesn't produce an empty
/// substring after a trailing terminator. /// substring after a trailing terminator.
/// ///
/// Note: the `Pattern` trait is private, for use only by Rayon itself. /// Note: the `Pattern` trait is private, for use only by Rayon itself.
/// It is implemented for `char`, `&[char]`, and any function or closure /// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
/// `F: Fn(char) -> bool + Sync + Send`. /// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
/// ///
/// # Examples /// # Examples
/// ///
@@ -203,6 +224,8 @@ pub trait ParallelString {
/// ///
/// As with `str::split_whitespace`, 'whitespace' is defined according to /// As with `str::split_whitespace`, 'whitespace' is defined according to
/// the terms of the Unicode Derived Core Property `White_Space`. /// the terms of the Unicode Derived Core Property `White_Space`.
/// If you only want to split on ASCII whitespace instead, use
/// [`par_split_ascii_whitespace`][`ParallelString::par_split_ascii_whitespace`].
/// ///
/// # Examples /// # Examples
/// ///
@@ -213,16 +236,71 @@ pub trait ParallelString {
/// .max_by_key(|word| word.len()); /// .max_by_key(|word| word.len());
/// assert_eq!(Some("longest"), longest); /// assert_eq!(Some("longest"), longest);
/// ``` /// ```
///
/// All kinds of whitespace are considered:
///
/// ```
/// use rayon::prelude::*;
/// let words: Vec<&str> = " Mary had\ta\u{2009}little \n\t lamb"
/// .par_split_whitespace()
/// .collect();
/// assert_eq!(words, ["Mary", "had", "a", "little", "lamb"]);
/// ```
///
/// If the string is empty or all whitespace, the iterator yields no string slices:
///
/// ```
/// use rayon::prelude::*;
/// assert_eq!("".par_split_whitespace().count(), 0);
/// assert_eq!(" ".par_split_whitespace().count(), 0);
/// ```
fn par_split_whitespace(&self) -> SplitWhitespace<'_> { fn par_split_whitespace(&self) -> SplitWhitespace<'_> {
SplitWhitespace(self.as_parallel_string()) SplitWhitespace(self.as_parallel_string())
} }
/// Returns a parallel iterator over the sub-slices of a string that are
/// separated by any amount of ASCII whitespace.
///
/// To split by Unicode `White_Space` instead, use
/// [`par_split_whitespace`][`ParallelString::par_split_whitespace`].
///
/// # Examples
///
/// ```
/// use rayon::prelude::*;
/// let longest = "which is the longest word?"
/// .par_split_ascii_whitespace()
/// .max_by_key(|word| word.len());
/// assert_eq!(Some("longest"), longest);
/// ```
///
/// All kinds of ASCII whitespace are considered, but not Unicode `White_Space`:
///
/// ```
/// use rayon::prelude::*;
/// let words: Vec<&str> = " Mary had\ta\u{2009}little \n\t lamb"
/// .par_split_ascii_whitespace()
/// .collect();
/// assert_eq!(words, ["Mary", "had", "a\u{2009}little", "lamb"]);
/// ```
///
/// If the string is empty or all ASCII whitespace, the iterator yields no string slices:
///
/// ```
/// use rayon::prelude::*;
/// assert_eq!("".par_split_whitespace().count(), 0);
/// assert_eq!(" ".par_split_whitespace().count(), 0);
/// ```
fn par_split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> {
SplitAsciiWhitespace(self.as_parallel_string())
}
/// Returns a parallel iterator over substrings that match a /// Returns a parallel iterator over substrings that match a
/// given character or predicate, similar to `str::matches`. /// given character or predicate, similar to `str::matches`.
/// ///
/// Note: the `Pattern` trait is private, for use only by Rayon itself. /// Note: the `Pattern` trait is private, for use only by Rayon itself.
/// It is implemented for `char`, `&[char]`, and any function or closure /// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
/// `F: Fn(char) -> bool + Sync + Send`. /// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
/// ///
/// # Examples /// # Examples
/// ///
@@ -245,8 +323,8 @@ pub trait ParallelString {
/// or predicate, with their positions, similar to `str::match_indices`. /// or predicate, with their positions, similar to `str::match_indices`.
/// ///
/// Note: the `Pattern` trait is private, for use only by Rayon itself. /// Note: the `Pattern` trait is private, for use only by Rayon itself.
/// It is implemented for `char`, `&[char]`, and any function or closure /// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
/// `F: Fn(char) -> bool + Sync + Send`. /// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
/// ///
/// # Examples /// # Examples
/// ///
@@ -291,6 +369,9 @@ mod private {
fn rfind_in(&self, haystack: &str) -> Option<usize>; fn rfind_in(&self, haystack: &str) -> Option<usize>;
fn is_suffix_of(&self, haystack: &str) -> bool; fn is_suffix_of(&self, haystack: &str) -> bool;
fn fold_splits<'ch, F>(&self, haystack: &'ch str, folder: F, skip_last: bool) -> F fn fold_splits<'ch, F>(&self, haystack: &'ch str, folder: F, skip_last: bool) -> F
where
F: Folder<&'ch str>;
fn fold_inclusive_splits<'ch, F>(&self, haystack: &'ch str, folder: F) -> F
where where
F: Folder<&'ch str>; F: Folder<&'ch str>;
fn fold_matches<'ch, F>(&self, haystack: &'ch str, folder: F) -> F fn fold_matches<'ch, F>(&self, haystack: &'ch str, folder: F) -> F
@@ -338,6 +419,13 @@ macro_rules! impl_pattern {
folder.consume_iter(split) folder.consume_iter(split)
} }
fn fold_inclusive_splits<'ch, F>(&$self, chars: &'ch str, folder: F) -> F
where
F: Folder<&'ch str>,
{
folder.consume_iter(chars.split_inclusive($pattern))
}
fn fold_matches<'ch, F>(&$self, chars: &'ch str, folder: F) -> F fn fold_matches<'ch, F>(&$self, chars: &'ch str, folder: F) -> F
where where
F: Folder<&'ch str>, F: Folder<&'ch str>,
@@ -362,6 +450,17 @@ impl Pattern for &[char] {
impl_pattern!(&self => *self); impl_pattern!(&self => *self);
} }
// TODO (MSRV 1.75): use `*self` for array patterns too.
// - Needs `DoubleEndedSearcher` so `split.next_back()` works.
impl<const N: usize> Pattern for [char; N] {
impl_pattern!(&self => self.as_slice());
}
impl<const N: usize> Pattern for &[char; N] {
impl_pattern!(&self => self.as_slice());
}
impl<FN: Sync + Send + Fn(char) -> bool> Pattern for FN { impl<FN: Sync + Send + Fn(char) -> bool> Pattern for FN {
impl_pattern!(&self => self); impl_pattern!(&self => self);
} }
@@ -600,18 +699,56 @@ impl<'ch, P: Pattern> Fissile<P> for &'ch str {
separator.rfind_in(&self[..end]) separator.rfind_in(&self[..end])
} }
fn split_once(self, index: usize) -> (Self, Self) { fn split_once<const INCL: bool>(self, index: usize) -> (Self, Self) {
let (left, right) = self.split_at(index); if INCL {
let mut right_iter = right.chars(); // include the separator in the left side
right_iter.next(); // skip the separator let separator = self[index..].chars().next().unwrap();
(left, right_iter.as_str()) self.split_at(index + separator.len_utf8())
} else {
let (left, right) = self.split_at(index);
let mut right_iter = right.chars();
right_iter.next(); // skip the separator
(left, right_iter.as_str())
}
} }
fn fold_splits<F>(self, separator: &P, folder: F, skip_last: bool) -> F fn fold_splits<F, const INCL: bool>(self, separator: &P, folder: F, skip_last: bool) -> F
where where
F: Folder<Self>, F: Folder<Self>,
{ {
separator.fold_splits(self, folder, skip_last) if INCL {
debug_assert!(!skip_last);
separator.fold_inclusive_splits(self, folder)
} else {
separator.fold_splits(self, folder, skip_last)
}
}
}
// /////////////////////////////////////////////////////////////////////////
/// Parallel iterator over substrings separated by a pattern
#[derive(Debug, Clone)]
pub struct SplitInclusive<'ch, P: Pattern> {
chars: &'ch str,
separator: P,
}
impl<'ch, P: Pattern> SplitInclusive<'ch, P> {
fn new(chars: &'ch str, separator: P) -> Self {
SplitInclusive { chars, separator }
}
}
impl<'ch, P: Pattern> ParallelIterator for SplitInclusive<'ch, P> {
type Item = &'ch str;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
let producer = SplitInclusiveProducer::new_incl(self.chars, &self.separator);
bridge_unindexed(producer, consumer)
} }
} }
@@ -733,6 +870,31 @@ impl<'ch> ParallelIterator for SplitWhitespace<'ch> {
// ///////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////
/// Parallel iterator over substrings separated by ASCII whitespace
#[derive(Debug, Clone)]
pub struct SplitAsciiWhitespace<'ch>(&'ch str);
#[inline]
fn is_ascii_whitespace(c: char) -> bool {
c.is_ascii_whitespace()
}
impl<'ch> ParallelIterator for SplitAsciiWhitespace<'ch> {
type Item = &'ch str;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
self.0
.par_split(is_ascii_whitespace)
.filter(not_empty)
.drive_unindexed(consumer)
}
}
// /////////////////////////////////////////////////////////////////////////
/// Parallel iterator over substrings that match a pattern /// Parallel iterator over substrings that match a pattern
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Matches<'ch, P: Pattern> { pub struct Matches<'ch, P: Pattern> {

View File

@@ -225,8 +225,9 @@ impl<'data, T: 'data + Send> Producer for DrainProducer<'data, T> {
impl<'data, T: 'data + Send> Drop for DrainProducer<'data, T> { impl<'data, T: 'data + Send> Drop for DrainProducer<'data, T> {
fn drop(&mut self) { fn drop(&mut self) {
// use `Drop for [T]` // extract the slice so we can use `Drop for [T]`
unsafe { ptr::drop_in_place(self.slice) }; let slice_ptr: *mut [T] = mem::take::<&'data mut [T]>(&mut self.slice);
unsafe { ptr::drop_in_place::<[T]>(slice_ptr) };
} }
} }
@@ -276,7 +277,7 @@ impl<'data, T: 'data> iter::FusedIterator for SliceDrain<'data, T> {}
impl<'data, T: 'data> Drop for SliceDrain<'data, T> { impl<'data, T: 'data> Drop for SliceDrain<'data, T> {
fn drop(&mut self) { fn drop(&mut self) {
// extract the iterator so we can use `Drop for [T]` // extract the iterator so we can use `Drop for [T]`
let iter = mem::replace(&mut self.iter, [].iter_mut()); let slice_ptr: *mut [T] = mem::replace(&mut self.iter, [].iter_mut()).into_slice();
unsafe { ptr::drop_in_place(iter.into_slice()) }; unsafe { ptr::drop_in_place::<[T]>(slice_ptr) };
} }
} }

View File

@@ -1,5 +1,4 @@
use rayon::prelude::*; use rayon::prelude::*;
use std::char;
#[test] #[test]
fn half_open_correctness() { fn half_open_correctness() {

View File

@@ -10,6 +10,13 @@ where
assert_eq!(a, b); assert_eq!(a, b);
} }
fn check_count<I>(iter: I)
where
I: ParallelIterator + Clone,
{
assert_eq!(iter.clone().count(), iter.count());
}
#[test] #[test]
fn clone_binary_heap() { fn clone_binary_heap() {
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
@@ -92,20 +99,24 @@ fn clone_str() {
check(s.par_chars()); check(s.par_chars());
check(s.par_lines()); check(s.par_lines());
check(s.par_split('\n')); check(s.par_split('\n'));
check(s.par_split_inclusive('\n'));
check(s.par_split_terminator('\n')); check(s.par_split_terminator('\n'));
check(s.par_split_whitespace()); check(s.par_split_whitespace());
check(s.par_split_ascii_whitespace());
} }
#[test] #[test]
fn clone_vec() { fn clone_vec() {
let v: Vec<_> = (0..1000).collect(); let v: Vec<_> = (0..1000).collect();
check(v.par_iter()); check(v.par_iter());
check(v.par_chunk_by(i32::eq));
check(v.par_chunks(42)); check(v.par_chunks(42));
check(v.par_chunks_exact(42)); check(v.par_chunks_exact(42));
check(v.par_rchunks(42)); check(v.par_rchunks(42));
check(v.par_rchunks_exact(42)); check(v.par_rchunks_exact(42));
check(v.par_windows(42)); check(v.par_windows(42));
check(v.par_split(|x| x % 3 == 0)); check(v.par_split(|x| x % 3 == 0));
check(v.par_split_inclusive(|x| x % 3 == 0));
check(v.into_par_iter()); check(v.into_par_iter());
} }
@@ -118,6 +129,8 @@ fn clone_array() {
#[test] #[test]
fn clone_adaptors() { fn clone_adaptors() {
let v: Vec<_> = (0..1000).map(Some).collect(); let v: Vec<_> = (0..1000).map(Some).collect();
check(v.par_iter().by_exponential_blocks());
check(v.par_iter().by_uniform_blocks(100));
check(v.par_iter().chain(&v)); check(v.par_iter().chain(&v));
check(v.par_iter().cloned()); check(v.par_iter().cloned());
check(v.par_iter().copied()); check(v.par_iter().copied());
@@ -150,8 +163,10 @@ fn clone_adaptors() {
check(v.par_iter().panic_fuse()); check(v.par_iter().panic_fuse());
check(v.par_iter().positions(|_| true)); check(v.par_iter().positions(|_| true));
check(v.par_iter().rev()); check(v.par_iter().rev());
check(v.par_iter().skip(1)); check(v.par_iter().skip(42));
check(v.par_iter().take(1)); check(v.par_iter().skip_any_while(|_| false));
check(v.par_iter().take(42));
check(v.par_iter().take_any_while(|_| true));
check(v.par_iter().cloned().while_some()); check(v.par_iter().cloned().while_some());
check(v.par_iter().with_max_len(1)); check(v.par_iter().with_max_len(1));
check(v.par_iter().with_min_len(1)); check(v.par_iter().with_min_len(1));
@@ -160,6 +175,13 @@ fn clone_adaptors() {
check(v.par_iter().step_by(2)); check(v.par_iter().step_by(2));
} }
#[test]
fn clone_counted_adaptors() {
let v: Vec<_> = (0..1000).collect();
check_count(v.par_iter().skip_any(42));
check_count(v.par_iter().take_any(42));
}
#[test] #[test]
fn clone_empty() { fn clone_empty() {
check(rayon::iter::empty::<i32>()); check(rayon::iter::empty::<i32>());

View File

@@ -6,6 +6,7 @@ use std::sync::atomic::Ordering;
use std::sync::Mutex; use std::sync::Mutex;
#[test] #[test]
#[cfg_attr(not(panic = "unwind"), ignore)]
fn collect_drop_on_unwind() { fn collect_drop_on_unwind() {
struct Recorddrop<'a>(i64, &'a Mutex<Vec<i64>>); struct Recorddrop<'a>(i64, &'a Mutex<Vec<i64>>);
@@ -61,6 +62,7 @@ fn collect_drop_on_unwind() {
} }
#[test] #[test]
#[cfg_attr(not(panic = "unwind"), ignore)]
fn collect_drop_on_unwind_zst() { fn collect_drop_on_unwind_zst() {
static INSERTS: AtomicUsize = AtomicUsize::new(0); static INSERTS: AtomicUsize = AtomicUsize::new(0);
static DROPS: AtomicUsize = AtomicUsize::new(0); static DROPS: AtomicUsize = AtomicUsize::new(0);

View File

@@ -2,6 +2,7 @@ use rayon::prelude::*;
use rayon::ThreadPoolBuilder; use rayon::ThreadPoolBuilder;
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn cross_pool_busy() { fn cross_pool_busy() {
let pool1 = ThreadPoolBuilder::new().num_threads(1).build().unwrap(); let pool1 = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
let pool2 = ThreadPoolBuilder::new().num_threads(1).build().unwrap(); let pool2 = ThreadPoolBuilder::new().num_threads(1).build().unwrap();

View File

@@ -104,8 +104,10 @@ fn debug_str() {
check(s.par_chars()); check(s.par_chars());
check(s.par_lines()); check(s.par_lines());
check(s.par_split('\n')); check(s.par_split('\n'));
check(s.par_split_inclusive('\n'));
check(s.par_split_terminator('\n')); check(s.par_split_terminator('\n'));
check(s.par_split_whitespace()); check(s.par_split_whitespace());
check(s.par_split_ascii_whitespace());
} }
#[test] #[test]
@@ -119,6 +121,8 @@ fn debug_vec() {
let mut v: Vec<_> = (0..10).collect(); let mut v: Vec<_> = (0..10).collect();
check(v.par_iter()); check(v.par_iter());
check(v.par_iter_mut()); check(v.par_iter_mut());
check(v.par_chunk_by(i32::eq));
check(v.par_chunk_by_mut(i32::eq));
check(v.par_chunks(42)); check(v.par_chunks(42));
check(v.par_chunks_exact(42)); check(v.par_chunks_exact(42));
check(v.par_chunks_mut(42)); check(v.par_chunks_mut(42));
@@ -129,7 +133,9 @@ fn debug_vec() {
check(v.par_rchunks_exact_mut(42)); check(v.par_rchunks_exact_mut(42));
check(v.par_windows(42)); check(v.par_windows(42));
check(v.par_split(|x| x % 3 == 0)); check(v.par_split(|x| x % 3 == 0));
check(v.par_split_inclusive(|x| x % 3 == 0));
check(v.par_split_mut(|x| x % 3 == 0)); check(v.par_split_mut(|x| x % 3 == 0));
check(v.par_split_inclusive_mut(|x| x % 3 == 0));
check(v.par_drain(..)); check(v.par_drain(..));
check(v.into_par_iter()); check(v.into_par_iter());
} }
@@ -143,6 +149,8 @@ fn debug_array() {
#[test] #[test]
fn debug_adaptors() { fn debug_adaptors() {
let v: Vec<_> = (0..10).collect(); let v: Vec<_> = (0..10).collect();
check(v.par_iter().by_exponential_blocks());
check(v.par_iter().by_uniform_blocks(5));
check(v.par_iter().chain(&v)); check(v.par_iter().chain(&v));
check(v.par_iter().cloned()); check(v.par_iter().cloned());
check(v.par_iter().copied()); check(v.par_iter().copied());
@@ -172,7 +180,11 @@ fn debug_adaptors() {
check(v.par_iter().positions(|_| true)); check(v.par_iter().positions(|_| true));
check(v.par_iter().rev()); check(v.par_iter().rev());
check(v.par_iter().skip(1)); check(v.par_iter().skip(1));
check(v.par_iter().skip_any(1));
check(v.par_iter().skip_any_while(|_| false));
check(v.par_iter().take(1)); check(v.par_iter().take(1));
check(v.par_iter().take_any(1));
check(v.par_iter().take_any_while(|_| true));
check(v.par_iter().map(Some).while_some()); check(v.par_iter().map(Some).while_some());
check(v.par_iter().with_max_len(1)); check(v.par_iter().with_max_len(1));
check(v.par_iter().with_min_len(1)); check(v.par_iter().with_min_len(1));

View File

@@ -20,6 +20,7 @@ fn iter_panic() {
} }
#[test] #[test]
#[cfg_attr(not(panic = "unwind"), ignore)]
fn iter_panic_fuse() { fn iter_panic_fuse() {
// We only use a single thread in order to make the behavior // We only use a single thread in order to make the behavior
// of 'panic_fuse' deterministic // of 'panic_fuse' deterministic

View File

@@ -4,6 +4,7 @@ use rayon::prelude::*;
use rayon::*; use rayon::*;
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn named_threads() { fn named_threads() {
ThreadPoolBuilder::new() ThreadPoolBuilder::new()
.thread_name(|i| format!("hello-name-test-{}", i)) .thread_name(|i| format!("hello-name-test-{}", i))

View File

@@ -68,7 +68,14 @@ fn two_threads<F: Send + FnOnce() -> R, R: Send>(f: F) -> R {
} }
#[test] #[test]
#[cfg_attr(not(target_pointer_width = "64"), ignore)] #[cfg_attr(
any(
not(target_pointer_width = "64"),
target_os = "emscripten",
target_family = "wasm"
),
ignore
)]
fn find_last_octillion() { fn find_last_octillion() {
// It would be nice if `find_last` could prioritize the later splits, // It would be nice if `find_last` could prioritize the later splits,
// basically flipping the `join` args, without needing indexed `rev`. // basically flipping the `join` args, without needing indexed `rev`.
@@ -78,32 +85,49 @@ fn find_last_octillion() {
} }
#[test] #[test]
#[cfg_attr(not(target_pointer_width = "64"), ignore)] #[cfg_attr(
any(
not(target_pointer_width = "64"),
target_os = "emscripten",
target_family = "wasm"
),
ignore
)]
fn find_last_octillion_inclusive() { fn find_last_octillion_inclusive() {
let x = two_threads(|| octillion_inclusive().find_last(|_| true)); let x = two_threads(|| octillion_inclusive().find_last(|_| true));
assert_eq!(x, Some(OCTILLION)); assert_eq!(x, Some(OCTILLION));
} }
#[test] #[test]
#[cfg_attr(not(target_pointer_width = "64"), ignore)] #[cfg_attr(
any(
not(target_pointer_width = "64"),
target_os = "emscripten",
target_family = "wasm"
),
ignore
)]
fn find_last_octillion_flat() { fn find_last_octillion_flat() {
let x = two_threads(|| octillion_flat().find_last(|_| true)); let x = two_threads(|| octillion_flat().find_last(|_| true));
assert_eq!(x, Some(OCTILLION - 1)); assert_eq!(x, Some(OCTILLION - 1));
} }
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn find_any_octillion() { fn find_any_octillion() {
let x = two_threads(|| octillion().find_any(|x| *x > OCTILLION / 2)); let x = two_threads(|| octillion().find_any(|x| *x > OCTILLION / 2));
assert!(x.is_some()); assert!(x.is_some());
} }
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn find_any_octillion_flat() { fn find_any_octillion_flat() {
let x = two_threads(|| octillion_flat().find_any(|x| *x > OCTILLION / 2)); let x = two_threads(|| octillion_flat().find_any(|x| *x > OCTILLION / 2));
assert!(x.is_some()); assert!(x.is_some());
} }
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn filter_find_any_octillion() { fn filter_find_any_octillion() {
let x = two_threads(|| { let x = two_threads(|| {
octillion() octillion()
@@ -114,6 +138,7 @@ fn filter_find_any_octillion() {
} }
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn filter_find_any_octillion_flat() { fn filter_find_any_octillion_flat() {
let x = two_threads(|| { let x = two_threads(|| {
octillion_flat() octillion_flat()
@@ -124,6 +149,7 @@ fn filter_find_any_octillion_flat() {
} }
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn fold_find_any_octillion_flat() { fn fold_find_any_octillion_flat() {
let x = two_threads(|| octillion_flat().fold(|| (), |_, _| ()).find_any(|_| true)); let x = two_threads(|| octillion_flat().fold(|| (), |_, _| ()).find_any(|_| true));
assert!(x.is_some()); assert!(x.is_some());

View File

@@ -4,6 +4,7 @@ use std::iter::once_with;
const N: usize = 100_000; const N: usize = 100_000;
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn par_bridge_recursion() { fn par_bridge_recursion() {
let pool = rayon::ThreadPoolBuilder::new() let pool = rayon::ThreadPoolBuilder::new()
.num_threads(10) .num_threads(10)

View File

@@ -116,20 +116,17 @@ fn array() {
#[test] #[test]
fn empty() { fn empty() {
let v = vec![42]; check(&[], rayon::iter::empty::<i32>);
check(&v[..0], rayon::iter::empty);
} }
#[test] #[test]
fn once() { fn once() {
let v = vec![42]; check(&[42], || rayon::iter::once(42));
check(&v, || rayon::iter::once(42));
} }
#[test] #[test]
fn option() { fn option() {
let v = vec![42]; check(&[42], || Some(42));
check(&v, || Some(42));
} }
#[test] #[test]

View File

@@ -2,17 +2,20 @@ use rand::distributions::Uniform;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use rayon::prelude::*; use rayon::prelude::*;
use std::cell::Cell; use std::cell::Cell;
use std::cmp::{self, Ordering}; use std::cmp::Ordering;
use std::panic; use std::panic;
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed; use std::sync::atomic::Ordering::Relaxed;
use std::thread; use std::thread;
static VERSIONS: AtomicUsize = AtomicUsize::new(0); // const is needed for array initializer
#[allow(clippy::declare_interior_mutable_const)]
const ZERO: AtomicUsize = AtomicUsize::new(0);
const LEN: usize = 20_000;
lazy_static::lazy_static! { static VERSIONS: AtomicUsize = ZERO;
static ref DROP_COUNTS: Vec<AtomicUsize> = (0..20_000).map(|_| AtomicUsize::new(0)).collect();
} static DROP_COUNTS: [AtomicUsize; LEN] = [ZERO; LEN];
#[derive(Clone, Eq)] #[derive(Clone, Eq)]
struct DropCounter { struct DropCounter {
@@ -27,6 +30,7 @@ impl PartialEq for DropCounter {
} }
} }
#[allow(clippy::non_canonical_partial_ord_impl)]
impl PartialOrd for DropCounter { impl PartialOrd for DropCounter {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.version.set(self.version.get() + 1); self.version.set(self.version.get() + 1);
@@ -65,7 +69,7 @@ macro_rules! test {
let step = if len <= 100 { let step = if len <= 100 {
1 1
} else { } else {
cmp::max(1, panic_countdown / 10) Ord::max(1, panic_countdown / 10)
}; };
// ... and then panic after each `step` comparisons. // ... and then panic after each `step` comparisons.
@@ -114,9 +118,10 @@ macro_rules! test {
}; };
} }
thread_local!(static SILENCE_PANIC: Cell<bool> = Cell::new(false)); thread_local!(static SILENCE_PANIC: Cell<bool> = const { Cell::new(false) });
#[test] #[test]
#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
fn sort_panic_safe() { fn sort_panic_safe() {
let prev = panic::take_hook(); let prev = panic::take_hook();
panic::set_hook(Box::new(move |info| { panic::set_hook(Box::new(move |info| {
@@ -144,8 +149,8 @@ fn sort_panic_safe() {
} }
for _ in 0..5 { for _ in 0..5 {
let a = rng.sample(&len_dist); let a = rng.sample(len_dist);
let b = rng.sample(&len_dist); let b = rng.sample(len_dist);
if a < b { if a < b {
input[a..b].reverse(); input[a..b].reverse();
} else { } else {

View File

@@ -56,38 +56,44 @@ pub fn execute_strings_split() {
("foo\nbar\n\r\nbaz", '\n'), ("foo\nbar\n\r\nbaz", '\n'),
("A few words", ' '), ("A few words", ' '),
(" Mary had\ta\u{2009}little \n\t lamb", ' '), (" Mary had\ta\u{2009}little \n\t lamb", ' '),
("Mary had a little lamb\nlittle lamb\nlittle lamb.", '\n'),
("Mary had a little lamb\nlittle lamb\nlittle lamb.\n", '\n'),
(include_str!("str.rs"), ' '), (include_str!("str.rs"), ' '),
]; ];
for &(string, separator) in &tests { macro_rules! check_separators {
let serial: Vec<_> = string.split(separator).collect(); ($split:ident, $par_split:ident) => {
let parallel: Vec<_> = string.par_split(separator).collect(); for &(string, separator) in &tests {
assert_eq!(serial, parallel); let serial: Vec<_> = string.$split(separator).collect();
let parallel: Vec<_> = string.$par_split(separator).collect();
assert_eq!(serial, parallel);
let pattern: &[char] = &['\u{0}', separator, '\u{1F980}']; let array = ['\u{0}', separator, '\u{1F980}'];
let serial: Vec<_> = string.split(pattern).collect(); let array_ref = &array;
let parallel: Vec<_> = string.par_split(pattern).collect(); let slice: &[char] = array_ref;
assert_eq!(serial, parallel);
let serial_fn: Vec<_> = string.split(|c| c == separator).collect(); let serial: Vec<_> = string.$split(slice).collect();
let parallel_fn: Vec<_> = string.par_split(|c| c == separator).collect(); let parallel: Vec<_> = string.$par_split(slice).collect();
assert_eq!(serial_fn, parallel_fn); assert_eq!(serial, parallel);
let serial: Vec<_> = string.$split(array).collect();
let parallel: Vec<_> = string.$par_split(array).collect();
assert_eq!(serial, parallel);
let serial: Vec<_> = string.$split(array_ref).collect();
let parallel: Vec<_> = string.$par_split(array_ref).collect();
assert_eq!(serial, parallel);
let serial_fn: Vec<_> = string.$split(|c| c == separator).collect();
let parallel_fn: Vec<_> = string.$par_split(|c| c == separator).collect();
assert_eq!(serial_fn, parallel_fn);
}
};
} }
for &(string, separator) in &tests { check_separators!(split, par_split);
let serial: Vec<_> = string.split_terminator(separator).collect(); check_separators!(split_inclusive, par_split_inclusive);
let parallel: Vec<_> = string.par_split_terminator(separator).collect(); check_separators!(split_terminator, par_split_terminator);
assert_eq!(serial, parallel);
let pattern: &[char] = &['\u{0}', separator, '\u{1F980}'];
let serial: Vec<_> = string.split_terminator(pattern).collect();
let parallel: Vec<_> = string.par_split_terminator(pattern).collect();
assert_eq!(serial, parallel);
let serial: Vec<_> = string.split_terminator(|c| c == separator).collect();
let parallel: Vec<_> = string.par_split_terminator(|c| c == separator).collect();
assert_eq!(serial, parallel);
}
for &(string, _) in &tests { for &(string, _) in &tests {
let serial: Vec<_> = string.lines().collect(); let serial: Vec<_> = string.lines().collect();
@@ -101,34 +107,13 @@ pub fn execute_strings_split() {
assert_eq!(serial, parallel); assert_eq!(serial, parallel);
} }
for &(string, _) in &tests {
let serial: Vec<_> = string.split_ascii_whitespace().collect();
let parallel: Vec<_> = string.par_split_ascii_whitespace().collect();
assert_eq!(serial, parallel);
}
// try matching separators too! // try matching separators too!
for &(string, separator) in &tests { check_separators!(matches, par_matches);
let serial: Vec<_> = string.matches(separator).collect(); check_separators!(match_indices, par_match_indices);
let parallel: Vec<_> = string.par_matches(separator).collect();
assert_eq!(serial, parallel);
let pattern: &[char] = &['\u{0}', separator, '\u{1F980}'];
let serial: Vec<_> = string.matches(pattern).collect();
let parallel: Vec<_> = string.par_matches(pattern).collect();
assert_eq!(serial, parallel);
let serial_fn: Vec<_> = string.matches(|c| c == separator).collect();
let parallel_fn: Vec<_> = string.par_matches(|c| c == separator).collect();
assert_eq!(serial_fn, parallel_fn);
}
for &(string, separator) in &tests {
let serial: Vec<_> = string.match_indices(separator).collect();
let parallel: Vec<_> = string.par_match_indices(separator).collect();
assert_eq!(serial, parallel);
let pattern: &[char] = &['\u{0}', separator, '\u{1F980}'];
let serial: Vec<_> = string.match_indices(pattern).collect();
let parallel: Vec<_> = string.par_match_indices(pattern).collect();
assert_eq!(serial, parallel);
let serial_fn: Vec<_> = string.match_indices(|c| c == separator).collect();
let parallel_fn: Vec<_> = string.par_match_indices(|c| c == separator).collect();
assert_eq!(serial_fn, parallel_fn);
}
} }