有没有更快的方法使用命令行在C ++中加载文件?

时间:2014-05-30 17:23:32

标签: c++ file input command-line

我想使用命令行将.txt中的一百万个随机整数加载到一个向量中:

program.exe < million-integers.txt

我的代码可以使用,但需要几秒钟才能运行。我有什么办法可以让它更快吗?我在SO上找到了一些解决方案,但它们似乎都依赖于对文件路径进行硬编码。我希望能够通过命令行传递文件名。

vector<int> data;
int input;

while (cin >> input)
{
    data.push_back(input);
}
cout << "Data loaded." << endl;

(在Win 8.1上使用Visual Studio的C ++ noob)

编辑:在这种情况下,我知道可以进行一些改进,因为我有其他人的.exe可以在一秒钟内完成。

编辑:所有整数都在同一行。

5 个答案:

答案 0 :(得分:7)

运行时间:4.08秒。什么?那很慢!

为什么会这样?

我做了剖析。我正在使用一个非常不同的系统:OS X 10.8,与Clang,但我的程序慢,我怀疑它是出于同样的原因。以下是分析结果中的两行(格式化道歉):

Running Time    Self        Symbol Name
3389.0ms   79.3%    76.0             std::__1::num_get<char, std::__1::istreambuf_iterator<char, std::__1::char_traits<char> > >::do_get(std::__1::istreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::istreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::ios_base&, unsigned int&, long&) const
824.0ms   19.2% 8.0          std::__1::basic_istream<char, std::__1::char_traits<char> >::sentry::sentry(std::__1::basic_istream<char, std::__1::char_traits<char> >&, bool)

如您所见,这两个函数几乎占执行时间的98.5%。哇!当我向下钻取时,这些库函数调用需要花费很多时间吗?

  • flockfile()
  • funlockfile()
  • pthread_mutex_unlock()

因此,在我的系统上,std::cin的实现与C的<stdio.h>函数一起使用,因此它们都可以在同一个程序中使用,并且这些函数确保与其他线程同步。这是低效的。

  1. 没有使用<stdio.h>的代码,因此无需与其同步。

  2. 只有一个线程使用stdin,所以锁定过多,尤其是,如果每个字符读取一次锁定。这太过分了。锁和系统调用非常快......但是如果你做了1000万次锁定和系统调用?不再快。

  3. 注意:是的,我正在运行OS X,并且Windows上的实际功能会有所不同。而不是flockfile()pthread_mutex_unlock(),您将看到Windows版本的任何内容。

    解决方案#1

    停止使用重定向。如果您使用ifstream,则假定您将自己锁定。在我的系统上,这给出了0.42秒的运行时间 - 接近10倍。

    解决方案#2

    将所有内容读入字符串,然后解析字符串。这允许您继续使用重定向来读取文件。

    解决方案#3

    禁用std::cin上的锁定。对不起,我不知道怎么做。这可能是可能的。

    性能限制

    我怀疑ifstream版本远未达到计算机的性能限制。如果性能至关重要,我怀疑当程序仅受内存带宽限制时,可以使暖缓存运行时间接近2或3毫秒。

答案 1 :(得分:5)

你可能会尝试两件事。

  1. 使用std::vector<T>::reserve预先设置矢量大小
  2. 使用std::iostream::read读取4K(或更大)块中的数据并自行解析数据。
  3. iostream应该被缓冲,并且假设向量具有良好的调整大小策略,但有时这些东西不能像预期的那样工作。

答案 2 :(得分:1)

在你进一步投票之前......

我已经确认我的答案(我在this gist中编码:https://gist.github.com/spundun/c91c07d7b8233e675d2e)有效且运行速度提高了10倍。我正在使用带有闪存驱动器的MacBook Pro(2012年我认为)

cpp $ruby gen_data.rb > data.txt 
cpp $c++ -O3 test1.cpp 
cpp $time ./a.out < data.txt 
Data loaded.

real    0m3.048s
user    0m3.023s
sys 0m0.011s
cpp $c++ -O3 test2.cpp 
cpp $time ./a.out data.txt 
data.txtData loaded.

real    0m0.351s
user    0m0.334s
sys 0m0.010s

答案

使用常规ifstream代替cin。在命令行(vargs)上传递文件名并打开它,而不是使用cin

int main(int argc, char *argv[]){
  vector<int> data;
  int input;
  ifstream in;
  in.open(argv[1]);

  while (in >> input)
  {
        data.push_back(input);
  }
  cout << "Data loaded." << endl;
}

这会更快,因为......

@DietrichEpp更清楚地了解为什么我的解决方案速度提高了10倍。当你使用cin时,它会尝试线程安全并增加额外的同步开销,使其运行得慢得多。

答案 3 :(得分:1)

您可能希望查看的另一个选项是ios_base::sync_with_stdio,默认为true。此选项强制std::cin将每个块作为单独的块读取,以允许您将C操作与C ++交织在一起。这减慢了阅读速度。可以在this answer中找到更详细的分析。

答案 4 :(得分:0)

是,并且不是。是的,一旦操作系统提供给您,有一种更快的方法来检索数据。不,您的程序受操作系统的支配,操作系统会根据操作系统选择将数据从文件发送到您的程序(可能还有其他任务可能会中断您的程序连续接收数据)。

将大量内容读入内存
搜索内存总是比设备更快,例如拇指驱动器或硬盘驱动器。

对大量数据的一次读取请求通常比对少量数据的许多读取请求快得多。

结合这些事实表明你应该分配(保留)大量内存并读入内存。这可能需要重复,具体取决于您可以保留的内存量和文件大小。

示例:

  const unsigned int BUFFER_SIZE = 1024 * 1024;
  char buffer[BUFFER_SIZE];
  cin.read(buffer, BUFFER_SIZE);

使用std :: istringstream
您可以创建一个使用缓冲区的std :: istringstream。因此,您可以像在cin或文本文件中那样从文本缓冲区“读取”整数。

文件重定向的开销
当您将文件的输出重定向到程序的输入时,操作系统必须打开文件,从文件中读取一些数据,然后将其传递给您的程序。这种开销是程序的主要瓶颈。如果您的程序直接打开文件,您可以避免开销并使程序运行得更快。

在网上搜索“C ++ cin rdbuf setbuf”,了解有关重定向cin以使用您选择的缓冲区的信息。