Skip to content

Commit 705cf74

Browse files
committed
Run ABCs in parallel
1 parent 715511b commit 705cf74

File tree

5 files changed

+294
-39
lines changed

5 files changed

+294
-39
lines changed

Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ LINK_ABC := 0
4343
# Needed for environments that can't run executables (i.e. emscripten, wasm)
4444
DISABLE_SPAWN := 0
4545
# Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now)
46+
ENABLE_THREADS := 1
47+
ifeq ($(ENABLE_THREADS),1)
4648
DISABLE_ABC_THREADS := 0
49+
else
50+
DISABLE_ABC_THREADS := 1
51+
endif
4752

4853
# clang sanitizers
4954
SANITIZER =
@@ -301,6 +306,7 @@ DISABLE_SPAWN := 1
301306

302307
ifeq ($(ENABLE_ABC),1)
303308
LINK_ABC := 1
309+
ENABLE_THREADS := 0
304310
DISABLE_ABC_THREADS := 1
305311
endif
306312

@@ -458,6 +464,11 @@ CXXFLAGS := -Og -DDEBUG $(filter-out $(OPT_LEVEL),$(CXXFLAGS))
458464
STRIP :=
459465
endif
460466

467+
ifeq ($(ENABLE_THREADS),1)
468+
CXXFLAGS += -DYOSYS_ENABLE_THREADS
469+
LIBS += -lpthread
470+
endif
471+
461472
ifeq ($(ENABLE_ABC),1)
462473
CXXFLAGS += -DYOSYS_ENABLE_ABC
463474
ifeq ($(LINK_ABC),1)
@@ -614,6 +625,7 @@ $(eval $(call add_include_file,kernel/satgen.h))
614625
$(eval $(call add_include_file,kernel/scopeinfo.h))
615626
$(eval $(call add_include_file,kernel/sexpr.h))
616627
$(eval $(call add_include_file,kernel/sigtools.h))
628+
$(eval $(call add_include_file,kernel/threading.h))
617629
$(eval $(call add_include_file,kernel/timinginfo.h))
618630
$(eval $(call add_include_file,kernel/utils.h))
619631
$(eval $(call add_include_file,kernel/yosys.h))
@@ -636,7 +648,7 @@ $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
636648
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o kernel/io.o kernel/gzip.o
637649
OBJS += kernel/binding.o kernel/tclapi.o
638650
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o kernel/sexpr.o
639-
OBJS += kernel/drivertools.o kernel/functional.o
651+
OBJS += kernel/drivertools.o kernel/functional.o kernel/threading.o
640652
ifeq ($(ENABLE_ZLIB),1)
641653
OBJS += kernel/fstdata.o
642654
endif

kernel/threading.cc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include "kernel/yosys_common.h"
2+
#include "kernel/threading.h"
3+
4+
YOSYS_NAMESPACE_BEGIN
5+
6+
void DeferredLogs::flush()
7+
{
8+
for (auto &m : logs)
9+
if (m.error)
10+
YOSYS_NAMESPACE_PREFIX log_error("%s", m.text.c_str());
11+
else
12+
YOSYS_NAMESPACE_PREFIX log("%s", m.text.c_str());
13+
}
14+
15+
int ThreadPool::pool_size(int reserved_cores, int max_threads)
16+
{
17+
#ifdef YOSYS_ENABLE_THREADS
18+
int num_threads = std::min<int>(std::thread::hardware_concurrency() - reserved_cores, max_threads);
19+
return std::max(0, num_threads);
20+
#else
21+
return 0;
22+
#endif
23+
}
24+
25+
ThreadPool::ThreadPool(int pool_size, std::function<void(int)> body)
26+
{
27+
#ifdef YOSYS_ENABLE_THREADS
28+
threads.reserve(pool_size);
29+
for (int i = 0; i < pool_size; i++)
30+
threads.emplace_back([i, &body]{ body(i); });
31+
#else
32+
log_assert(pool_size == 0);
33+
#endif
34+
}
35+
36+
ThreadPool::~ThreadPool()
37+
{
38+
#ifdef YOSYS_ENABLE_THREADS
39+
for (auto &t : threads)
40+
t.join();
41+
#endif
42+
}
43+
44+
YOSYS_NAMESPACE_END

kernel/threading.h

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#ifdef YOSYS_ENABLE_THREADS
2+
#include <condition_variable>
3+
#include <deque>
4+
#include <mutex>
5+
#include <thread>
6+
#endif
7+
8+
#include "kernel/yosys_common.h"
9+
#include "kernel/log.h"
10+
11+
#ifndef YOSYS_THREADING_H
12+
#define YOSYS_THREADING_H
13+
14+
YOSYS_NAMESPACE_BEGIN
15+
16+
// Concurrent queue implementation. Not fast, but simple.
17+
// Multi-producer, multi-consumer, optionally bounded.
18+
// When YOSYS_ENABLE_THREADS is not defined, this is just a non-thread-safe non-blocking deque.
19+
template <typename T>
20+
class ConcurrentQueue
21+
{
22+
public:
23+
ConcurrentQueue(int capacity = INT_MAX)
24+
: capacity(capacity) {}
25+
// Push an element into the queue. If it's at capacity, block until there is room.
26+
void push_back(T t)
27+
{
28+
#ifdef YOSYS_ENABLE_THREADS
29+
std::unique_lock<std::mutex> lock(mutex);
30+
not_full_condition.wait(lock, [this] { return static_cast<int>(contents.size()) < capacity; });
31+
if (contents.empty())
32+
not_empty_condition.notify_one();
33+
#endif
34+
log_assert(!closed);
35+
contents.push_back(std::move(t));
36+
#ifdef YOSYS_ENABLE_THREADS
37+
if (static_cast<int>(contents.size()) < capacity)
38+
not_full_condition.notify_one();
39+
#endif
40+
}
41+
// Signal that no more elements will be produced. `pop_front()` will return nullopt.
42+
void close()
43+
{
44+
#ifdef YOSYS_ENABLE_THREADS
45+
std::unique_lock<std::mutex> lock(mutex);
46+
not_empty_condition.notify_all();
47+
#endif
48+
closed = true;
49+
}
50+
// Pop an element from the queue. Blocks until an element is available
51+
// or the queue is closed and empty.
52+
std::optional<T> pop_front()
53+
{
54+
return pop_front_internal(true);
55+
}
56+
// Pop an element from the queue. Does not block, just returns nullopt if the
57+
// queue is empty.
58+
std::optional<T> try_pop_front()
59+
{
60+
return pop_front_internal(false);
61+
}
62+
private:
63+
std::optional<T> pop_front_internal(bool wait = false)
64+
{
65+
#ifdef YOSYS_ENABLE_THREADS
66+
std::unique_lock<std::mutex> lock(mutex);
67+
if (wait) {
68+
not_empty_condition.wait(lock, [this] { return !contents.empty() || closed; });
69+
}
70+
if (contents.empty())
71+
return std::nullopt;
72+
if (static_cast<int>(contents.size()) == capacity)
73+
not_full_condition.notify_one();
74+
#endif
75+
T result = std::move(contents.front());
76+
contents.pop_front();
77+
#ifdef YOSYS_ENABLE_THREADS
78+
if (!contents.empty())
79+
not_empty_condition.notify_one();
80+
#endif
81+
return std::move(result);
82+
}
83+
84+
#ifdef YOSYS_ENABLE_THREADS
85+
std::mutex mutex;
86+
// Signals one waiter thread when the queue changes and is not full.
87+
std::condition_variable not_full_condition;
88+
// Signals one waiter thread when the queue changes and is not empty.
89+
std::condition_variable not_empty_condition;
90+
#endif
91+
std::deque<T> contents;
92+
int capacity;
93+
bool closed = false;
94+
};
95+
96+
class DeferredLogs
97+
{
98+
public:
99+
template <typename... Args>
100+
void log(FmtString<TypeIdentity<Args>...> fmt, Args... args)
101+
{
102+
logs.push_back({fmt.format(args...), false});
103+
}
104+
template <typename... Args>
105+
void log_error(FmtString<TypeIdentity<Args>...> fmt, Args... args)
106+
{
107+
logs.push_back({fmt.format(args...), true});
108+
}
109+
void flush();
110+
private:
111+
struct Message
112+
{
113+
std::string text;
114+
bool error;
115+
};
116+
std::vector<Message> logs;
117+
};
118+
119+
class ThreadPool
120+
{
121+
public:
122+
// Computes the number of worker threads to use.
123+
// `reserved_cores` cores are set aside for other threads (e.g. work on the main thread).
124+
// `max_threads` --- don't return more workers than this.
125+
// The result may be 0.
126+
static int pool_size(int reserved_cores, int max_threads);
127+
128+
// Create a pool of threads running the given closure (parameterized by thread number).
129+
// `pool_size` must be the result of a `pool_size()` call.
130+
ThreadPool(int pool_size, std::function<void(int)> body);
131+
// Waits for all threads to terminate. Make sure those closures return!
132+
~ThreadPool();
133+
134+
// Return the number of threads in the pool.
135+
int num_threads() const
136+
{
137+
#ifdef YOSYS_ENABLE_THREADS
138+
return threads.size();
139+
#else
140+
return 0;
141+
#endif
142+
}
143+
private:
144+
#ifdef YOSYS_ENABLE_THREADS
145+
std::vector<std::thread> threads;
146+
#endif
147+
};
148+
149+
YOSYS_NAMESPACE_END
150+
151+
#endif // YOSYS_THREADING_H

kernel/yosys.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ int run_command(const std::string &command, std::function<void(const std::string
177177

178178
int ret = pclose(f);
179179
if (ret < 0)
180-
return -1;
180+
return -2;
181181
#ifdef _WIN32
182182
return ret;
183183
#else

0 commit comments

Comments
 (0)