# What Is the Performance Impact of spdlog? Benchmarks and Architecture Explained

> Discover spdlog performance: up to 14M messages/sec synchronous null mode and lock-free async queues eliminate thread contention and minimize C++ overhead.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: performance
- Published: 2026-07-27

---

**spdlog adds minimal overhead to C++ applications, achieving up to 14 million messages per second in synchronous null mode and eliminating thread contention via lock-free asynchronous queues.**

The `gabime/spdlog` repository provides a high-performance logging library designed for speed-critical applications. Understanding the performance impact of spdlog helps developers choose between synchronous and asynchronous modes based on their latency and throughput requirements. The library achieves microsecond-level latencies by minimizing formatting costs and using lock-free data structures for inter-thread communication.

## Architecture Overview: How spdlog Minimizes Overhead

spdlog reduces logging overhead through three core architectural patterns that separate formatting from I/O and minimize lock contention.

### Synchronous Logging

In synchronous mode, each log call writes directly to the sink after minimal formatting. The calling thread blocks until the message is written, making I/O latency the dominant cost. Single-threaded synchronous loggers achieve approximately **5.78 million messages per second** with file sinks, while the `null_sink` (formatting only) reaches **14.13 million messages per second**, proving that formatting overhead is negligible compared to disk writes.

### Asynchronous Logging with Lock-Free Queues

Asynchronous loggers use a **lock-free multiple-producer-multiple-consumer queue** (`mpmc_blocking_queue`) to buffer messages. A background thread pool drains the queue and performs I/O operations. According to the source code in [`include/spdlog/details/thread_pool.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/thread_pool.h), this design decouples application threads from I/O latency, reducing the cost of a log call to roughly the time required for formatting and pushing a struct onto the queue.

### Overflow Policies for Latency Control

When the async queue fills, spdlog offers three overflow policies configured via `async_overflow_policy`:
- **block**: Waits until space is available, ensuring no message loss but potentially increasing latency
- **overrun_oldest**: Discards the oldest message to make room, prioritizing current throughput  
- **discard_new**: Drops new messages when the queue is full

## Benchmark Results: Throughput by Mode

The benchmark suite in [`bench/bench.cpp`](https://github.com/gabime/spdlog/blob/main/bench/bench.cpp) demonstrates spdlog's performance characteristics on Ubuntu 64-bit with an i7-4770 processor.

**Single-threaded synchronous mode:**
- `basic_st`: 5.78 million messages/second
- `rotating_st`: 5.48 million messages/second
- `daily_st`: 5.06 million messages/second
- `null_st`: 14.13 million messages/second (formatting only)

**Multi-threaded synchronous mode (10 threads):**
- `basic_mt`: 1.66 million messages/second
- `rotating_mt`: 1.61 million messages/second
- `daily_mt`: 1.64 million messages/second
- `null_mt`: 6.27 million messages/second

**Asynchronous mode (10 threads, 8192 queue):**
- Block policy: 0.59 million messages/second
- Overrun policy: 2.68 million messages/second

These results show that while multi-threaded synchronous logging suffers from mutex contention (dropping to ~1.6M msgs/s), asynchronous logging with the overrun policy achieves **2.68 million messages per second** by eliminating producer blocking.

## Code Examples: Measuring Impact in Practice

### Synchronous Logger Baseline

The `basic_logger_mt` factory function creates a thread-safe synchronous logger that writes directly to file. As implemented in [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h), this mode provides the lowest latency for single-threaded applications but encounters lock contention under high concurrency.

```cpp
#include "spdlog/spdlog.h"

int main() {
    auto logger = spdlog::basic_logger_mt("sync", "logs/sync.log");
    for (int i = 0; i < 1'000'000; ++i) {
        logger->info("Message {}", i);   // ~5M msgs/s single-threaded
    }
}

```

### Asynchronous Logger with Thread Pool

To eliminate I/O blocking, use `init_thread_pool` and `create_async` as defined in [`include/spdlog/async_logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/async_logger.h). This configuration moves file writes to a background worker thread.

```cpp
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"

int main() {
    spdlog::init_thread_pool(8192, 1);
    auto async_logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>(
        "async", "logs/async.log");

    for (int i = 0; i < 1'000'000; ++i) {
        async_logger->info("Async message {}", i);   // ~2.5M msgs/s multi-threaded
    }
    spdlog::shutdown();
}

```

### Configuring Overflow Policies

Control memory usage and backpressure by specifying the overflow policy during logger creation. The `overrun_oldest` policy prevents blocking the producer thread when the queue saturates.

```cpp
spdlog::init_thread_pool(8192, 1);
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>(
    "async_overrun",
    "logs/overrun.log",
    spdlog::async_overflow_policy::overrun_oldest);

```

## Key Source Files Affecting Performance

Understanding these implementation files clarifies how spdlog maintains high throughput:

- **[`bench/bench.cpp`](https://github.com/gabime/spdlog/blob/main/bench/bench.cpp)**: Contains the benchmark harness generating the throughput metrics cited above
- **[`include/spdlog/details/thread_pool.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/thread_pool.h)**: Implements the lock-free `mpmc_blocking_queue` and worker threads that enable asynchronous performance
- **[`include/spdlog/async_logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/async_logger.h)**: Defines the `async_logger` class and `async_overflow_policy` enum affecting latency characteristics
- **[`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h)**: Provides factory functions (`basic_logger_mt`, `rotating_logger_mt`) used in synchronous high-performance paths

## Summary

- **Synchronous mode** achieves **5-14 million messages/second** depending on I/O requirements, with single-threaded performance exceeding multi-threaded due to lock contention
- **Asynchronous mode** eliminates producer blocking via lock-free queues in [`thread_pool.h`](https://github.com/gabime/spdlog/blob/main/thread_pool.h), yielding **2.7 million messages/second** with the overrun policy
- **Overflow policies** allow explicit trade-offs between message durability (block) and throughput (overrun_oldest)
- **Memory overhead** is limited to the fixed-size async queue (configurable, typically 8192 entries), making the performance impact predictable and bounded

## Frequently Asked Questions

### Does spdlog slow down my application?

No, spdlog adds negligible overhead when configured appropriately. In asynchronous mode, the library performs only lock-free queue operations on the calling thread, costing microseconds per call. Even in synchronous mode, throughput exceeds 5 million messages per second on modern hardware, making the impact imperceptible for typical logging volumes.

### How fast is spdlog compared to other C++ loggers?

According to the benchmarks in [`bench/bench.cpp`](https://github.com/gabime/spdlog/blob/main/bench/bench.cpp), spdlog ranks among the fastest C++ logging libraries, achieving **14 million messages per second** in null-sink mode (formatting only) and **2.7 million messages per second** in multi-threaded asynchronous mode. This performance stems from its header-only design, compile-time format string checks, and lock-free queue implementation.

### Should I use synchronous or asynchronous logging?

Use **synchronous logging** for simple single-threaded applications or when immediate disk persistence is required for every message. Use **asynchronous logging** when logging from multiple threads or when minimizing application latency is critical, as the lock-free queue in [`include/spdlog/details/thread_pool.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/thread_pool.h) decouples your code from I/O operations.

### What is the memory overhead of spdlog's async queue?

The memory overhead is fixed and configurable via `init_thread_pool(queue_size, thread_count)`. Each queue entry stores a formatted log message struct, typically consuming a few hundred bytes per slot. For the default 8192-entry queue used in benchmarks, this translates to approximately a few megabytes of RAM, regardless of message throughput.