1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
#include <aocommon/recursivefor.h>
#include <aocommon/staticfor.h>
#include <atomic>
#include <set>
#include <mutex>
#include <unistd.h> // for sleep
#include <boost/test/unit_test.hpp>
using aocommon::RecursiveFor;
using aocommon::StaticFor;
using aocommon::ThreadPool;
BOOST_AUTO_TEST_SUITE(staticfor)
BOOST_AUTO_TEST_CASE(run) {
ThreadPool::GetInstance().SetNThreads(4);
StaticFor<size_t> loop;
std::mutex mutex;
std::vector<size_t> counts(10, 0);
loop.Run(0, 10, [&](size_t a, size_t b) {
for (size_t iter = a; iter != b; ++iter) {
std::unique_lock<std::mutex> lock(mutex);
counts[iter]++;
}
});
std::vector<size_t> ref(10, 1);
BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(), ref.begin(),
ref.end());
}
BOOST_AUTO_TEST_CASE(empty_loop) {
ThreadPool::GetInstance().SetNThreads(4);
StaticFor<size_t> loop;
std::atomic<bool> fail = false;
loop.Run(10, 10, [&](size_t a, size_t b) {
if (a != b) fail = true;
});
BOOST_CHECK(!fail);
}
BOOST_AUTO_TEST_CASE(single_threaded) {
ThreadPool::GetInstance().SetNThreads(1);
StaticFor<size_t> loop;
std::vector<size_t> counts(10, 0);
loop.Run(0, 10, [&](size_t a, size_t b) {
for (size_t iter = a; iter != b; ++iter) {
counts[iter]++;
}
});
std::vector<size_t> ref(10, 1);
BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(), ref.begin(),
ref.end());
}
BOOST_AUTO_TEST_CASE(resume_run) {
std::vector<size_t> counts(20, 0);
std::mutex mutex;
ThreadPool::GetInstance().SetNThreads(40);
StaticFor<size_t> loop;
loop.Run(0, 10, [&](size_t a, size_t b) {
for (size_t iter = a; iter != b; ++iter) {
std::unique_lock<std::mutex> lock(mutex);
counts[iter]++;
}
});
std::vector<size_t> ref(20, 0);
std::fill(ref.begin(), ref.begin() + 10, 1);
BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(), ref.begin(),
ref.end());
loop.Run(10, 20, [&](size_t a, size_t b) {
for (size_t iter = a; iter != b; ++iter) {
std::unique_lock<std::mutex> lock(mutex);
counts[iter]++;
}
});
ref = std::vector<size_t>(20, 1);
BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(), ref.begin(),
ref.end());
}
BOOST_AUTO_TEST_CASE(run_with_thread_id) {
ThreadPool::GetInstance().SetNThreads(4);
StaticFor<size_t> loop;
std::mutex mutex;
std::vector<size_t> counts(10, 0);
std::vector<size_t> threads(4, 0);
loop.Run(0, 10, [&](size_t a, size_t b, size_t t) {
for (size_t iter = a; iter != b; ++iter) {
std::unique_lock<std::mutex> lock(mutex);
counts[iter]++;
}
std::unique_lock<std::mutex> lock(mutex);
threads[t]++;
});
std::vector<size_t> ref(10, 1);
BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(), ref.begin(),
ref.end());
// Not all threads might actually be used, because if one thread
// finishes before a second thread is starting, the first
// thread is used to perform the next loop.
// Therefore all we can check is whether there weren't more than
// 4 blocks:
for (size_t i = 0; i != threads.size(); ++i) BOOST_CHECK_LT(threads[i], 5);
}
BOOST_AUTO_TEST_CASE(inside_recursive_for) {
const size_t kNThreads = 7;
ThreadPool::GetInstance().SetNThreads(kNThreads);
RecursiveFor recursive_for;
std::mutex mutex;
std::map<std::pair<size_t, size_t>, size_t> counter;
recursive_for.Run(0, 10, [&](size_t outer, size_t) {
StaticFor<size_t> nested_for;
nested_for.Run(0, 10, [&, outer](size_t start, size_t end) {
for (size_t inner = start; inner != end; ++inner) {
std::lock_guard lock(mutex);
counter[std::make_pair(outer, inner)]++;
}
});
});
BOOST_CHECK_EQUAL(counter.size(), 100);
for (const std::pair<const std::pair<size_t, size_t>, size_t>& count :
counter) {
BOOST_CHECK_LT(count.first.first, 10); // outer
BOOST_CHECK_LT(count.first.second, 10); // inner
BOOST_CHECK_EQUAL(count.second, 1); // count
}
}
BOOST_AUTO_TEST_CASE(constrained_run_without_id) {
ThreadPool::GetInstance().SetNThreads(4);
StaticFor<size_t> loop;
std::mutex mutex;
std::vector<size_t> counts(10, 0);
std::set<std::thread::id> thread_ids;
const size_t kMaxThreads = 3;
loop.ConstrainedRun(0, 10, kMaxThreads, [&](size_t a, size_t b) {
for (size_t iter = a; iter != b; ++iter) {
std::scoped_lock<std::mutex> lock(mutex);
counts[iter]++;
}
std::scoped_lock<std::mutex> lock(mutex);
thread_ids.emplace(std::this_thread::get_id());
});
std::vector<size_t> ref(10, 1);
BOOST_CHECK_EQUAL(thread_ids.size(), kMaxThreads);
BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(), ref.begin(),
ref.end());
}
BOOST_AUTO_TEST_CASE(constrained_run_with_id) {
ThreadPool::GetInstance().SetNThreads(4);
StaticFor<size_t> loop;
std::mutex mutex;
std::vector<size_t> counts(10, 0);
std::set<std::thread::id> thread_ids;
std::set<size_t> thread_indices;
const size_t kMaxThreads = 3;
loop.ConstrainedRun(0, 10, kMaxThreads,
[&](size_t a, size_t b, size_t thread_index) {
for (size_t iter = a; iter != b; ++iter) {
std::scoped_lock<std::mutex> lock(mutex);
counts[iter]++;
}
std::scoped_lock<std::mutex> lock(mutex);
thread_ids.emplace(std::this_thread::get_id());
thread_indices.emplace(thread_index);
});
BOOST_CHECK_EQUAL(thread_ids.size(), kMaxThreads);
BOOST_CHECK_EQUAL(thread_indices.size(), kMaxThreads);
const std::set<size_t> expected_indices{0, 1, 2};
BOOST_CHECK_EQUAL_COLLECTIONS(thread_indices.begin(), thread_indices.end(),
expected_indices.begin(),
expected_indices.end());
std::vector<size_t> expected_counts(10, 1);
BOOST_CHECK_EQUAL_COLLECTIONS(counts.begin(), counts.end(),
expected_counts.begin(), expected_counts.end());
}
BOOST_AUTO_TEST_CASE(conditional_nested_run) {
// Test the special case in which no RecursiveFor exists and a static for
// is nested nevertheless, but constrained to one thread max. This allows
// nested parallelization and the making of a RecursiveFor to be done
// conditional, which for certain cases is useful to use as many threads as
// possible.
size_t iteration_count = 0;
std::mutex mutex;
BOOST_CHECK(aocommon::RecursiveFor::GetInstance() == nullptr);
aocommon::RunStaticFor<size_t>(0, 1000, [&](size_t start, size_t end) {
for (size_t outer_index = start; outer_index != end; ++outer_index) {
aocommon::RunConstrainedStaticFor<size_t>(
0, 10, 1, [&](size_t nested_start, size_t nested_end) {
std::lock_guard lock(mutex);
iteration_count += nested_end - nested_start;
});
}
});
BOOST_CHECK_EQUAL(iteration_count, 10000);
}
BOOST_AUTO_TEST_SUITE_END()
|