为什么在C+中从stdin读取行的速度比Python慢得多?

为什么在C+中从stdin读取行的速度比Python慢得多?

我想比较一下使用Python和C+从stdin中读取字符串输入的行,并惊讶地看到我的C+代码运行的速度比等效的Python代码慢一个数量级。由于我的C+生疏了,而且我还不是Pythonsta专家,请告诉我是否做错了什么,或者我是否误解了什么。


(TLDR答复:包括说明:cin.sync_with_stdio(false)或者直接用fgets相反。


TLDR结果:一直滚动到问题的底部,看看表格。)


C+代码:


#include <iostream>

#include <time.h>


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 = (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

Python等效:


#!/usr/bin/env python

import time

import sys


count = 0

start = 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))

    print("Read {0} lines in {1} seconds. LPS: {2}".format(count, delta_sec,

       lines_per_sec))

以下是我的研究结果:


$ 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

我应该注意,我在MacOSXv10.6.8(雪豹)和Linux2.6.32(RedHatLinux6.2)下都尝试过这一点。前者是一个MacBookPro,后者是一个非常强大的服务器,这并不是说这太相关了。

德玛西亚99
浏览 785回答 3
3回答

小唯快跑啊

我落后了几年,但是:在原始文章的“编辑4/5/6”中,您使用的是结构:$&nbsp;/usr/bin/time&nbsp;cat&nbsp;big_file&nbsp;|&nbsp;program_to_benchmark这在几个不同的方面是错误的:你实际上是在计时“猫”的执行,而不是你的基准。“time”显示的‘user’和‘sys’CPU使用率是‘cat’,而不是您的基准程序。更糟糕的是,“真实”时间也不一定准确。根据‘cat’和本地操作系统中管道的实现,‘cat’可能会写入最后一个巨大缓冲区,并在读者进程完成工作之前就退出。使用‘cat’是不必要的,实际上适得其反;你在添加移动部件。如果您使用的是一个足够老的系统(即使用单个CPU,并且-在某些代计算机中-I/O比CPU快)-仅仅是`cat‘正在运行这一事实就会严重影响结果。您还受输入和输出缓冲以及其他“cat”处理可能做的任何操作的限制。(这可能会给你带来一个“猫的无用途”如果我是兰德尔·施瓦茨。一个更好的结构是:$&nbsp;/usr/bin/time&nbsp;program_to_benchmark&nbsp;<&nbsp;big_file在这个声明中,它是壳它打开bigfile,将其作为已经打开的文件描述符传递给您的程序(实际上是“time”,然后作为子进程执行您的程序)。100%的文件读取严格来说是你想要测试的程序的责任。这可以让您真正地阅读它的性能,而不会出现虚假的复杂情况。我将提到两个可能但实际上是错误的,也可以考虑的“修正”(但我对它们的“编号”不同,因为在最初的文章中,这些都不是错误的):答:你只需计时你的程序就可以“修复”这个问题:$&nbsp;cat&nbsp;big_file&nbsp;|&nbsp;/usr/bin/time&nbsp;program_to_benchmarkB.或根据整个管道的时间安排:$&nbsp;/usr/bin/time&nbsp;sh&nbsp;-c&nbsp;'cat&nbsp;big_file&nbsp;|&nbsp;program_to_benchmark'这些错误的原因与#2相同:它们仍然不必要地使用“cat”。我提到它们有几个原因:对于那些对POSIX外壳的I/O重定向设施不太满意的人来说,它们更“自然”。在某些情况下,“猫”是需要(例如:要读取的文件需要某种访问权限,并且您不希望将该权限授予要对其进行基准测试的程序:‘sudo cat/dev/sda区/usr/bin/time my_press_test-no-output’)在实践中,在现代机器上,在管道中添加的‘cat’可能没有什么实际意义。但我说的最后一件事有点犹豫。如果我们检查“编辑5”的最后结果-$&nbsp;/usr/bin/time&nbsp;cat&nbsp;temp_big_file&nbsp;|&nbsp;wc&nbsp;-l0.01user&nbsp;1.34system&nbsp;0:01.83elapsed&nbsp;74%CPU&nbsp;...-这声称“cat”在测试期间消耗了74%的CPU;实际上1.34/1.83约占74%。也许是一连串的:$&nbsp;/usr/bin/time&nbsp;wc&nbsp;-l&nbsp;<&nbsp;temp_big_file剩下的0.49秒就好了!可能不会:这里的`cat‘必须支付从’disk‘(实际上是缓冲区缓存)传输文件的read()系统调用(或等效的),以及管道写入以将它们传递给’wc‘。正确的测试仍然需要执行这些read()调用;只有写到管道和从管道读取的调用才会被保存,而且这些调用应该相当便宜。不过,我预计您将能够测量“Cat file x WC-l”和“wc-l<file”之间的差异,并发现明显的(2位数的百分比)差异。每一次较慢的测试都将在绝对时间内支付类似的惩罚;然而,这将相当于其较长时间的一小部分。实际上,我在Linux 3.13(Ubuntu14.04)系统上对1.5G垃圾文件做了一些快速测试,获得了以下结果(这些结果实际上是“3中最好的”结果;当然,在启动缓存之后):$&nbsp;time&nbsp;wc&nbsp;-l&nbsp;<&nbsp;/tmp/junk real&nbsp;0.280s&nbsp;user&nbsp;0.156s&nbsp;sys&nbsp;0.124s&nbsp;(total&nbsp;cpu&nbsp;0.280s)$&nbsp;time&nbsp;cat&nbsp;/tmp/junk&nbsp;|&nbsp;wc&nbsp;-l real&nbsp;0.407s&nbsp;user&nbsp;0.157s&nbsp;sys&nbsp;0.618s&nbsp;(total&nbsp;cpu&nbsp;0.775s)$&nbsp;time&nbsp;sh&nbsp;-c&nbsp;'cat&nbsp;/tmp/junk&nbsp;|&nbsp;wc&nbsp;-l'real&nbsp;0.411s&nbsp;user&nbsp;0.118s&nbsp;sys&nbsp;0.660s&nbsp;(total&nbsp;cpu&nbsp;0.778s)请注意,这两个管道结果声称占用的CPU时间(用户+sys)比实时时间要多。这是因为我使用的是shell(Bash)内置的‘time’命令,它知道管道;我在一台多核机器上,管道中的单独进程可以使用不同的内核,积累CPU时间比实时快。使用/usr/bin/time,我看到的CPU时间比实时的要小-表明它只能对命令行上传递给它的单个管道元素进行计时。另外,shell的输出给出毫秒,而/usr/bin/time只给出百分之一秒。因此,在“wc-l”的效率级别上,“cat”产生了巨大的差异:409/283=1.453或45.3%的实时,775/280=2.768,或高达177%的使用CPU!在我的随机测试箱里。我应该补充一点,在这些测试风格之间至少还有一个显著的区别,我不能说它是有利的还是错误的;你必须自己决定:当您运行`Cat bix_file\/usr/bin/time my_Programm‘时,您的程序正在接收来自管道的输入,其速度与“cat”所发送的速度正好相同,并且以不大于“cat”编写的块的方式进行。当您运行`/usr/bin/timemy_Program<Big_file‘时,您的程序将收到一个打开的文件描述符到实际文件。你的节目-或在许多情况下,编写它的语言的I/O库-当显示引用常规文件的文件描述符时,可能会采取不同的操作。它可以使用mmap(2)将输入文件映射到其地址空间,而不是使用显式读取(2)系统调用。这些差异对基准结果的影响可能比运行“cat”二进制文件的成本小得多。当然,如果相同的程序在两种情况下的执行情况明显不同,这将是一个有趣的基准结果。它表明,实际上,程序或其I/O库是做一些有趣的事情,比如使用mmap()。因此,在实践中,最好是以两种方式来运行基准;也许可以用一些小因素来“原谅”运行`cat‘本身的成本,从而将`cat’结果打折扣。
打开App,查看更多内容
随时随地看视频慕课网APP