ofstream::operator<<(streambuf) 是一种复制文件的缓慢方法

发布于 2024-12-02 02:45:30 字数 808 浏览 1 评论 0 原文

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

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 上找到了一篇文章(抱歉,找不到正确的链接) now) 说我可以将上面的所有代码替换为:

dest << src.rdbuf();

这很好且紧凑,但隐藏了很多关于它正在做什么的信息。事实证明它也非常慢 因为 ofstream::operator<<(streambuf) 的实现一次移动 1 个字符(使用 snetxc()/sputc())。

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

更新:在 Windows 上使用运算符<<(streambuf) 效率较低。 .read()/.write() 循环看起来总是比operator<< 执行得更好。

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

I need a cross-platform, no external library, way of copying a file. In my first pass I came up with (error handling omitted):

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());
}

This worked nicely and I knew exactly what it was doing.

Then I found a post on stackoverflow (sorry, can't find a link right now) that says I can replace all of the above code with:

dest << src.rdbuf();

Which is nice and compact, but hides a lot about what it's doing. It also turns out to be really slow because the implementation of ofstream::operator<<(streambuf) moves things 1 character at a time (using snetxc()/sputc()).

Is there a way for me to make this method faster? Is there a drawback to my original method?

Update: There's something inefficient about using operator<<(streambuf) on windows. The .read()/.write() loop looks to always perform better than operator<<.

Also, changing the size of the buffer in the code above does not affect the size of the reads and writes to the hard drive. To do that you need to set the buffers using stream.rdbuf()->pubsetbuf().

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

独自←快乐 2024-12-09 02:45:30

我想知道你的 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();

}

I wonder if your fstream is unbuffered by default. GCC 4.5.2 by default uses an internal buffer, but I don't think that's required by the standard. Have you tried using pubsetbuf (see below) to set a buffer for your in/out streams.

A quick test on my system, if I set LEN to 0 (and therefore unbuffered), it took 10 seconds to copy a 1 MB file. With a 4k buffer, it completed in less than a second.

#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();

}
鹊巢 2024-12-09 02:45:30

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

仅仅因为代码紧凑并不意味着它会更快。


由于您无法重载 std::filebufoperator<< (因为它已经重载了),因此您无能为力。最好只使用效果相当好的方法。

Of course the src.rdbuf method is slower. It's doing reading and writing at the same time. Unless you're copying to a different harddisk or some form of network or attached storage, that's going to be slower than reading a block and then writing a block.

Just because code is compact does not make it faster.


Since you can't overload operator<< for the std::filebuf (since it's already overloaded), there isn't much you can do. It's better to just use the method that works reasonably well.

_失温 2024-12-09 02:45:30

尝试使用 C stdio API,它在许多实现中通常会更快(请参阅 此线程 对于某些数字),但并非总是如此。例如:

// 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);

Try using the C stdio API instead, it can often be faster in many implementations (see this thread for some numbers), though not always. For example:

// 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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文