Question
Why C++ stdin Line Reading Can Be Slower Than Python: cin, sync_with_stdio, and fgets
Question
I compared reading lines from standard input in Python and C++, and I was surprised that my C++ program was much slower than the Python version. I am not sure whether my C++ code is inefficient, whether I am benchmarking incorrectly, or whether I am misunderstanding how C++ input works.
Here is the C++ program:
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
int main() {
string input_line;
long line_count = 0;
time_t start = time(NULL);
int sec;
int lps;
while (cin) {
getline(cin, input_line);
if (!cin.eof()) {
line_count++;
}
}
sec = static_cast<int>(time(NULL) - start);
cerr << "Read " << line_count << " lines in " << sec << " seconds.";
if (sec > 0) {
lps = line_count / sec;
cerr << " LPS: " << lps << endl;
} else {
cerr << endl;
}
return 0;
}
Compiled with:
g++ -O3 -o readline_test_cpp foo.cpp
Here is the Python version:
#!/usr/bin/env python
import time
import sys
count = 0
start_time = time.time()
for line in sys.stdin:
count += 1
delta_sec = int(time.time() - start_time)
if delta_sec >= 0:
lines_per_sec = int(round(count / delta_sec)) if delta_sec else count
print("Read {0} lines in {1} seconds. LPS: {2}".format(
count, delta_sec, lines_per_sec
))
Example results:
$ cat test_lines | ./readline_test_cpp
Read 5570000 lines in 9 seconds. LPS: 618889
$ cat test_lines | ./readline_test.py
Read 5570000 lines in 1 seconds. LPS: 5570000
Why is the C++ version so much slower than Python when reading lines from stdin? Is there something wrong with using std::getline(cin, input_line) this way?
Short Answer
By the end of this page, you will understand why C++ stream input can appear much slower than Python for large stdin workloads, what std::ios::sync_with_stdio(false) changes, when std::cin.tie(nullptr) helps, and when lower-level functions like fgets may be faster. You will also learn how to write a more accurate and more idiomatic C++ line-reading loop.
Concept
In this question, the real concept is input performance in C++, especially the difference between:
- C++ iostreams like
std::cinandstd::getline - C stdio functions like
fgets - Python's buffered input behavior
A beginner often assumes that C++ must automatically be faster than Python because C++ is compiled. In practice, performance depends heavily on the library and API you use, not just the language.
Why default std::cin can be slow
By default, C++ iostreams are usually synchronized with C stdio. This means C++ streams cooperate with functions like printf, scanf, and fgets so they can be safely mixed. That compatibility has a cost.
This line disables that extra synchronization:
std::ios::sync_with_stdio(false);
When you do that, std::cin often becomes dramatically faster.
Why Python can look faster
Python's sys.stdin iteration is implemented in highly optimized C code under the hood, with efficient buffering. So even though Python is an interpreted language, its input layer may outperform C++ iostreams.
Mental Model
Think of input like receiving boxes at a warehouse.
- Default
std::cinis like checking each box through an extra security desk because it must stay compatible with another shipping system. sync_with_stdio(false)removes that extra coordination step.fgetsis like using a simpler, direct loading dock.- Python
sys.stdinmay look fast because the workers behind the scenes already batch the boxes efficiently.
So the surprising result is not really “Python is faster than C++.” It is more like “one input path has more overhead than another.”
Syntax and Examples
Fast C++ stream setup
If you want to use std::cin efficiently, do this near the start of main():
#include <iostream>
#include <string>
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string line;
long long count = 0;
while (std::getline(std::cin, line)) {
count++;
}
std::cout << count << '\n';
}
What these lines do
std::ios::sync_with_stdio(false);
- Disables synchronization between C++ streams and C stdio.
- Usually gives a large speed boost.
- After this, avoid mixing
cin/coutwithscanf/printfunless you really know what you are doing.
Step by Step Execution
Consider this improved example:
#include <iostream>
#include <string>
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string line;
int count = 0;
while (std::getline(std::cin, line)) {
++count;
}
std::cout << count << '\n';
}
Suppose the input is:
apple
banana
carrot
Step-by-step
sync_with_stdio(false)disables the extra synchronization overhead.cin.tie(nullptr)stopscoutfrom being flushed before each read.linestarts as an empty string.countstarts at0.- First
getlinereadsappleinto .
Real World Use Cases
Fast input matters whenever your program reads a lot of text.
Common examples
- Log analysis tools
- Read millions of server log lines from
stdinor a file.
- Read millions of server log lines from
- Competitive programming
- Large input size can cause time limit errors if I/O is slow.
- Command-line filters
- Programs that process piped input, such as transforming CSV or JSON lines.
- Data ingestion scripts
- Import rows from exports, reports, or message streams.
- Monitoring pipelines
- Consume line-based events from another process.
Example scenario
A C++ tool that counts failed login attempts in logs:
#include <iostream>
#include <string>
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string line;
long long failed = 0;
while (std::getline(std::cin, line)) {
(line.() != std::string::npos) {
++failed;
}
}
std::cout << failed << ;
}
Real Codebase Usage
In real projects, developers usually combine fast input with clear loop patterns and simple validation.
Common patterns
1. Idiomatic read loop
std::string line;
while (std::getline(std::cin, line)) {
// process line
}
This is the standard pattern for line-based reading.
2. Fast I/O setup at program start
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
This is common in tools that do heavy input parsing.
3. Guard clause for empty lines
if (line.empty()) {
continue;
}
Useful when blank lines should be ignored.
4. Validation before processing
if (line.size() < 3) {
continue;
}
Avoids errors when input may be malformed.
5. Early return on setup failure
If reading from files instead of stdin, developers check input immediately:
Common Mistakes
1. Using while (cin) instead of checking the read directly
Broken style:
while (std::cin) {
std::getline(std::cin, line);
count++;
}
Problem:
- The loop condition checks the stream before reading.
- The final failed read can make your logic awkward or wrong.
Better:
while (std::getline(std::cin, line)) {
count++;
}
2. Forgetting to disable sync when benchmarking iostreams
std::string line;
while (std::getline(std::cin, line)) {
// slow for huge input compared with unsynced iostreams
}
If performance matters, add:
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
3. Mixing C and C++ I/O after disabling sync
Potentially confusing:
std::ios::sync_with_stdio();
std::cout << ;
();
Comparisons
| Approach | Ease of use | Typical speed | Best for | Notes |
|---|---|---|---|---|
std::getline(std::cin, line) with default settings | Easy | Often slower | Small programs, simple scripts | Stream sync adds overhead |
std::getline(std::cin, line) with sync_with_stdio(false) | Easy | Fast | Most C++ line-processing tasks | Good balance of speed and safety |
fgets | Medium | Very fast | Performance-sensitive C-style input | Must manage buffer size manually |
Python for line in sys.stdin | Very easy |
Cheat Sheet
Fast line reading in C++
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::string line;
while (std::getline(std::cin, line)) {
// use line
}
Why default cin can be slow
- C++ streams are synchronized with C stdio by default.
- This adds overhead.
- Disabling sync often gives a large speedup.
Best loop pattern
Good:
while (std::getline(std::cin, line)) {
++count;
}
Avoid:
while (std::cin) {
std::getline(std::cin, line);
}
cin.tie(nullptr)
- Stops automatic flushing of
coutbefore input. - Useful in input-heavy programs.
fgets
char buffer[];
((buffer, (buffer), stdin)) {
}
FAQ
Why is std::cin slower than Python in this benchmark?
Because default C++ iostreams often include synchronization overhead with C stdio, while Python's stdin iteration uses efficient buffered code underneath.
Does std::ios::sync_with_stdio(false) always make C++ input faster?
Usually yes for heavy input workloads, but you should measure. It removes compatibility overhead and often gives a large improvement.
Should I always call std::cin.tie(nullptr) too?
It is commonly used with fast input. It helps when your program does lots of reading and does not rely on automatic flushing before input.
Is fgets faster than std::getline?
It can be, especially in some benchmarks. But it is lower-level and requires manual buffer handling.
Is the original while (cin) loop correct?
It is not the best style. The idiomatic and safer form is while (std::getline(std::cin, line)).
Can I mix printf and cout after calling sync_with_stdio(false)?
You should avoid that unless you understand buffering behavior well, because output order can become surprising.
Does this mean Python is better for I/O than C++?
Mini Project
Description
Build a command-line program that reads log lines from standard input and counts how many lines match a keyword. This demonstrates fast line-based input in C++ and shows a realistic use case for std::getline, sync_with_stdio(false), and simple text processing.
Goal
Create a C++ program that reads from stdin, searches each line for a target word, and prints the total number of matching lines.
Requirements
- Read all input from standard input line by line.
- Disable iostream synchronization for faster input.
- Count only lines that contain a given keyword.
- Print the final count as a single number.
- Use
std::getlineandstd::string.
Keep learning
Related questions
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Casts Explained: C-Style Cast vs static_cast vs dynamic_cast
Learn the difference between C-style casts, static_cast, and dynamic_cast in C++ with clear examples, safety rules, and real usage tips.
C++ Lambda Expressions Explained: What They Are and When to Use Them
Learn what C++ lambda expressions are, why they exist, when to use them, and how they simplify callbacks, algorithms, and local logic.