Rether / docs
systems/io/'\n' vs std::endl
cheatsheet

'\n' vs std::endl

updated 2026.04.22created 2025.08.15~5 min

A Codeforces problem. A friend's tip: swap std::endl for '\n'. Output got faster. The reason wasn't obvious — so here's the breakdown.

The core difference

It comes down to one thing: std::endl flushes the buffer. '\n' does not.

Under the hood, std::endl is doing two things in one call:

// cpp
cout << '\n';
cout.flush();  // <-- this is the expensive part

Flushing forces the OS to drain the output buffer immediately via a write() syscall — a round-trip from user space to kernel space. Do that once and it costs almost nothing. Do it inside a loop that runs a million times, and it starts to matter.

But that raises an obvious question: does it actually matter in practice? Time to measure.

Benchmark — v1

// cpp
#include <bits/stdc++.h>
#include <chrono>
using namespace std;

void nothing(const int i) { cout << i; }
void newline(const int i) { cout << i << '\n'; }
void endline(const int i) { cout << i << endl; }

auto time(void (*out)(int), const int iterations) {
    const auto start{chrono::steady_clock::now()};
    for (int i = 0; i < iterations; ++i) out(i);
    const auto end{chrono::steady_clock::now()};
    return chrono::duration<double>{end - start};
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    constexpr int iterations = 1000;
    cerr << "None:    " << time(nothing, iterations).count() << "s\n";
    cerr << "Newline: " << time(newline, iterations).count() << "s\n";
    cerr << "Endline: " << time(endline, iterations).count() << "s\n";
}

Results

Iterationsnothing'\n'endl
1M0.195s0.832s0.813s
10M1.584s7.745s7.865s
100M16.471s74.049s74.369s

Two things jump out. First, '\n' is ~4.5× slower than nothing — that's expected, writing an extra character has a cost. But second, '\n' and endl are almost identical. If endl flushes and '\n' doesn't, why is there no gap?

The answer is that the test is measuring the wrong thing. The benchmark runs against a terminal, and terminals have their own opinion about flushing.

How output buffering actually works

The C++ standard library doesn't write to the OS on every character — it accumulates data in an internal buffer first. When and how it flushes that buffer depends on the mode:

Block buffering kicks in when output goes to a file or pipe. Characters pile up in a ~4–8 KB buffer. The OS only sees a write() when the buffer fills, or when an explicit flush is requested. One syscall per buffer-full — very efficient.

// code
[char][char][char]...[char]  →  buffer full  →  write()  →  OS

Line buffering kicks in when output goes to an interactive terminal. The buffer flushes automatically on every '\n'. This keeps interactive output responsive — ls and echo need to appear line by line, not in chunks.

// code
[char][char]\n  →  write()  →  terminal
[char][char]\n  →  write()  →  terminal

This is why the v1 results looked strange. Running against a terminal, '\n' was already triggering a flush on its own — making it behaviorally identical to endl. To see the real difference, the output needs to go somewhere that uses block buffering. Redirecting to a file does exactly that.

Benchmark — v2 (block buffered)

Rerunning with output redirected to a file (./bench > /dev/null):

Iterations Line buffered (terminal) Block buffered (file)
nothing '\n' endl nothing '\n' endl
1M 0.195s 0.832s 0.813s 0.106s 0.114s 0.542s
10M 1.584s 7.745s 7.865s 0.796s 1.158s 5.530s
100M 16.471s 74.049s 74.369s 7.478s 11.417s 54.439s

The block-buffered column is where the difference shows up.

Reading the numbers

Two independent effects are visible in the data.

Effect 1 — The flush penalty ('\n' vs endl, block buffered)

In block-buffered mode, endl is consistently ~4.8× slower than '\n':

// code
1M:    0.114s  vs  0.542s  →  4.8×
10M:   1.158s  vs  5.530s  →  4.8×
100M:  11.42s  vs  54.44s  →  4.8×

Every endl forces a syscall. Every '\n' lets the buffer absorb the character and batch the write. At 100M iterations, that difference compounds to over 43 wasted seconds — time spent bouncing between user space and kernel space for no reason.

Effect 2 — The line-buffer penalty (nothing vs '\n', terminal)

In line-buffered mode, '\n' is already ~4.5× slower than nothing:

// code
1M:    0.195s  vs  0.832s  →  4.3×
10M:   1.584s  vs  7.745s  →  4.9×
100M:  16.47s  vs  74.05s  →  4.5×

Because the terminal flushes on every newline, '\n' in line-buffered mode behaves almost identically to endl. The gap between them is just ~2% — the overhead of the extra flush() call on top of the flush '\n' already triggers.

This explains why Codeforces judges produce a clear difference while a local terminal test does not: online judges redirect output to a file, putting the stream in block-buffered mode where the flush penalty is fully exposed.

When std::endl is actually the right tool

Flushing is not always wrong. There are situations where it is exactly the right call:

  • Crash-adjacent logging — flush before a risky operation to ensure the message survives if the program aborts.
  • Interactive promptscout << "Enter name: " << endl must appear before the program blocks on cin.
  • Pipe consumers — if a downstream process is waiting for data, an unflushed buffer means it never arrives until the buffer fills naturally.

In all other cases — especially tight output loops — prefer '\n' and call cout.flush() explicitly at the one place it is actually needed.

'\n' moves the cursor. std::endl moves the cursor and pays a syscall tax — on a judge, in a file, in a pipe, that tax always compounds.

So what did a write call that allow to change line ????

Keyboard

search this vaultK / /
step the sidebarjk
open selected
switch browse mode123
switch vaultgv
toggle themet
close / clearesc
this help?
esc to close