【问题标题】:How can this line-by-line file reading be much slower in c++ than in Python?在 c++ 中,这种逐行文件读取如何比在 Python 中慢得多?
【发布时间】:2018-06-16 18:18:54
【问题描述】:

我有一个 2GB 的文件。平均一行有 15 个字符(最多 50 个)。使用时:

#include <iostream>
#include <fstream>
#include <string>

void main()
{
    std::ifstream input("myfile.txt");
    std::string line;
    while (std::getline(input, line))
    {
    }
    return;
}

大约需要 320 秒,而这段 Python 代码:

with open('myfile.txt', mode='r') as f:
    for l in f:
        semicolonpresent = ';' in l        # do anything here, not important

不到一分钟。

我使用的 C++ 版本有什么问题?

注意:我已经尝试了很多次,每次都在重新启动之后,或者在之前的多次运行之后(所以它可能在 I/O 缓存中),但我总是得到这样的数量级。

注意 2:我在 Windows 7/64 上编译了 C++ 代码,使用:

call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86
cl main.cpp

【问题讨论】:

  • 你编译时是否开启了优化?
  • @Galik 我用编译行编辑了问题
  • cl main.cpp 缺少编译器可以使用的大多数花哨的参数。您最好使用默认的 VS 控制台应用程序模板创建程序。并且在构建时不要忘记选择Release配置。
  • @VTT nobody really uses iostreams to read files in C++ 然后我误解了stackoverflow.com/questions/7868936/read-file-line-by-line/…。你将如何在 C++ 中逐行读取一个大文件?
  • 我的意思是没有人真正在现实生活中使用它们,并不是说它们根本不能用。例如,请参阅What serious alternatives exist for the IOStream library。 C++ 中缺乏适当的 io 例程是一个严重的语言缺陷,迫使开发人员寻找替代方案。不幸的是,标准委员会忙于添加诸如表情符号语法或黎曼 zeta 函数之类的东西。我会使用自制的 mmap 或异步读取包装器。

标签: python c++ file fstream


【解决方案1】:

在我的测试中,C++ 并不比Python 慢。在一个大的二进制文件中,它们大致相同。

我稍微修改了您的代码以提供我认为应该是准确的时间。

Python 代码

import sys
import time 

start = time.time()
sum = 0
with open(sys.argv[1], mode='r') as f:
    for l in f:
        sum = sum + 1
end = time.time()

print "(", sum, ")", (end - start), "secs"

C++ 代码

    auto start = std::chrono::steady_clock::now();

    std::ifstream ifs(argv[1]);
    std::string line;

    unsigned sum = 0;
    while(std::getline(ifs, line))
        ++sum;

    auto end = std::chrono::steady_clock::now();

    auto time = double(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count());

    OUT("( " << sum << " ) " << (time / 1000.0) << " secs");

处理7.1 GiB 二进制视频文件给出:

Python: ( 32547618 ) 62.9722070694 secs
C++   : ( 32547618 ) 63.368 secs

C++ 代码是在 GCC v7.2 上使用以下标志编译的:

g++ -std=c++17 -O3 ...

相比之下,68.541 secs 中运行的未优化代码

【讨论】:

  • 感谢您的回答。您能否添加有关如何编译的详细信息(编译参数、优化等),因为我注意到它改变了很多时间。
  • @Basj 当然我添加了优化级别。在我的测试中,它们没有太大区别。似乎文件越大,不同方法之间的差异就越小。我希望这是因为大部分时间都花在等待磁盘旋转上 :)
【解决方案2】:

void main 不是有效的 C++。 main() 中的 return 也不是空的。此外,启用优化后,您的 while 循环将被优化掉。话虽如此...

如果您启用优化,您应该会注意到速度有所提高。但是,C++ 中的流很慢。这对 C++ 世界来说不是新闻。那么为什么 Python 快得多呢?两者并不是真正等价的。 Python 是用 C 语言编写的,并在内部委托给 C 库函数。此外,Python 可能会提前分配缓冲区并一次性复制缓冲区。如果你想在 C++ 中做类似的事情:

std::ifstream ifs(filename);

auto end = ifs.tellg();
ifs.seekg(0, std::ios::beg);
auto beg = ifs.tellg();

std::string buffer;
buffer.reserve(end - beg);
std::copy(std::istreambuf_iterator<char>(ifs),
          std::istreambuf_iterator<char>(),
          buffer.begin());

【讨论】:

  • 如何使用cl helloworld1.cpp开启优化?
  • 如果没有足够的备用 ram 来分配 2 GB 固态块,执行 buffer.reserve(end - beg); 可能不会很好。
  • @Basj cl optimization flags。一个基本的应该是cl /O2 helloworld1.cpp,尽管默认情况下我很确定它已经使用了/Ot,它使用了一组速度优化。
  • 在我的测试中,流并不慢,它们的执行与底层的OS 调用(Linux 系统)基本相同。
  • @Galik 在我的测试中,流并不慢,它们的执行与底层操作系统调用(Linux 系统)基本相同 获得更快的磁盘和具有更多内存带宽的系统。如果您正在运行商用硬件,磁盘 IO 寻道时间可能很短,并且内存带宽可能有限。这可以隐藏流处理的开销。大多数计算机购买者不会忽略 CPU 以及系统 RAM 的数量和速度。即使对于看驱动器的买家来说,也很少有人看磁盘控制器。流处理可能完全保留在 CPU 和板载缓存中,将其屏蔽。
【解决方案3】:

(部分)答案:

正如cmets很多人提到的,cloptimization帮助了:

cl /Ox helloworld1.cpp

将运行时间除以 6!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 2019-04-16
    • 2021-02-03
    • 2012-03-11
    • 1970-01-01
    相关资源
    最近更新 更多