1 | //===- llvm/unittest/Support/ParallelTest.cpp -----------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | /// |
9 | /// \file |
10 | /// Parallel.h unit tests. |
11 | /// |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/Support/Parallel.h" |
15 | #include "llvm/Support/ThreadPool.h" |
16 | #include "gtest/gtest.h" |
17 | #include <array> |
18 | #include <random> |
19 | |
20 | uint32_t array[1024 * 1024]; |
21 | |
22 | using namespace llvm; |
23 | |
24 | // Tests below are hanging up on mingw. Investigating. |
25 | #if !defined(__MINGW32__) |
26 | |
27 | TEST(Parallel, sort) { |
28 | std::mt19937 randEngine; |
29 | std::uniform_int_distribution<uint32_t> dist; |
30 | |
31 | for (auto &i : array) |
32 | i = dist(randEngine); |
33 | |
34 | parallelSort(Start: std::begin(arr&: array), End: std::end(arr&: array)); |
35 | ASSERT_TRUE(llvm::is_sorted(array)); |
36 | } |
37 | |
38 | TEST(Parallel, parallel_for) { |
39 | // We need to test the case with a TaskSize > 1. We are white-box testing |
40 | // here. The TaskSize is calculated as (End - Begin) / 1024 at the time of |
41 | // writing. |
42 | uint32_t range[2050]; |
43 | std::fill(first: range, last: range + 2050, value: 1); |
44 | parallelFor(Begin: 0, End: 2049, Fn: [&range](size_t I) { ++range[I]; }); |
45 | |
46 | uint32_t expected[2049]; |
47 | std::fill(first: expected, last: expected + 2049, value: 2); |
48 | ASSERT_TRUE(std::equal(range, range + 2049, expected)); |
49 | // Check that we don't write past the end of the requested range. |
50 | ASSERT_EQ(range[2049], 1u); |
51 | } |
52 | |
53 | TEST(Parallel, TransformReduce) { |
54 | // Sum an empty list, check that it works. |
55 | auto identity = [](uint32_t v) { return v; }; |
56 | uint32_t sum = parallelTransformReduce(R: ArrayRef<uint32_t>(), Init: 0U, |
57 | Reduce: std::plus<uint32_t>(), Transform: identity); |
58 | EXPECT_EQ(sum, 0U); |
59 | |
60 | // Sum the lengths of these strings in parallel. |
61 | const char *strs[] = {"a" , "ab" , "abc" , "abcd" , "abcde" , "abcdef" }; |
62 | size_t lenSum = |
63 | parallelTransformReduce(R&: strs, Init: static_cast<size_t>(0), Reduce: std::plus<size_t>(), |
64 | Transform: [](const char *s) { return strlen(s: s); }); |
65 | EXPECT_EQ(lenSum, static_cast<size_t>(21)); |
66 | |
67 | // Check that we handle non-divisible task sizes as above. |
68 | uint32_t range[2050]; |
69 | std::fill(first: std::begin(arr&: range), last: std::end(arr&: range), value: 1); |
70 | sum = parallelTransformReduce(R&: range, Init: 0U, Reduce: std::plus<uint32_t>(), Transform: identity); |
71 | EXPECT_EQ(sum, 2050U); |
72 | |
73 | std::fill(first: std::begin(arr&: range), last: std::end(arr&: range), value: 2); |
74 | sum = parallelTransformReduce(R&: range, Init: 0U, Reduce: std::plus<uint32_t>(), Transform: identity); |
75 | EXPECT_EQ(sum, 4100U); |
76 | |
77 | // Avoid one large task. |
78 | uint32_t range2[3060]; |
79 | std::fill(first: std::begin(arr&: range2), last: std::end(arr&: range2), value: 1); |
80 | sum = parallelTransformReduce(R&: range2, Init: 0U, Reduce: std::plus<uint32_t>(), Transform: identity); |
81 | EXPECT_EQ(sum, 3060U); |
82 | } |
83 | |
84 | TEST(Parallel, ForEachError) { |
85 | int nums[] = {1, 2, 3, 4, 5, 6}; |
86 | Error e = parallelForEachError(R&: nums, Fn: [](int v) -> Error { |
87 | if ((v & 1) == 0) |
88 | return createStringError(EC: std::errc::invalid_argument, Fmt: "asdf" ); |
89 | return Error::success(); |
90 | }); |
91 | EXPECT_TRUE(e.isA<ErrorList>()); |
92 | std::string errText = toString(E: std::move(e)); |
93 | EXPECT_EQ(errText, std::string("asdf\nasdf\nasdf" )); |
94 | } |
95 | |
96 | TEST(Parallel, TaskGroupSequentialFor) { |
97 | size_t Count = 0; |
98 | { |
99 | parallel::TaskGroup tg; |
100 | for (size_t Idx = 0; Idx < 500; Idx++) |
101 | tg.spawn(f: [&Count, Idx]() { EXPECT_EQ(Count++, Idx); }, Sequential: true); |
102 | } |
103 | EXPECT_EQ(Count, 500ul); |
104 | } |
105 | |
106 | #if LLVM_ENABLE_THREADS |
107 | TEST(Parallel, NestedTaskGroup) { |
108 | // This test checks: |
109 | // 1. Root TaskGroup is in Parallel mode. |
110 | // 2. Nested TaskGroup is not in Parallel mode. |
111 | parallel::TaskGroup tg; |
112 | |
113 | tg.spawn(f: [&]() { |
114 | EXPECT_TRUE(tg.isParallel() || (parallel::strategy.ThreadsRequested == 1)); |
115 | }); |
116 | |
117 | tg.spawn(f: [&]() { |
118 | parallel::TaskGroup nestedTG; |
119 | EXPECT_FALSE(nestedTG.isParallel()); |
120 | |
121 | nestedTG.spawn(f: [&]() { |
122 | // Check that root TaskGroup is in Parallel mode. |
123 | EXPECT_TRUE(tg.isParallel() || |
124 | (parallel::strategy.ThreadsRequested == 1)); |
125 | |
126 | // Check that nested TaskGroup is not in Parallel mode. |
127 | EXPECT_FALSE(nestedTG.isParallel()); |
128 | }); |
129 | }); |
130 | } |
131 | |
132 | TEST(Parallel, ParallelNestedTaskGroup) { |
133 | // This test checks that it is possible to have several TaskGroups |
134 | // run from different threads in Parallel mode. |
135 | std::atomic<size_t> Count{0}; |
136 | |
137 | { |
138 | std::function<void()> Fn = [&]() { |
139 | parallel::TaskGroup tg; |
140 | |
141 | tg.spawn(f: [&]() { |
142 | // Check that root TaskGroup is in Parallel mode. |
143 | EXPECT_TRUE(tg.isParallel() || |
144 | (parallel::strategy.ThreadsRequested == 1)); |
145 | |
146 | // Check that nested TaskGroup is not in Parallel mode. |
147 | parallel::TaskGroup nestedTG; |
148 | EXPECT_FALSE(nestedTG.isParallel()); |
149 | ++Count; |
150 | |
151 | nestedTG.spawn(f: [&]() { |
152 | // Check that root TaskGroup is in Parallel mode. |
153 | EXPECT_TRUE(tg.isParallel() || |
154 | (parallel::strategy.ThreadsRequested == 1)); |
155 | |
156 | // Check that nested TaskGroup is not in Parallel mode. |
157 | EXPECT_FALSE(nestedTG.isParallel()); |
158 | ++Count; |
159 | }); |
160 | }); |
161 | }; |
162 | |
163 | DefaultThreadPool Pool; |
164 | |
165 | Pool.async(F&: Fn); |
166 | Pool.async(F&: Fn); |
167 | Pool.async(F&: Fn); |
168 | Pool.async(F&: Fn); |
169 | Pool.async(F&: Fn); |
170 | Pool.async(F&: Fn); |
171 | |
172 | Pool.wait(); |
173 | } |
174 | EXPECT_EQ(Count, 12ul); |
175 | } |
176 | #endif |
177 | |
178 | #endif |
179 | |