# Understanding the Performance Impact of move_events() in tui-logger

> Explore the performance impact of move_events() in tui-logger. Learn how this O(N) function runs in a background thread to maintain zero blocking on your main logging path.

- Repository: [gin66/tui-logger](https://github.com/gin66/tui-logger)
- Tags: performance
- Published: 2026-03-02

---

**The `move_events()` function in tui-logger incurs a linear O(N) performance cost relative to the number of pending log events, but executes in a dedicated background thread every 10ms or when the hot buffer reaches 50% capacity, ensuring zero blocking of the main logging path.**

In the `gin66/tui-logger` crate, `move_events()` serves as the critical bridge between the high-performance hot buffer and the long-term main storage. This function determines how quickly log records transition from temporary memory to persistent circular buffers, directly impacting both latency and throughput in terminal user interface applications.

## What Does move_events() Do?

`move_events()` implements the **hot-buffer → main-buffer pipeline**, transferring pending log records from the temporary hot storage to the permanent circular buffer. According to the implementation in [`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs) (lines 128-140), the function performs several distinct operations with varying computational costs.

### Step-by-Step Execution Flow

The function executes the following sequence every time it runs:

1. **Early-exit check** – Verifies if `self.hot_log.lock().events` contains any pending records. This costs **O(1)** and returns immediately if empty.

2. **Buffer swap** – Replaces the hot circular buffer with a fresh instance via `CircularBuffer::new(hot_depth)`. This **O(1)** swap operation is memory-efficient and atomic.

3. **Consume and reverse** – Drains all pending `ExtLogRecord` instances into a temporary `Vec`, reversing their order so older records appear first. This step costs **O(N)** where *N* equals the number of pending events, requiring allocation of a temporary vector of size *N*.

4. **Overrun detection** – If event production exceeded hot buffer capacity, generates a synthetic "events lost" record. Cost remains **O(1)**.

5. **Target-filter resolution** – For each record, performs target lookup in `tli.targets`, queries optional `env_filter`, and computes `fast_str_hash` to cache the resolved `LevelFilter` in `self.hot_select.lock().hashtable`. This costs **O(N × Cfilter)**, where *Cfilter* represents iterating over five `LevelFilter` variants when filters are present.

6. **Optional file dump** – Formats and writes records according to `TuiLoggerFile` configuration. This costs **O(N × Cwrite)** and depends entirely on I/O subsystem performance.

7. **Main buffer insertion** – Pushes processed records into the long-term circular buffer (`tli.events`) at **O(N)** cost.

## Performance Characteristics

Understanding the computational complexity helps predict behavior under load.

### Time Complexity and Big-O Analysis

`move_events()` exhibits **linear time complexity O(N)** relative to the number of pending events. In steady-state operation where the hot buffer contains only a few dozen entries, execution completes within microseconds. The dominant costs emerge during the consume-reverse phase and target-filter resolution, both scaling proportionally with event volume.

### Memory Allocation Patterns

The function allocates a temporary `Vec` sized to the exact number of pending events during the consume phase. While this prevents unnecessary memory bloat, burst logging patterns trigger corresponding allocation spikes. The buffer swap mechanism (`CircularBuffer::new`) pre-allocates fresh storage, ensuring subsequent log calls maintain O(1) insertion performance.

### Background Thread Isolation

As implemented in [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs) (lines 44-50), `move_events()` executes exclusively within the `"tui-logger::move_events"` background thread. This architectural choice isolates all O(N) processing costs from the application threads, preventing latency spikes in time-sensitive code paths.

## When Is move_events() Triggered?

Three distinct mechanisms invoke `move_events()`:

1. **Periodic background execution** – The dedicated thread sleeps 10ms between iterations, creating a default processing interval of approximately every 10 milliseconds.

2. **Hot-buffer back-pressure** – When the hot buffer reaches **50% capacity**, `raw_log()` (in [`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs) lines 98-103) sets a `need_signal` flag and unparks the background thread. This ensures prompt processing during log bursts without waiting for the next 10ms interval.

3. **Explicit user invocation** – The public wrapper function `tui_logger::move_events()` (defined in [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs) lines 32-35) allows synchronous execution. Test suites utilize this capability to enforce deterministic behavior during assertions.

## Code Example: Monitoring Performance

```rust
use tui_logger;

// Initialize the logger and spawn the background thread
tui_logger::init_logger(log::LevelFilter::Info).unwrap();

// These calls return immediately (O(1) hot buffer insertion)
log::info!("Application starting");
log::debug!("Configuration loaded");

// Background thread automatically processes these every ~10ms
// or sooner if buffer reaches 50% capacity

// Force synchronous processing for deterministic tests
tui_logger::move_events();

```

## Implementation Details

The performance characteristics derive from specific implementation choices across the codebase:

- **[`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs)** (lines 128-140): Contains the core `TuiLogger::move_events()` implementation with the hot buffer swap logic and event reversal algorithm.

- **[`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs)** (lines 44-50, 32-35): Defines the background thread spawn logic and the public API wrapper that exposes `move_events()` to external callers.

- **[`src/logger/fast_hash.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/fast_hash.rs)**: Implements `fast_str_hash`, the hashing algorithm used during target-filter resolution to minimize cache misses in the hot path.

- **[`src/logger/mod.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/mod.rs)**: Re-exports the public API surface used by consuming applications.

## Summary

- `move_events()` operates in **O(N)** time where *N* equals pending log events, with linear costs for reversal, filtering, and buffer insertion.
- The function runs in a **dedicated background thread** every 10ms by default, ensuring application threads never block on log processing.
- **Back-pressure handling** triggers immediate execution when the hot buffer reaches 50% capacity, preventing overflow during logging bursts.
- **Memory allocation** occurs proportionally to pending event counts during the temporary vector creation phase.
- **File I/O** costs only apply when `TuiLoggerFile` dumping is enabled, and remain isolated to the background thread.

## Frequently Asked Questions

### Does move_events() block the main application thread?

No. The function executes exclusively within the `"tui-logger::move_events"` background thread spawned by `init_logger()`. Application threads calling `log::info!()` or similar macros perform only O(1) hot buffer insertions and return immediately, while the background thread handles all O(N) processing asynchronously.

### How often does move_events() run in production?

By default, the background thread invokes `move_events()` approximately every **10 milliseconds** during normal operation. However, if the hot buffer fills to **50% capacity**, the system triggers immediate execution through thread unparking (see `raw_log()` in [`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs) lines 98-103), ensuring rapid response to logging bursts.

### What happens if the hot buffer overflows?

When event production exceeds hot buffer capacity before `move_events()` processes them, the system generates a **synthetic "events lost" record** during the next execution cycle. This mechanism alerts developers to buffer exhaustion while maintaining system stability, though it indicates the 10ms polling interval proved insufficient for the current log volume.

### Can I manually trigger event processing?

Yes. The public API exposes `tui_logger::move_events()` (defined in [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs) lines 32-35), which simply forwards to `TUI_LOGGER.move_events()`. This is particularly useful in test suites requiring deterministic log ordering or when applications need to ensure all pending events are visible in the TUI before rendering specific frames.