为什么C++从stdin读取行比Python慢?分析与解决方案

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:引爆Stack Overflow的速度悖论



想象一下这个场景:您正在解决一道竞赛题或处理一个GB级别的日志文件。Python脚本在2秒内完成。您将其重写为C++,期望获得十倍的加速,但……却花了10秒!是不是很熟悉?正是这个问题在Stack Overflow上获得了数千次浏览,并引发了一场传奇性的讨论:“为什么从stdin读取行在C++中比Python慢得多?”。



看起来,“快速”的C++怎么会在读取行这样简单的操作上输给“缓慢”的Python呢?答案不在于语言本身的速度,而在于默认设置和iostream的架构。在本文中,我们将进行自己的调查,编写C++和Python的测试代码,测量时间,最重要的是,学习如何让C++的输入输出真正变得快速。



本文不是枯燥的理论,而是一本实用的指南,适用于所有编写C++并遇到输入输出性能问题的人。我们开始吧!



问题的本质:为什么iostream会“卡顿”?



当您编写std::cin >> data或std::getline(std::cin, line)时,C++默认会做一些很多人都没有意识到的事情。它将自身的输入输出流与C的标准流(stdin、stdout)同步。为什么?为了兼容性:您可以在同一个程序中同时使用printf和std::cout,而输出不会混乱。



这种同步代价高昂。每次调用cin或cout都会检查C流的状态,这将快速的缓冲操作变成了缓慢的同步操作。



而在Python中则完全不同。input()函数和sys.stdin.readline()使用自身经过良好优化的C级缓冲。没有多余的同步——只是从文件描述符中读取一块数据。这正是为什么从stdin读取在Python中可能比“开箱即用”的C++更快的原因。



实验:速度测试



让我们在实践中验证这一点。编写一个简单的程序,从stdin读取1,000,000行,并且不对它们做任何操作(仅读取)。首先使用“原样”的C++:



// slow_cin.cpp#include <iostream>#include <string>

int main() { std::ios::sync_with_stdio(false); // 暂时注释掉 std::cin.tie(nullptr); // 以及这个

std::string line; while (std::getline(std::cin, line)) { // 什么也不做 } return 0;}


然后是Python:



# fast_python.pyimport sys

def main(): for line in sys.stdin: pass

if __name__ == "__main__": main()


使用seq 1000000 | ./slow_cin和seq 1000000 | python3 fast_python.py运行测试。结果将令人震惊:未经优化的C++可能比Python慢5-10倍!但不要急于放弃C++——这个问题可以通过两行代码解决。



解决方案:两行魔法代码



加速C++输入输出所需要做的全部事情,就是禁用同步和流绑定。在main()的开头添加:



std::ios::sync_with_stdio(false);std::cin.tie(nullptr);


第一行禁用了iostream和stdio之间的同步。第二行解除了cin与cout的绑定(默认情况下,每次输入操作前cout都会刷新缓冲区,这也会减慢速度)。之后,您的C++代码将开始与Python并驾齐驱,并且通常更快。



验证结果



现在,在我们的测试中取消这些行的注释,然后再次运行。您会看到C++的性能飙升。在某些基准测试中,差异可达10倍。

博客

书籍推荐