Skip to content

Commit 8605e60

Browse files
authored
Async Utils (#212)
* Add async_utils for easier multithreading * Joseph's comments * Consolidate async tests
1 parent f007661 commit 8605e60

File tree

4 files changed

+166
-0
lines changed

4 files changed

+166
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (C) 2020 Swift Navigation Inc.
3+
* Contact: Swift Navigation <dev@swiftnav.com>
4+
*
5+
* This source is subject to the license found in the file 'LICENSE' which must
6+
* be distributed together with this source. All other rights reserved.
7+
*
8+
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
9+
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
10+
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
11+
*/
12+
13+
#ifndef INCLUDE_ALBATROSS_SRC_UTILS_ASYNC_UTILS_HPP_
14+
#define INCLUDE_ALBATROSS_SRC_UTILS_ASYNC_UTILS_HPP_
15+
16+
namespace albatross {
17+
18+
// This method makes sure we don't accidentally call async with the
19+
// default mode which has some flaws:
20+
//
21+
// https://eli.thegreenplace.net/2016/the-promises-and-challenges-of-stdasync-task-based-parallelism-in-c11/
22+
template <typename F, typename... Ts>
23+
inline auto async_safe(F &&f, Ts &&... params) {
24+
return std::async(std::launch::async, std::forward<F>(f),
25+
std::forward<Ts>(params)...);
26+
}
27+
28+
template <typename ValueType, typename ApplyFunction,
29+
typename ApplyType = typename details::value_only_apply_result<
30+
ApplyFunction, ValueType>::type,
31+
typename std::enable_if<details::is_valid_value_only_apply_function<
32+
ApplyFunction, ValueType>::value &&
33+
std::is_same<void, ApplyType>::value,
34+
int>::type = 0>
35+
void async_apply(const std::vector<ValueType> &xs, const ApplyFunction &f) {
36+
std::vector<std::future<void>> futures;
37+
for (const auto &x : xs) {
38+
futures.emplace_back(async_safe(f, x));
39+
}
40+
for (auto &f : futures) {
41+
f.get();
42+
}
43+
}
44+
45+
template <typename ValueType, typename ApplyFunction,
46+
typename ApplyType = typename details::value_only_apply_result<
47+
ApplyFunction, ValueType>::type,
48+
typename std::enable_if<details::is_valid_value_only_apply_function<
49+
ApplyFunction, ValueType>::value &&
50+
!std::is_same<void, ApplyType>::value,
51+
int>::type = 0>
52+
auto async_apply(const std::vector<ValueType> &xs, const ApplyFunction &f) {
53+
std::vector<std::future<ApplyType>> futures;
54+
for (const auto &x : xs) {
55+
futures.emplace_back(async_safe(f, x));
56+
}
57+
58+
std::vector<ApplyType> output;
59+
for (auto &f : futures) {
60+
output.emplace_back(f.get());
61+
}
62+
return output;
63+
}
64+
65+
} // namespace albatross
66+
67+
#endif /* INCLUDE_ALBATROSS_SRC_UTILS_ASYNC_UTILS_HPP_ */

include/albatross/utils/AsyncUtils

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (C) 2020 Swift Navigation Inc.
3+
* Contact: Swift Navigation <dev@swiftnav.com>
4+
*
5+
* This source is subject to the license found in the file 'LICENSE' which must
6+
* be distributed together with this source. All other rights reserved.
7+
*
8+
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
9+
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
10+
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
11+
*/
12+
13+
#ifndef ALBATROSS_UTILS_ASYNC_UTILS_H
14+
#define ALBATROSS_UTILS_ASYNC_UTILS_H
15+
16+
#include <future>
17+
18+
#include "../src/utils/async_utils.hpp"
19+
20+
#endif

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_executable(albatross_unit_tests
22
test_apply.cc
3+
test_async_utils.cc
34
test_block_utils.cc
45
test_call_trace.cc
56
test_callers.cc

tests/test_async_utils.cc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (C) 2020 Swift Navigation Inc.
3+
* Contact: Swift Navigation <dev@swiftnav.com>
4+
*
5+
* This source is subject to the license found in the file 'LICENSE' which must
6+
* be distributed together with this source. All other rights reserved.
7+
*
8+
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
9+
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
10+
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
11+
*/
12+
13+
#include <gtest/gtest.h>
14+
15+
#include <albatross/Indexing>
16+
#include <albatross/utils/AsyncUtils>
17+
#include <albatross/utils/RandomUtils>
18+
19+
#include <chrono>
20+
#include <mutex>
21+
#include <numeric>
22+
23+
namespace albatross {
24+
25+
TEST(test_async_utils, test_async_apply_with_capture) {
26+
std::vector<int> xs = {0, 1, 2, 3, 4, 5};
27+
28+
std::mutex mu;
29+
int sum = 0.;
30+
31+
std::vector<int> order_processed;
32+
33+
auto add_to_sum = [&](const int x) {
34+
std::this_thread::sleep_for(std::chrono::milliseconds(abs(x - 2)));
35+
36+
std::lock_guard<std::mutex> lock(mu);
37+
sum += x;
38+
order_processed.push_back(x);
39+
};
40+
41+
async_apply(xs, add_to_sum);
42+
43+
EXPECT_EQ(sum, std::accumulate(xs.begin(), xs.end(), 0));
44+
// Make sure the async apply was indeed processed out of order.
45+
EXPECT_NE(order_processed, xs);
46+
}
47+
48+
TEST(test_async_utils, test_async_is_faster) {
49+
50+
auto slow_process = [&](const int i) {
51+
const auto start = std::chrono::system_clock::now();
52+
std::chrono::seconds delay(1);
53+
while (std::chrono::system_clock::now() - start < delay) {
54+
};
55+
return i;
56+
};
57+
58+
std::vector<int> inds;
59+
for (std::size_t i = 0; i < 4; ++i) {
60+
inds.push_back(i);
61+
}
62+
63+
const auto start = std::chrono::system_clock::now();
64+
const auto actual = async_apply(inds, slow_process);
65+
const auto end = std::chrono::system_clock::now();
66+
67+
EXPECT_LT(end - start, std::chrono::seconds(inds.size() - 1));
68+
69+
const auto start_direct = std::chrono::system_clock::now();
70+
const auto expected = apply(inds, slow_process);
71+
const auto end_direct = std::chrono::system_clock::now();
72+
73+
EXPECT_EQ(actual, expected);
74+
75+
EXPECT_GT(end_direct - start_direct, std::chrono::seconds(inds.size() - 1));
76+
}
77+
78+
} // namespace albatross

0 commit comments

Comments
 (0)