【问题标题】:ofstream::operator<<(streambuf) is a slow way to copy a fileofstream::operator<<(streambuf) 是复制文件的慢速方法
【发布时间】:2011-08-30 22:56:14
【问题描述】:

我需要一种跨平台、无外部库的复制文件的方式。在我的第一遍中,我想出了(省略了错误处理):

char buffer[LEN];
ifstream src(srcFile, ios::in | ios::binary);
ofstream dest(destFile, ios::out | ios::binary);

while (!src.eof()) {
  src.read(buffer, LEN);
  dest.write(buffer, src.gcount());
}

这很好我知道它在做什么。

然后我在 stackoverflow 上找到了一个帖子(抱歉,现在找不到链接)说我可以将上面的所有代码替换为:

dest << src.rdbuf();

它既漂亮又紧凑,但隐藏了很多关于它在做什么。结果也很慢因为ofstream::operator

有没有办法让这个方法更快?我原来的方法有缺点吗?

更新:在 Windows 上使用 operator 执行得更好

此外,在上面的代码中更改缓冲区的大小不会影响对硬盘驱动器的读写大小。为此,您需要使用 stream.rdbuf()->pubsetbuf() 设置缓冲区。

【问题讨论】:

  • 你为什么不define每个平台的副本?还是使用提升?
  • dest.write(src.rdbuf(), size); 怎么样?
  • “我需要一个跨平台,没有外部库,方式”我不确定在什么情况下需要同时成为跨平台被禁止从使用库。尤其是因为库是跨平台开发最有效的工具。
  • 如果你使用普通的 c 调用,你会看到更好的性能。流在性能方面被削弱了。如果性能很重要,请不要使用它们
  • @Pavel:这些流旨在轻松处理大文件。我相信对于小文件,它们只比 C 稍慢。

标签: c++ fstream


【解决方案1】:

我想知道你的 fstream 是否默认没有缓冲。 GCC 4.5.2 默认使用内部缓冲区,但我不认为这是标准要求的。您是否尝试过使用 pubsetbuf(见下文)为您的输入/输出流设置缓冲区。

对我的系统进行快速测试,如果我将 LEN 设置为 0(因此没有缓冲),复制 1 MB 文件需要 10 秒。使用 4k 缓冲区,不到一秒就完成了。

#include <iostream>
#include <fstream>

int main() {
  using namespace std;
  const char* srcFile = "test.in";
  const char* destFile = "test.out";

  ifstream src;
  ofstream dest;

  const int LEN=8192;
  char buffer_out[LEN];
  char buffer_in[LEN];
  if (LEN) {
    src.rdbuf()->pubsetbuf(buffer_in, LEN );
    dest.rdbuf()->pubsetbuf(buffer_out, LEN);
  } else {
    src.rdbuf()->pubsetbuf(NULL, 0 );
    dest.rdbuf()->pubsetbuf(NULL, 0);
  }
  src.open(srcFile, ios::in | ios::binary);
  dest.open(destFile, ios::out | ios::binary);
  dest << src.rdbuf();

}

【讨论】:

  • 好收获。在 windows (vs2005) 上 ofstream::operator
  • 我得到了一些有趣的结果,我无法在此处的评论中完全解释。但基础:operator
  • @Dave S wow - 非常有帮助的答案。我只是花了几个小时想知道为什么从方法返回的 ifstream 而不是直接从调用方法中使用的 ifstream 慢了大约 10 倍 - 结果是缓冲区。
【解决方案2】:

当然src.rdbuf 方法比较慢。它正在同时进行读取和写入。除非您要复制到不同的硬盘或某种形式的网络或附加存储,否则这将比读取块然后写入块要慢。

仅仅因为代码紧凑并不能使其更快。


由于您不能为std::filebuf 重载operator&lt;&lt;(因为它已经重载),所以您无能为力。最好只使用效果不错的方法。

【讨论】:

  • 嗯?他交替读取和写入。如果LEN 足够大(可能几百KB 左右),那么不会有很多磁盘抖动。
  • 我不是在问为什么一种方法更快。 streambuf 有对块进行操作的能力,为什么 operator
  • @Adam:我说的是rdbuf 版本。
【解决方案3】:

尝试改用 C stdio API,它在许多实现中通常会更快(请参阅this thread 了解一些数字),但并非总是如此。例如:

// Error checking omitted for expository purposes
char buffer[LEN];
FILE *src = fopen(srcFile, "rb");
FILE *dest = fopen(destFile, "wb");

int n;
while ((n = fread(buffer, 1, LEN, src)) > 0)
{
    fwrite(buffer, 1, n, dest);
}

fclose(src);
fclose(dest);

【讨论】:

  • 是的,有一种不会在你背后做黑魔法的语言真是太好了;)
  • 一些实现速度更快。其他人比较慢。无论哪种方式,您的循环都是错误的。试试while ((n=fread(...))&gt;0)。照原样,如果在到达文件末尾之前遇到读取问题,它将进入无限循环。
  • @Jerry:我确实说过“出于说明目的而省略了错误检查”,但无论如何,如果fread 遇到错误(这将是非常不寻常的——最可能的原因),它现在应该可以工作了从网络存储(如 AFS 或 NFS)上的文件读取时,网络连接断开)。
猜你喜欢
  • 1970-01-01
  • 2020-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-13
  • 2018-11-15
  • 2018-08-09
相关资源
最近更新 更多