Skip to content

Commit 1663ef6

Browse files
committed
Wordsmith
1 parent f2f01b3 commit 1663ef6

File tree

1 file changed

+59
-56
lines changed

1 file changed

+59
-56
lines changed

README.md

Lines changed: 59 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,65 @@
55

66
Light, self-contained, thread pool-based implementation of [C++17 parallel standard library algorithms](https://en.cppreference.com/w/cpp/algorithm).
77

8-
C++17 introduced parallel versions of many algorithms in the standard library.
9-
These parallel versions accept an *Execution Policy* as their first argument.
10-
Different policies allow the implementation to parallelize the algorithm in a different way,
11-
such as using threads, vectorization, or even GPU.
8+
C++17 introduced parallel overloads of standard library algorithms that accept an [*Execution Policy*](https://en.cppreference.com/w/cpp/algorithm/execution_policy_tag) as the first argument.
9+
Policies specify limits on how the implementation may parallelize the algorithm, enabling methods like threads, vectorization, or even GPU.
1210
Policies can be supplied by the compiler or by libraries like this one.
1311

14-
Unfortunately compiler support for the [standard policies](https://en.cppreference.com/w/cpp/algorithm/execution_policy_tag) varies.
15-
PoolSTL is a *supplement* to fill in the support gaps, so we can use parallel algorithms now.
16-
It is not meant as a full implementation; only the basics are expected to be covered. Use this if:
17-
* you only need the basics, including no nested parallel calls.
18-
* you must use a [compiler lacking native support](https://en.cppreference.com/w/cpp/compiler_support/17) (see "Parallel algorithms and execution policies").
19-
* you cannot link against TBB for whatever reason.
20-
* the [Parallel STL](https://www.intel.com/content/www/us/en/developer/articles/guide/get-started-with-parallel-stl.html) is too heavy.
12+
```c++
13+
std::sort(std::execution::par, vec.begin(), vec.end());
14+
// ^^^^^^^^^^^^^^^^^^^ native C++17 parallel Execution Policy
15+
```
16+
17+
Unfortunately compiler support [varies](https://en.cppreference.com/w/cpp/compiler_support/17):
18+
19+
| | Linux | macOS | Windows |
20+
|:-------------|:------------:|:------------:|:------------:|
21+
| GCC 8- | ❌ | ❌ | ❌ |
22+
| GCC 9+ | TBB Required | TBB Required | TBB Required |
23+
| Clang | TBB Required | TBB Required | TBB Required |
24+
| Apple Clang | | ❌ | |
25+
| MSVC | | | ✅ |
26+
| [Parallel STL](https://www.intel.com/content/www/us/en/developer/articles/guide/get-started-with-parallel-stl.html) | TBB Required | TBB Required | TBB Required |
27+
| **poolSTL** | ✅ | ✅ | ✅ |
28+
29+
PoolSTL is a *supplement* to fill in the support gaps. It is small, easy to integrate, and has no external dependencies.
30+
Note that poolSTL is not a full implementation; only the basics are covered.
31+
32+
Use poolSTL exclusively, or only on platforms lacking native support,
33+
or only if [TBB](https://www.intel.com/content/www/us/en/developer/tools/oneapi/onetbb.html) is not present.
2134
2235
Supports C++11 and higher, C++17 preferred.
2336
Tested in CI on GCC 7+, Clang/LLVM 5+, Apple Clang, MSVC.
2437
2538
## Implemented Algorithms
26-
Algorithms are added on an as-needed basis. If you need one [open an issue](https://github.com/alugowski/poolSTL/issues) or contribute a PR.
39+
Algorithms are added on an as-needed basis. If you need one [open an issue](https://github.com/alugowski/poolSTL/issues) or contribute a PR.
40+
**Limitations:** All iterators must be random access. No nested parallel calls.
2741
2842
### `<algorithm>`
29-
* [all_of](https://en.cppreference.com/w/cpp/algorithm/all_of), [any_of](https://en.cppreference.com/w/cpp/algorithm/any_of), [none_of](https://en.cppreference.com/w/cpp/algorithm/none_of)
30-
* [copy](https://en.cppreference.com/w/cpp/algorithm/copy), [copy_n](https://en.cppreference.com/w/cpp/algorithm/copy_n)
31-
* [count](https://en.cppreference.com/w/cpp/algorithm/count), [count_if](https://en.cppreference.com/w/cpp/algorithm/count_if)
32-
* [fill](https://en.cppreference.com/w/cpp/algorithm/fill), [fill_n](https://en.cppreference.com/w/cpp/algorithm/fill_n)
33-
* [find](https://en.cppreference.com/w/cpp/algorithm/find), [find_if](https://en.cppreference.com/w/cpp/algorithm/find_if), [find_if_not](https://en.cppreference.com/w/cpp/algorithm/find_if_not)
34-
* [for_each](https://en.cppreference.com/w/cpp/algorithm/for_each), [for_each_n](https://en.cppreference.com/w/cpp/algorithm/for_each_n)
35-
* [sort](https://en.cppreference.com/w/cpp/algorithm/sort), [stable_sort](https://en.cppreference.com/w/cpp/algorithm/stable_sort)
36-
* [transform](https://en.cppreference.com/w/cpp/algorithm/transform)
43+
* [`all_of`](https://en.cppreference.com/w/cpp/algorithm/all_of), [`any_of`](https://en.cppreference.com/w/cpp/algorithm/any_of), [`none_of`](https://en.cppreference.com/w/cpp/algorithm/none_of)
44+
* [`copy`](https://en.cppreference.com/w/cpp/algorithm/copy), [`copy_n`](https://en.cppreference.com/w/cpp/algorithm/copy_n)
45+
* [`count`](https://en.cppreference.com/w/cpp/algorithm/count), [`count_if`](https://en.cppreference.com/w/cpp/algorithm/count_if)
46+
* [`fill`](https://en.cppreference.com/w/cpp/algorithm/fill), [`fill_n`](https://en.cppreference.com/w/cpp/algorithm/fill_n)
47+
* [`find`](https://en.cppreference.com/w/cpp/algorithm/find), [`find_if`](https://en.cppreference.com/w/cpp/algorithm/find_if), [`find_if_not`](https://en.cppreference.com/w/cpp/algorithm/find_if_not)
48+
* [`for_each`](https://en.cppreference.com/w/cpp/algorithm/for_each), [`for_each_n`](https://en.cppreference.com/w/cpp/algorithm/for_each_n)
49+
* [`sort`](https://en.cppreference.com/w/cpp/algorithm/sort), [`stable_sort`](https://en.cppreference.com/w/cpp/algorithm/stable_sort)
50+
* [`transform`](https://en.cppreference.com/w/cpp/algorithm/transform)
3751
3852
### `<numeric>`
39-
* [exclusive_scan](https://en.cppreference.com/w/cpp/algorithm/exclusive_scan) (C++17 only)
40-
* [reduce](https://en.cppreference.com/w/cpp/algorithm/reduce)
41-
* [transform_reduce](https://en.cppreference.com/w/cpp/algorithm/transform_reduce) (C++17 only)
53+
* [`exclusive_scan`](https://en.cppreference.com/w/cpp/algorithm/exclusive_scan) (C++17 only)
54+
* [`reduce`](https://en.cppreference.com/w/cpp/algorithm/reduce)
55+
* [`transform_reduce`](https://en.cppreference.com/w/cpp/algorithm/transform_reduce) (C++17 only)
4256
4357
All in `std::` namespace.
4458
45-
**Note:** All iterators must be random access.
59+
### Other
60+
* [`poolstl::iota_iter`](include/poolstl/iota_iter.hpp) - Iterate over integers. Same as iterating over output of [`std::iota`](https://en.cppreference.com/w/cpp/algorithm/iota) but without materializing anything. Iterator version of [`std::ranges::iota_view`](https://en.cppreference.com/w/cpp/ranges/iota_view).
4661
4762
## Usage
4863
49-
PoolSTL provides these execution policies:
64+
PoolSTL provides:
5065
* `poolstl::par`: Substitute for [`std::execution::par`](https://en.cppreference.com/w/cpp/algorithm/execution_policy_tag). Parallelized using a [thread pool](https://github.com/alugowski/task-thread-pool).
51-
* `poolstl::seq`: Substitute for `std::execution::seq`. Simply calls the sequential (non-policy) overload.
66+
* `poolstl::seq`: Substitute for `std::execution::seq`. Simply calls the regular (non-policy) overload.
5267
* `poolstl::par_if()`: (C++17 only) choose parallel or sequential at runtime. See below.
5368
5469
In short, use `poolstl::par` to make your code parallel. Complete example:
@@ -58,7 +73,7 @@ In short, use `poolstl::par` to make your code parallel. Complete example:
5873
5974
int main() {
6075
std::vector<int> v = {0, 1, 2, 3, 4, 5};
61-
auto sum = std::reduce(poolstl::par, v.cbegin(), v.cend());
76+
auto sum = std::reduce(poolstl::par, vec.cbegin(), vec.cend());
6277
// ^^^^^^^^^^^^
6378
// Add this to make your code parallel.
6479
std::cout << "Sum=" << sum << std::endl;
@@ -68,15 +83,14 @@ int main() {
6883

6984
### Controlling Thread Pool Size with `par.on(pool)`
7085

71-
The thread pool used by `poolstl::par` is managed internally by poolSTL. It is started on first use.
72-
86+
The thread pool used by `poolstl::par` is managed internally by poolSTL. It is started on first use.
7387
Use your own [thread pool](https://github.com/alugowski/task-thread-pool)
7488
with `poolstl::par.on(pool)` for control over thread count, startup/shutdown, etc.:
7589

7690
```c++
7791
task_thread_pool::task_thread_pool pool{4}; // 4 threads
7892

79-
std::reduce(poolstl::par.on(pool), v.cbegin(), v.cbegin());
93+
std::reduce(poolstl::par.on(pool), vec.begin(), vec.end());
8094
```
8195
8296
### Choosing Parallel or Sequential at Runtime with `par_if`
@@ -86,64 +100,53 @@ the cost of starting threads, while large datasets do and should be parallelized
86100
87101
Use `poolstl::par_if` to select between `par` and `seq` at runtime:
88102
```c++
89-
bool is_parallel = true;
103+
bool is_parallel = vec.size() > 10000;
90104
91-
std::reduce(poolstl::par_if(is_parallel), v.cbegin(), v.cbegin());
105+
std::reduce(poolstl::par_if(is_parallel), vec.begin(), vec.end());
92106
```
93107

94108
Use `poolstl::par_if(is_parallel, pool)` to control the thread pool used by `par`, if selected.
95109

96110
# Examples
97111

98-
## Parallelize a `for-each` loop
112+
### Parallel `for (auto& value : vec)`
99113

100114
```c++
101-
std::vector<int> v = {0, 1, 2, 3, 4, 5};
115+
std::vector<int> vec = {0, 1, 2, 3, 4, 5};
102116

103-
// Sequential
104-
for (auto& value : v) {
105-
std::cout << value; // loop body
106-
}
107-
108-
// Parallel
109-
std::for_each(poolstl::par, v.begin(), v.end(), [](auto& value) {
117+
// Parallel for-each
118+
std::for_each(poolstl::par, vec.begin(), vec.end(), [](auto& value) {
110119
std::cout << value; // loop body
111120
});
112121
```
113122

114-
## Parallelize a `for` loop (counter)
123+
### Parallel `for (int i = 0; i < 100; ++i)`
115124

116125
```c++
117-
// Sequential
118-
for (int i = 0; i < 100 : ++i) {
119-
std::cout << i; // loop body
120-
}
121-
122-
// Parallel
123126
using poolstl::iota_iter;
124127

128+
// parallel for loop
125129
std::for_each(poolstl::par, iota_iter<int>(0), iota_iter<int>(100), [](auto i) {
126130
std::cout << i; // loop body
127131
});
128132
```
129133
130134
131135
132-
## Parallel Sort
136+
### Parallel Sort
133137
134138
```c++
135-
std::vector<int> v = {5, 2, 1, 3, 0, 4};
136-
std::sort(poolstl::par, v.begin(), v.end();
137-
// `v` is now {0, 1, 2, 3, 4, 5}
139+
std::vector<int> vec = {5, 2, 1, 3, 0, 4};
140+
141+
std::sort(poolstl::par, vec.begin(), vec.end());
138142
```
139143

140144
## Installation
141145

142146
### Single File
143147

144-
Copy a single-file amalgamated `poolstl.hpp` from the [latest release](https://github.com/alugowski/poolSTL/releases) and into your project.
145-
146-
Note: Some compilers, including non-Apple Clang and GCC 8 and older, require the `-lpthread` linker flag to use C++11 threads.
148+
Each [release](https://github.com/alugowski/poolSTL/releases/latest) publishes a single-file amalgamated `poolstl.hpp`. Simply copy this into your project.
149+
**Note:** Some compilers (non-Apple Clang, GCC 8 and older) require `-lpthread` to use C++11 threads.
147150

148151
### CMake
149152

@@ -205,7 +208,7 @@ reduce(std::execution::par)/real_time 3.38 ms
205208
```
206209

207210
# poolSTL as `std::execution::par`
208-
**USE AT YOUR OWN RISK!**
211+
**USE AT YOUR OWN RISK! THIS IS A HACK!**
209212

210213
Two-line hack for missing compiler support. A no-op on compilers with support.
211214

0 commit comments

Comments
 (0)