Browse Source

Lift out a single parallel_run for the whole project

default_compile_flags
vector-of-bool 4 years ago
parent
commit
d88b7787a2
4 changed files with 77 additions and 107 deletions
  1. +1
    -53
      src/dds/build/plan/compile_exec.cpp
  2. +1
    -54
      src/dds/build/plan/full.cpp
  3. +13
    -0
      src/dds/util/parallel.cpp
  4. +62
    -0
      src/dds/util/parallel.hpp

+ 1
- 53
src/dds/build/plan/compile_exec.cpp View File

@@ -3,6 +3,7 @@
#include <dds/build/file_deps.hpp>
#include <dds/error/errors.hpp>
#include <dds/proc.hpp>
#include <dds/util/parallel.hpp>
#include <dds/util/string.hpp>
#include <dds/util/time.hpp>

@@ -20,59 +21,6 @@ using namespace ranges;

namespace {

template <typename Range, typename Fn>
bool parallel_run(Range&& rng, int n_jobs, Fn&& fn) {
// We don't bother with a nice thread pool, as the overhead of most build
// tasks dwarf the cost of interlocking.
std::mutex mut;

auto iter = rng.begin();
const auto stop = rng.end();

std::vector<std::exception_ptr> exceptions;

auto run_one = [&]() mutable {
while (true) {
std::unique_lock lk{mut};
if (!exceptions.empty()) {
break;
}
if (iter == stop) {
break;
}
auto&& item = *iter;
++iter;
lk.unlock();
try {
fn(item);
} catch (...) {
lk.lock();
exceptions.push_back(std::current_exception());
break;
}
}
};

std::unique_lock lk{mut};
std::vector<std::thread> threads;
if (n_jobs < 1) {
n_jobs = std::thread::hardware_concurrency() + 2;
}
std::generate_n(std::back_inserter(threads), n_jobs, [&] { return std::thread(run_one); });
lk.unlock();
for (auto& t : threads) {
t.join();
}
for (auto eptr : exceptions) {
try {
std::rethrow_exception(eptr);
} catch (const std::exception& e) {
spdlog::error(e.what());
}
}
return exceptions.empty();
}

/// The actual "real" information that we need to perform a compilation.
struct compile_file_full {
const compile_file_plan& plan;

+ 1
- 54
src/dds/build/plan/full.cpp View File

@@ -3,6 +3,7 @@
#include <dds/build/iter_compilations.hpp>
#include <dds/build/plan/compile_exec.hpp>
#include <dds/error/errors.hpp>
#include <dds/util/parallel.hpp>

#include <range/v3/view/concat.hpp>
#include <range/v3/view/filter.hpp>
@@ -20,60 +21,6 @@ using namespace dds;

namespace {

/// XXX: Duplicated in compile_exec.cpp !!
template <typename Range, typename Fn>
bool parallel_run(Range&& rng, int n_jobs, Fn&& fn) {
// We don't bother with a nice thread pool, as the overhead of most build
// tasks dwarf the cost of interlocking.
std::mutex mut;

auto iter = rng.begin();
const auto stop = rng.end();

std::vector<std::exception_ptr> exceptions;

auto run_one = [&]() mutable {
while (true) {
std::unique_lock lk{mut};
if (!exceptions.empty()) {
break;
}
if (iter == stop) {
break;
}
auto&& item = *iter;
++iter;
lk.unlock();
try {
fn(item);
} catch (...) {
lk.lock();
exceptions.push_back(std::current_exception());
break;
}
}
};

std::unique_lock lk{mut};
std::vector<std::thread> threads;
if (n_jobs < 1) {
n_jobs = std::thread::hardware_concurrency() + 2;
}
std::generate_n(std::back_inserter(threads), n_jobs, [&] { return std::thread(run_one); });
lk.unlock();
for (auto& t : threads) {
t.join();
}
for (auto eptr : exceptions) {
try {
std::rethrow_exception(eptr);
} catch (const std::exception& e) {
spdlog::error(e.what());
}
}
return exceptions.empty();
}

template <typename T, typename Range>
decltype(auto) pair_up(T& left, Range& right) {
auto rep = ranges::view::repeat(left);

+ 13
- 0
src/dds/util/parallel.cpp View File

@@ -0,0 +1,13 @@
#include "./parallel.hpp"

#include <spdlog/spdlog.h>

using namespace dds;

void dds::log_exception(std::exception_ptr eptr) noexcept {
try {
std::rethrow_exception(eptr);
} catch (const std::exception& e) {
spdlog::error(e.what());
}
}

+ 62
- 0
src/dds/util/parallel.hpp View File

@@ -0,0 +1,62 @@
#pragma once

#include <algorithm>
#include <mutex>
#include <stdexcept>
#include <thread>
#include <vector>

namespace dds {

void log_exception(std::exception_ptr) noexcept;

template <typename Range, typename Func>
bool parallel_run(Range&& rng, int n_jobs, Func&& fn) {
// We don't bother with a nice thread pool, as the overhead of most build
// tasks dwarf the cost of interlocking.
std::mutex mut;

auto iter = rng.begin();
const auto stop = rng.end();

std::vector<std::exception_ptr> exceptions;

auto run_one = [&]() mutable {
while (true) {
std::unique_lock lk{mut};
if (!exceptions.empty()) {
break;
}
if (iter == stop) {
break;
}
auto&& item = *iter;
++iter;
lk.unlock();
try {
fn(item);
} catch (...) {
lk.lock();
exceptions.push_back(std::current_exception());
break;
}
}
};

std::unique_lock lk{mut};
std::vector<std::thread> threads;
if (n_jobs < 1) {
n_jobs = std::thread::hardware_concurrency() + 2;
}
std::generate_n(std::back_inserter(threads), n_jobs, [&] { return std::thread(run_one); });
lk.unlock();
for (auto& t : threads) {
t.join();
}
for (auto eptr : exceptions) {
log_exception(eptr);
}
return exceptions.empty();
}

} // namespace dds

Loading…
Cancel
Save