Skip to content

Commit

Permalink
V3ThreadPool benchmark tool
Browse files Browse the repository at this point in the history
  • Loading branch information
Mariusz Glebocki committed Feb 13, 2024
1 parent 9af87a6 commit e0f3f9b
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 1 deletion.
19 changes: 18 additions & 1 deletion src/Makefile_obj.in
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ THREAD_SAFETY_NEGATIVE_OBJS := \
V3Task.o \
V3Width.o \

$(THREAD_SAFETY_NEGATIVE_OBJS): override CXXFLAGS_PRIV = -Wthread-safety-negative
$(THREAD_SAFETY_NEGATIVE_OBJS): override CXXFLAGS_PRIV := ${CXXFLAGS_PRIV} -Wthread-safety-negative
endif

$(TGT): $(PREDEP_H) $(OBJS)
Expand All @@ -355,6 +355,23 @@ $(TGT): $(PREDEP_H) $(OBJS)
V3Number_test: V3Number_test.o
${LINK} ${LDFLAGS} -o $@ $^ ${LIBS}

# V3ThreadPool_benchmark

V3THREADPOOL_BENCHMARK_OBJS := \
V3ThreadPool_benchmark__V3ThreadPool_benchmark.o \
V3ThreadPool__V3ThreadPool_benchmark.o \

%__V3ThreadPool_benchmark.o: override CPPFLAGS_PRIV := \
${CPPFLAGS_PRIV} \
-DV3OPTION_PARSER_NO_VOPTION_BOOL \
-DV3ERROR_NO_GLOBAL_ \

%__V3ThreadPool_benchmark.o: %.cpp
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} ${CPPFLAGS_PRIV} -c $< -o $@

V3ThreadPool_benchmark: ${V3THREADPOOL_BENCHMARK_OBJS}
${LINK} ${LDFLAGS} -o $@ $^ ${LIBS}

#### Modules

%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
Expand Down
3 changes: 3 additions & 0 deletions src/V3ThreadPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
// c++11 requires definition of static constexpr as well as declaration
constexpr unsigned int V3ThreadPool::FUTUREWAITFOR_MS;

thread_local std::size_t V3ThreadPool::s_threadId{V3ThreadPool::mainThreadId()};

void V3ThreadPool::resize(unsigned n) VL_MT_UNSAFE VL_REQUIRES_UNLOCKED(m_mutex)
VL_REQUIRES_UNLOCKED(m_stoppedJobsMutex) VL_EXCLUDES(V3MtDisabledLock::instance()) {
// At least one thread (main)
Expand Down Expand Up @@ -91,6 +93,7 @@ void V3ThreadPool::resumeMultithreading() VL_MT_SAFE VL_REQUIRES_UNLOCKED(m_mute
void V3ThreadPool::startWorker(V3ThreadPool* selfThreadp, int id) VL_MT_SAFE
VL_REQUIRES_UNLOCKED(selfThreadp->m_stoppedJobsMutex)
VL_REQUIRES_UNLOCKED(selfThreadp->m_mutex) {
s_threadId = id;
selfThreadp->workerJobLoop(id);
}

Expand Down
9 changes: 9 additions & 0 deletions src/V3ThreadPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ class V3ThreadPool final {

std::atomic_bool m_shutdown{false}; // Termination pending

// Sequential thread index.
static thread_local std::size_t s_threadId;

// Indicates whether multithreading has been suspended.
// Used for error detection in resumeMultithreading only. You probably should use
// m_exclusiveAccess for information whether something should be run in current thread.
Expand Down Expand Up @@ -165,6 +168,12 @@ class V3ThreadPool final {
return s_s;
}

// Returns sequential thread index.
// Main thread has index 0, first worker thread has index 1, etc.
static std::size_t currentThreadId() VL_MT_SAFE { return s_threadId; }

static constexpr std::size_t mainThreadId() VL_PURE { return 0; }

// Resize thread pool to n workers (queue must be empty)
void resize(unsigned n) VL_MT_UNSAFE VL_REQUIRES_UNLOCKED(m_mutex)
VL_REQUIRES_UNLOCKED(m_stoppedJobsMutex) VL_EXCLUDES(V3MtDisabledLock::instance());
Expand Down
154 changes: 154 additions & 0 deletions src/V3ThreadPool_benchmark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: V3ThreadPool benchmark
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// Building and running:
//
// mkdir -p src/obj_opt
// make -C src/obj_opt -f ../Makefile_obj V3ThreadPool_benchmark
// ./src/obj_opt/V3ThreadPool_benchmark -timeout 5 -threads 2
//

// clang-format off
#include "config_build.h"
#ifndef HAVE_CONFIG_PACKAGE
# error "Something failed during ./configure as config_package.h is incomplete. Perhaps you used autoreconf, don't."
#endif
// clang-format on

#include "verilatedos.h"

// Cheat for speed and compile .cpp files into one object TODO: Reconsider
#ifndef V3ERROR_NO_GLOBAL_
#define V3ERROR_NO_GLOBAL_
#endif

#ifndef V3OPTION_PARSER_NO_VOPTION_BOOL
#define V3OPTION_PARSER_NO_VOPTION_BOOL
#endif

#include "V3Error.h"
static int debug() { return V3Error::debugDefault(); }
#include "V3OptionParser.h"
#include "V3ThreadPool.h"

#include <atomic>
#include <chrono>
#include <cstdio>
#include <memory>
#include <vector>

// clang-format off
#include "V3Error.cpp"
#include "V3String.cpp"
#include "V3OptionParser.cpp"
#include "V3Os.cpp"
#include "V3Mutex.h"
// clang-format on

struct Options {
int m_threads = 2;
int m_timeout = 5;

void parseOptsList(int argc, char** argv);
};

void Options::parseOptsList(int argc, char** argv) VL_MT_DISABLED {
V3OptionParser parser;
V3OptionParser::AppendHelper DECL_OPTION{parser};
V3OPTION_PARSER_DECL_TAGS;

DECL_OPTION("-threads", Set, &m_threads);
DECL_OPTION("-timeout", Set, &m_timeout);
parser.finalize();
for (int i = 0; i < argc;) {
if (argv[i][0] == '-') {
if (const int consumed = parser.parse(i, argc, argv)) {
i += consumed;
continue;
}
}
v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i]));
++i; // LCOV_EXCL_LINE
}
}

int main(int argc, char** argv) {
// General initialization
std::ios::sync_with_stdio();

Options opt;
{
const V3MtDisabledLockGuard mtDisabler{v3MtDisabledLock()};
opt.parseOptsList(argc - 1, argv + 1);

UASSERT(opt.m_threads >= 0, "Invalid threads count");
UASSERT(opt.m_timeout >= 0, "Invalid timeout");

V3MutexConfig::s().configure(true);
}

V3ThreadPool::s().resize(opt.m_threads + 1);
std::unique_ptr<std::atomic_uint64_t[]> counters
= std::make_unique<std::atomic_uint64_t[]>(opt.m_threads + 1);

uint64_t scheduled_jobs = 0;

bool stop = false;
V3SharedMutex stopLock;

std::deque<std::future<void>> futures;

{
const auto expiration
= std::chrono::high_resolution_clock::now() + std::chrono::seconds(opt.m_timeout);

while (std::chrono::high_resolution_clock::now() < expiration) {
for (int i = 0; i < 100; ++i) {
auto f = V3ThreadPool::s().enqueue([counters = counters.get(), &stop, &stopLock] {
static thread_local uint64_t counter = 0;
V3SharedLockGuard l{stopLock};
if (!stop) {
++counter;
counters[V3ThreadPool::currentThreadId()].store(counter,
std::memory_order_release);
}
});
futures.push_back(std::move(f));
}
scheduled_jobs += 100;
}

V3ExclusiveLockGuard l{stopLock};
stop = true;
}
uint64_t executed_jobs = 0;
for (int i = 0; i < opt.m_threads + 1; ++i) {
executed_jobs += counters[i].load(std::memory_order_consume);
}

std::printf("Elapsed time [s]: %10d\n", opt.m_timeout);
std::printf("Worker threads: %10d\n", opt.m_threads);
std::printf("Scheduled jobs: %10lu\n", scheduled_jobs);
std::printf("Executed jobs: %10lu (%.6f%%)\n", executed_jobs,
1.0 * executed_jobs / scheduled_jobs * 100.0);
std::printf("Avg exec. jobs/thread: %10.0f\n",
1.0 * executed_jobs / std::max(1, opt.m_threads));
std::printf("Avg time/job [µs]: %10.3f\n", opt.m_timeout * 1000000.0 / executed_jobs);
std::printf("Avg thread*time/job [µs]:%10.3f\n",
opt.m_timeout * std::max(1, opt.m_threads) * 1000000.0 / executed_jobs);

return 0;
}

0 comments on commit e0f3f9b

Please sign in to comment.