Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add iouring file sink #558

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ option(QUILL_NO_THREAD_NAME_SUPPORT "Enable this option to disable features that

option(QUILL_X86ARCH "Enable x86-specific optimizations for cache coherence using _mm_prefetch, _mm_clflush, and _mm_clflushopt instructions. Ensure the target architecture must also be specified with -march=\"...\" when enabling this option." OFF)

option(QUILL_ENABLE_IOURING "Enable io_uring sinks for the backend. Requires liburing and a recent Linux kernel (Linux-only feature)." OFF)

option(QUILL_DISABLE_NON_PREFIXED_MACROS "Enable this option to disable non-prefixed `LOG_*` macros, keeping only the `QUILL_LOG_*` macros to avoid conflicts with other logging libraries." OFF)

option(QUILL_BUILD_EXAMPLES "Enable this option to build and install the examples. Set this to ON to include example projects in the build process and have them installed after configuring with CMake." OFF)
Expand Down Expand Up @@ -209,6 +211,7 @@ set(HEADER_FILES

include/quill/sinks/ConsoleSink.h
include/quill/sinks/FileSink.h
include/quill/sinks/IOUringFileSink.h
include/quill/sinks/JsonConsoleSink.h
include/quill/sinks/JsonFileSink.h
include/quill/sinks/NullSink.h
Expand Down Expand Up @@ -281,6 +284,14 @@ if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
target_link_libraries(${TARGET_NAME} PUBLIC INTERFACE stdc++fs)
endif ()

if (QUILL_ENABLE_IOURING)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBURING REQUIRED liburing)

target_include_directories(${TARGET_NAME} INTERFACE ${LIBURING_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} INTERFACE ${LIBURING_LIBRARIES})
endif ()

# Add include directories for this library
target_include_directories(${TARGET_NAME}
INTERFACE
Expand Down
8 changes: 7 additions & 1 deletion benchmarks/backend_throughput/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ target_link_libraries(BENCHMARK_quill_backend_throughput quill)

add_executable(BENCHMARK_quill_backend_throughput_no_buffering quill_backend_throughput_no_buffering.cpp)
set_common_compile_options(BENCHMARK_quill_backend_throughput_no_buffering)
target_link_libraries(BENCHMARK_quill_backend_throughput_no_buffering quill)
target_link_libraries(BENCHMARK_quill_backend_throughput_no_buffering quill)

if (QUILL_ENABLE_IOURING)
add_executable(BENCHMARK_quill_backend_iouring_throughput quill_backend_iouring_throughput.cpp)
set_common_compile_options(BENCHMARK_quill_backend_iouring_throughput)
target_link_libraries(BENCHMARK_quill_backend_iouring_throughput quill)
endif ()
67 changes: 67 additions & 0 deletions benchmarks/backend_throughput/quill_backend_iouring_throughput.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include <chrono>
#include <iostream>

#include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/sinks/IOUringFileSink.h"

static constexpr size_t total_iterations = 4'000'000;

/**
* The backend worker just spins, so we just measure the total time elapsed for total_iterations
*/
int main()
{
// main thread affinity
quill::detail::set_cpu_affinity(1);

quill::BackendOptions backend_options;
backend_options.cpu_affinity = 5;
backend_options.sleep_duration = std::chrono::nanoseconds{0};

// Start the logging backend thread and give it some tiem to init
quill::Backend::start(backend_options);

// Create a file sink to write to a file
std::shared_ptr<quill::Sink> file_sink = quill::Frontend::create_or_get_sink<quill::IOUringFileSink>(
"quill_backend_total_time.log",
[]()
{
quill::FileSinkConfig cfg;
cfg.set_open_mode('w');
return cfg;
}(),
quill::FileEventNotifier{});

quill::Logger* logger = quill::Frontend::create_or_get_logger(
"bench_logger", std::move(file_sink),
quill::PatternFormatterOptions{
"%(time) [%(thread_id)] %(short_source_location) %(log_level) %(message)", "%H:%M:%S.%Qns",
quill::Timezone::LocalTime, false});

LOG_INFO(logger, "preallocate");
logger->flush_log(0);

// start counting the time until backend worker finishes
auto const start_time = std::chrono::steady_clock::now();
for (size_t iteration = 0; iteration < total_iterations; ++iteration)
{
LOG_INFO(logger, "Iteration: {} int: {} double: {}", iteration, iteration * 2,
static_cast<double>(iteration) / 2);
}

// block until all messages are flushed
logger->flush_log(0);

auto const end_time = std::chrono::steady_clock::now();
auto const delta = end_time - start_time;
auto delta_d = std::chrono::duration_cast<std::chrono::duration<double>>(delta).count();

std::cout << fmtquill::format(
"Throughput is {:.2f} million msgs/sec average, total time elapsed: {} ms for {} "
"log messages \n",
total_iterations / delta_d / 1e6,
std::chrono::duration_cast<std::chrono::milliseconds>(delta).count(), total_iterations)
<< std::endl;
}
8 changes: 7 additions & 1 deletion benchmarks/hot_path_latency/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ target_link_libraries(BENCHMARK_quill_hot_path_rdtsc_clock quill)

add_executable(BENCHMARK_quill_hot_path_system_clock hot_path_bench_config.h hot_path_bench.h quill_hot_path_system_clock.cpp)
set_common_compile_options(BENCHMARK_quill_hot_path_system_clock)
target_link_libraries(BENCHMARK_quill_hot_path_system_clock quill)
target_link_libraries(BENCHMARK_quill_hot_path_system_clock quill)

if (QUILL_ENABLE_IOURING)
add_executable(BENCHMARK_quill_hot_path_rdtsc_clock_iouring quill_hot_path_rdtsc_clock_io_uring.cpp)
set_common_compile_options(BENCHMARK_quill_hot_path_rdtsc_clock_iouring)
target_link_libraries(BENCHMARK_quill_hot_path_rdtsc_clock_iouring quill)
endif ()
5 changes: 0 additions & 5 deletions benchmarks/hot_path_latency/quill_hot_path_rdtsc_clock.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* Adding a benchmark for a another logger should be straight forward by duplicating and modifying
* this file.
*/

#include "hot_path_bench.h"

#include "quill/Backend.h"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "hot_path_bench.h"

#include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/sinks/IOUringFileSink.h"

struct FrontendOptions
{
static constexpr quill::QueueType queue_type = quill::QueueType::UnboundedBlocking;
static constexpr uint32_t initial_queue_capacity = 131'072;
static constexpr uint32_t blocking_queue_retry_interval_ns = 800;
static constexpr bool huge_pages_enabled = false;
};

using Frontend = quill::FrontendImpl<FrontendOptions>;
using Logger = quill::LoggerImpl<FrontendOptions>;

/***/
void quill_benchmark(std::vector<uint16_t> const& thread_count_array,
size_t num_iterations_per_thread, size_t messages_per_iteration)
{
/** - MAIN THREAD START - Logger setup if any **/

/** - Setup Quill **/
// main thread affinity
quill::detail::set_cpu_affinity(0);

quill::BackendOptions backend_options;
backend_options.cpu_affinity = 5;
backend_options.sleep_duration = std::chrono::nanoseconds{0};

// Start the logging backend thread and give it some tiem to init
quill::Backend::start(backend_options);

std::this_thread::sleep_for(std::chrono::milliseconds{100});

// wait for the backend thread to start
std::this_thread::sleep_for(std::chrono::seconds(1));

// Create a file sink to write to a file
std::shared_ptr<quill::Sink> file_sink = Frontend::create_or_get_sink<quill::IOUringFileSink>(
"quill_hot_path_rdtsc_clock.log",
[]()
{
quill::FileSinkConfig cfg;
cfg.set_open_mode('w');
return cfg;
}(),
quill::FileEventNotifier{});

Logger* logger = Frontend::create_or_get_logger(
"bench_logger", std::move(file_sink),
quill::PatternFormatterOptions{
"%(time) [%(thread_id)] %(short_source_location) %(log_level) %(message)", "%H:%M:%S.%Qns",
quill::Timezone::LocalTime, false});

/** LOGGING THREAD FUNCTIONS - on_start, on_exit, log_func must be implemented **/
/** those run on a several thread(s). It can be one or multiple threads based on THREAD_LIST_COUNT config */
auto on_start = []()
{
// on thread start
Frontend::preallocate();
};

auto on_exit = [logger]()
{
// on thread exit we block flush, so the next benchmark starts with the backend thread ready
// to process the messages
logger->flush_log();
};

// on main
auto log_func = [logger](uint64_t k, uint64_t i, double d)
{
// Main logging function
// This will get called MESSAGES_PER_ITERATION * ITERATIONS for each caller thread.
// MESSAGES_PER_ITERATION will get averaged to a single number

LOG_INFO(logger, "Logging iteration: {}, message: {}, double: {}", k, i, d);
};

/** ALWAYS REQUIRED **/
// Run the benchmark for n threads
for (auto thread_count : thread_count_array)
{
run_benchmark("Logger: Quill - Benchmark: Hot Path Latency / Nanoseconds", thread_count,
num_iterations_per_thread, messages_per_iteration, on_start, log_func, on_exit);
}
}

/***/
int main(int, char**) { quill_benchmark(THREAD_LIST_COUNT, ITERATIONS, MESSAGES_PER_ITERATION); }
5 changes: 0 additions & 5 deletions benchmarks/hot_path_latency/quill_hot_path_system_clock.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* Adding a benchmark for a another logger should be straight forward by duplicating and modifying
* this file.
*/

#include "hot_path_bench.h"

#include "quill/Backend.h"
Expand Down
Loading