Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ jobs:
- name: Init submodules
run: |
git submodule update --init --depth=1 \
contrib/libbacktrace \
contrib/librseq \
contrib/liburing \
contrib/googletest \
contrib/benchmark \
contrib/bpftool \
contrib/cxxopts \
contrib/googletest \
contrib/libbacktrace \
contrib/libbpf \
contrib/bpftool
contrib/librseq \
contrib/liburing

- name: Init Poco submodule
if: matrix.build.name == 'release' || matrix.build.name == 'tsan'
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@
[submodule "contrib/bpftool"]
path = contrib/bpftool
url = https://github.com/libbpf/bpftool.git
[submodule "contrib/cxxopts"]
path = contrib/cxxopts
url = https://github.com/jarro2783/cxxopts.git
12 changes: 7 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
endif()

find_package(Boost REQUIRED context program_options)
find_package(Boost REQUIRED context)

if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
add_compile_options(-march=x86-64-v3)
Expand Down Expand Up @@ -85,6 +85,7 @@ endif()

add_subdirectory(contrib/liburing-cmake)
add_subdirectory(contrib/librseq-cmake)
add_subdirectory(contrib/cxxopts-cmake)
add_subdirectory(contrib/googletest-cmake)
add_subdirectory(contrib/benchmark-cmake)
add_subdirectory(contrib/systemtap-sdt)
Expand Down Expand Up @@ -133,11 +134,12 @@ add_subdirectory(src/util)
add_subdirectory(src/fibers)
add_subdirectory(src/gdb)

# Perf and profiler targets may pull in system libs with libstdc++ ABI
# (Poco, AWS SDK) that conflict with the instrumented libc++ used in MSan
# builds. Boost::context (used by silk) is assembly-level and has no
# std::string in its ABI, so it works with libc++ despite being a system
# library.
if(NOT SANITIZER STREQUAL "memory")
# perf targets link against system Boost::program_options (libstdc++ ABI),
# which is incompatible with the instrumented libc++ used in MSan builds.
# Boost::context (used by silk) is assembly-level and has no std::string
# in its ABI, so it works with libc++ despite being a system library.
add_subdirectory(src/perf)
if(BUILD_LIBBACKTRACE)
# symbolizer.cpp depends directly on <backtrace.h>.
Expand Down
7 changes: 4 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ Initialize the required submodules after cloning:

```
git submodule update --init --depth=1 \
contrib/benchmark \
contrib/cxxopts \
contrib/googletest \
contrib/libbacktrace \
contrib/librseq \
contrib/liburing \
contrib/googletest \
contrib/benchmark
contrib/liburing
```

To build optional components, initialize their submodules too:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ A cooperative fiber scheduler for Linux with per-CPU scheduler threads, io_uring
- Ninja
- Clang 21
- ccache (optional)
- Boost (`libboost-dev`, `libboost-context-dev`, `libboost-program-options-dev`)
- Boost (`libboost-dev`, `libboost-context-dev`)
- libelf (`libelf-dev`) — optional, required only for `src/profiler`; the profiler is silently skipped if absent.

GTest, Google Benchmark, libbacktrace, liburing, librseq, libbpf, and bpftool are bundled as submodules under `contrib/` and do not need to be installed separately. Poco, the AWS SDK, and jemalloc are built on demand via `--build-poco`, `--build-aws`, and `--build-jemalloc` passed to `configure`.
GTest, Google Benchmark, libbacktrace, liburing, librseq, libbpf, bpftool, and cxxopts are bundled as submodules under `contrib/` and do not need to be installed separately. Poco, the AWS SDK, and jemalloc are built on demand via `--build-poco`, `--build-aws`, and `--build-jemalloc` passed to `configure`.

Runtime dependencies for optional benchmarks: nginx (only for `http-perf --nginx`; the default uses an internal Poco-based server built into the `http-perf` binary), fio (for `fio-perf`), and MinIO (for `s3-perf`). MinIO is downloaded automatically to `.tools/` if not in PATH; the others must be installed separately.

Expand Down
1 change: 1 addition & 0 deletions bb
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ def _run_flamegraph(preset: str, name: str, client_args: list[str]) -> None:
profiler_bin,
"--pid",
str(client.pid),
"--on-cpu",
"--off-cpu",
"--kernel-stacks",
*verbose_flag,
Expand Down
1 change: 1 addition & 0 deletions contrib/cxxopts
Submodule cxxopts added at 44380e
6 changes: 6 additions & 0 deletions contrib/cxxopts-cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
set(CXXOPTS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../cxxopts)

add_library(cxxopts INTERFACE)
target_include_directories(cxxopts SYSTEM INTERFACE ${CXXOPTS_SRC}/include)

add_library(cxxopts::cxxopts ALIAS cxxopts)
12 changes: 6 additions & 6 deletions src/perf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ add_library(silk-perf STATIC common.cpp)
target_link_libraries(silk-perf PUBLIC silk-fibers)

add_executable(file-perf file-perf.cpp)
target_link_libraries(file-perf PRIVATE silk-perf Boost::program_options)
target_link_libraries(file-perf PRIVATE silk-perf cxxopts::cxxopts)

add_executable(net-perf net-perf.cpp)
target_link_libraries(net-perf PRIVATE silk-perf Boost::program_options)
target_link_libraries(net-perf PRIVATE silk-perf cxxopts::cxxopts)

add_executable(net-perf-epoll net-perf-epoll.cpp)
target_link_libraries(net-perf-epoll PRIVATE silk-perf Boost::program_options)
target_link_libraries(net-perf-epoll PRIVATE silk-perf cxxopts::cxxopts)

# asio::awaitable requires Boost >= 1.75
if(Boost_VERSION_STRING VERSION_GREATER_EQUAL "1.75")
add_executable(net-perf-asio net-perf-asio.cpp)
target_link_libraries(net-perf-asio PRIVATE silk-perf Boost::program_options)
target_link_libraries(net-perf-asio PRIVATE silk-perf cxxopts::cxxopts)

if(BUILD_JEMALLOC)
target_link_libraries(net-perf-asio PRIVATE jemalloc::jemalloc)
Expand All @@ -25,7 +25,7 @@ if(BUILD_POCO)
target_link_libraries(silk-perf-http PUBLIC silk-perf Poco::Net Poco::Foundation)

add_executable(http-perf http-perf.cpp)
target_link_libraries(http-perf PRIVATE silk-perf-http Boost::program_options)
target_link_libraries(http-perf PRIVATE silk-perf-http cxxopts::cxxopts)

if(BUILD_JEMALLOC)
target_link_libraries(http-perf PRIVATE jemalloc::jemalloc)
Expand All @@ -34,7 +34,7 @@ endif()

if(BUILD_AWS)
add_executable(s3-perf s3-perf.cpp)
target_link_libraries(s3-perf PRIVATE silk-perf-http Boost::program_options aws-cpp-sdk-s3 aws-cpp-sdk-core)
target_link_libraries(s3-perf PRIVATE silk-perf-http cxxopts::cxxopts aws-cpp-sdk-s3 aws-cpp-sdk-core)

if(BUILD_JEMALLOC)
target_link_libraries(s3-perf PRIVATE jemalloc::jemalloc)
Expand Down
49 changes: 25 additions & 24 deletions src/perf/file-perf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#include <silk/util/platform.h>
#include <silk/util/tsc.h>

#include <boost/program_options.hpp>

#include <atomic>
#include <cerrno>
#include <cmath>
Expand All @@ -26,6 +24,7 @@
#include <pthread.h>
#include <unistd.h>

#include <cxxopts.hpp>
#include <sys/stat.h>

//
Expand Down Expand Up @@ -346,44 +345,46 @@ int main(int argc, char ** argv)
std::string warmupStr = "2s";
bool verbose = false;

namespace po = boost::program_options;
po::options_description desc("file-perf options");
cxxopts::Options cli("file-perf", "file-perf options");

// clang-format off
desc.add_options()
("help,h", "show this help")
("numjobs", po::value(&cfg.numJobs), "number of concurrent worker fibers")
("iodepth", po::value(&cfg.iodepth), "per-fiber IO queue depth")
("bs", po::value(&bsStr), "block size (e.g. 4k, 1m)")
("rw", po::value(&rwStr), "I/O mode: randread | seqread | randwrite")
("size", po::value(&sizeStr), "file size (e.g. 1g, 512m)")
("runtime", po::value(&runtimeStr), "measurement duration (e.g. 10s, 500ms)")
("warmup", po::value(&warmupStr), "warmup duration (e.g. 2s, 500ms)")
("filename", po::value(&cfg.filename)->required(), "file path")
("direct", po::bool_switch(&cfg.direct), "use O_DIRECT (bypass page cache)")
("print-counters", po::bool_switch(&cfg.printCounters), "enable per-CPU profiler and include counters in the JSON report")
("verbose,v", po::bool_switch(&verbose), "enable debug logging")
cli.add_options()
("h,help", "show this help")
("numjobs", "number of concurrent worker fibers", cxxopts::value<uint32_t>(cfg.numJobs))
("iodepth", "per-fiber IO queue depth", cxxopts::value<uint32_t>(cfg.iodepth))
("bs", "block size (e.g. 4k, 1m)", cxxopts::value<std::string>(bsStr))
("rw", "I/O mode: randread | seqread | randwrite", cxxopts::value<std::string>(rwStr))
("size", "file size (e.g. 1g, 512m)", cxxopts::value<std::string>(sizeStr))
("runtime", "measurement duration (e.g. 10s, 500ms)", cxxopts::value<std::string>(runtimeStr))
("warmup", "warmup duration (e.g. 2s, 500ms)", cxxopts::value<std::string>(warmupStr))
("filename", "file path", cxxopts::value<std::string>(cfg.filename))
("direct", "use O_DIRECT (bypass page cache)", cxxopts::value<bool>(cfg.direct))
("print-counters", "enable per-CPU profiler and include counters in the JSON report", cxxopts::value<bool>(cfg.printCounters))
("v,verbose", "enable debug logging", cxxopts::value<bool>(verbose))
;
// clang-format on

po::variables_map vm;
try
{
po::store(po::parse_command_line(argc, argv, desc), vm);
if (vm.count("help"))
auto result = cli.parse(argc, argv);
if (result.count("help"))
{
std::cout << "usage: file-perf [options]\n" << desc << "\n";
std::cout << cli.help() << "\n";
return 0;
}
po::notify(vm);
if (result.count("filename") == 0)
{
std::cerr << "error: --filename is required\n" << cli.help() << "\n";
return 1;
}
if (verbose)
{
silk::Logger::setLevel(silk::LogLevel::DEBUG);
}
}
catch (const po::error & ex)
catch (const cxxopts::exceptions::exception & ex)
{
std::cerr << "error: " << ex.what() << "\n" << desc << "\n";
std::cerr << "error: " << ex.what() << "\n" << cli.help() << "\n";
return 1;
}

Expand Down
69 changes: 32 additions & 37 deletions src/perf/http-perf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include <Poco/Net/NetException.h>
#include <Poco/Net/ServerSocket.h>
#include <Poco/Net/StreamSocket.h>
#include <boost/program_options.hpp>

#include <atomic>
#include <chrono>
Expand All @@ -43,6 +42,8 @@

#include <pthread.h>

#include <cxxopts.hpp>

//
// Client
//
Expand Down Expand Up @@ -256,43 +257,40 @@ static void runClient(int argc, char ** argv)
std::string warmupStr = "2s";
bool verbose = false;

namespace po = boost::program_options;
po::options_description desc("http-perf client options");
cxxopts::Options cli("http-perf client", "http-perf client options");

// clang-format off
desc.add_options()
("help,h", "show this help")
("host", po::value(&cfg.host), "server host")
("port", po::value(&cfg.port), "server port")
("connections", po::value(&cfg.numConnections), "parallel connections or threads")
("threads", po::bool_switch(&cfg.useThreads), "use OS threads instead of fibers")
("duration", po::value(&durationStr), "measurement duration (e.g. 10s, 500ms)")
("warmup", po::value(&warmupStr), "warmup duration (e.g. 2s, 500ms)")
("print-counters", po::bool_switch(&cfg.printCounters), "enable per-CPU profiler and include counters in the JSON report")
("verbose,v", po::bool_switch(&verbose), "enable debug logging")
cli.add_options()
("h,help", "show this help")
("host", "server host", cxxopts::value<std::string>(cfg.host))
("port", "server port", cxxopts::value<uint16_t>(cfg.port))
("connections", "parallel connections or threads", cxxopts::value<uint32_t>(cfg.numConnections))
("threads", "use OS threads instead of fibers", cxxopts::value<bool>(cfg.useThreads))
("duration", "measurement duration (e.g. 10s, 500ms)", cxxopts::value<std::string>(durationStr))
("warmup", "warmup duration (e.g. 2s, 500ms)", cxxopts::value<std::string>(warmupStr))
("print-counters", "enable per-CPU profiler and include counters in the JSON report", cxxopts::value<bool>(cfg.printCounters))
("v,verbose", "enable debug logging", cxxopts::value<bool>(verbose))
;
// clang-format on

po::variables_map vm;
try
{
po::store(po::parse_command_line(argc, argv, desc), vm);
if (vm.count("help"))
auto result = cli.parse(argc, argv);
if (result.count("help"))
{
std::cout << "usage: http-perf client [options]\n" << desc << "\n";
std::cout << cli.help() << "\n";
return;
}
po::notify(vm);
cfg.durationNs = parseDuration(durationStr);
cfg.warmupNs = parseDuration(warmupStr);
if (verbose)
{
silk::Logger::setLevel(silk::LogLevel::DEBUG);
}
}
catch (const po::error & ex)
catch (const cxxopts::exceptions::exception & ex)
{
std::cerr << "error: " << ex.what() << "\n" << desc << "\n";
std::cerr << "error: " << ex.what() << "\n" << cli.help() << "\n";
exit(1);
}

Expand Down Expand Up @@ -606,40 +604,37 @@ static void runServer(int argc, char ** argv)
std::string delayStr = "0";
bool verbose = false;

namespace po = boost::program_options;
po::options_description desc("http-perf server options");
cxxopts::Options cli("http-perf server", "http-perf server options");

// clang-format off
desc.add_options()
("help,h", "show this help")
("port", po::value(&cfg.port), "listen port")
("queued", po::value(&cfg.maxQueued), "max queued connections (default: 4 * available CPUs)")
("delay", po::value(&delayStr), "per-request response delay (e.g. 5ms, 100us)")
("threads", po::bool_switch(&cfg.useThreads), "use OS threads instead of fibers")
("print-counters", po::bool_switch(&cfg.printCounters), "enable per-CPU profiler and include counters in the JSON report")
("verbose,v", po::bool_switch(&verbose), "enable debug logging")
cli.add_options()
("h,help", "show this help")
("port", "listen port", cxxopts::value<uint16_t>(cfg.port))
("queued", "max queued connections (default: 4 * available CPUs)", cxxopts::value<uint32_t>(cfg.maxQueued))
("delay", "per-request response delay (e.g. 5ms, 100us)", cxxopts::value<std::string>(delayStr))
("threads", "use OS threads instead of fibers", cxxopts::value<bool>(cfg.useThreads))
("print-counters", "enable per-CPU profiler and include counters in the JSON report", cxxopts::value<bool>(cfg.printCounters))
("v,verbose", "enable debug logging", cxxopts::value<bool>(verbose))
;
// clang-format on

po::variables_map vm;
try
{
po::store(po::parse_command_line(argc, argv, desc), vm);
if (vm.count("help"))
auto result = cli.parse(argc, argv);
if (result.count("help"))
{
std::cout << "usage: http-perf server [options]\n" << desc << "\n";
std::cout << cli.help() << "\n";
return;
}
po::notify(vm);
cfg.delayNs = parseDuration(delayStr);
if (verbose)
{
silk::Logger::setLevel(silk::LogLevel::DEBUG);
}
}
catch (const po::error & ex)
catch (const cxxopts::exceptions::exception & ex)
{
std::cerr << "error: " << ex.what() << "\n" << desc << "\n";
std::cerr << "error: " << ex.what() << "\n" << cli.help() << "\n";
exit(1);
}

Expand Down
Loading
Loading