为啥用 C 复制文件比 C++ 快得多?
Posted
技术标签:
【中文标题】为啥用 C 复制文件比 C++ 快得多?【英文标题】:Why is copying a file in C so much faster than C++?为什么用 C 复制文件比 C++ 快得多? 【发布时间】:2014-04-20 03:45:48 【问题描述】:我已经在一个相当大的 C++ 项目上工作了几个星期。我最初的目标是使用这个项目来了解 C++11,并且只使用纯 C++ 代码,避免手动分配和 C 构造。但是,我认为这个问题将迫使我将 C 用于一个小功能,我想知道为什么。
基本上我有一个保存功能,它会在我对其中的数据进行更改之前将一个较大的二进制文件复制到一个单独的位置。文件本身是最大大小约为 700MB 的 CD 映像。这是我使用的原始 C++ 代码:
std::ios::sync_with_stdio(false);
std::ifstream in(infile, std::ios::binary);
std::ofstream out(outfile, std::ios::binary);
std::copy(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(out));
out.close();
in.close();
此代码与 690MB 文件一起使用时只需不到 4 分钟即可完成。我用多个文件运行过它,结果总是一样的; 3分钟内什么都没有。但是,我还发现了以下方式,它运行得更快一些,但仍然没有 C 快:
std::ios::sync_with_stdio(false);
std::ifstream in(infile, std::ios::binary);
std::ofstream out(outfile, std::ios::binary);
out << in.rdbuf();
out.close();
in.close();
这个耗时 24 秒,但仍然比 C 慢 20 倍左右。
环顾四周后,我发现有人需要编写一个 80GB 的文件,并且发现他可以使用 C 全速编写。我决定尝试使用以下代码:
FILE *in = fopen(infile, "rb");
FILE *out = fopen(outfile, "wb");
char buf[1024];
int read = 0;
// Read data in 1kb chunks and write to output file
while ((read = fread(buf, 1, 1024, in)) == 1024)
fwrite(buf, 1, 1024, out);
// If there is any data left over write it out
fwrite(buf, 1, read, out);
fclose(out);
fclose(in);
结果非常令人震惊。这是我在许多不同文件上多次运行后的基准之一:
File Size: 565,371,408 bytes
C : 1.539s | 350.345 MB/s
C++: 24.754s | 21.7815 MB/s - out << in.rdbuf()
C++: 220.555s | 2.44465 MB/s - std::copy()
造成这种巨大差异的原因是什么?我知道 C++ 无法与普通 C 的性能相媲美,但 348MB/s 的差异是巨大的。我有什么遗漏吗?
编辑:
我在 Windows 8.1 64 位操作系统上使用 Visual Studio 2013 进行编译。
编辑 2:
阅读 John Zwinck 的回答后,我决定只走平台特定的路线。因为我仍然想让我的项目跨平台,所以我整理了一个简单的例子。我真的不确定这些是否适用于 Windows 以外的其他系统,但我可以在以后测试 Linux。我无法测试 OSX,但我认为 copyfile 看起来像一个简单的函数,所以我认为它是正确的。
请记住,您需要执行相同的 #ifdef 逻辑来包含特定于平台的标头。
void copy(std::string infile, std::string outfile)
#ifdef _WIN32 || _WIN64
// Windows
CopyFileA(infile.c_str(), outfile.c_str(), false);
#elif __APPLE__
// OSX
copyfile(infile.c_str(), outfile.c_str(), NULL, COPYFILE_DATA);
#elif __linux
// Linux
struct stat stat_buf;
int in_fd, out_fd;
offset_t offset = 0;
in_fd = open(infile.c_str(), O_RDONLY);
fstat(in_fd, &stat_buf);
out_fd = open(outfile.c_str(), O_WRONLY | O_CREAT, stat_buf.st_mode);
sendfile(out_fd, in_fd, &offset, stat_buf.st_size);
close(out_fd);
close(in_fd);
#endif
【问题讨论】:
哪些操作系统,哪些编译器,哪些 C 和 C++ 运行时?如果没有更多细节,这基本上是无法回答的。 (我的魔术 8-ball 认为std::copy
实现会一次调用 read
和 write
一个字符,但它必须这样做并没有内在原因;这完全是一个 QoI 问题。)
@Zack 我将使用该信息添加编辑。
不确定到底发生了什么,但我想您的 C++ 版本正在等待缓冲区刷新,而 C 版本只是将其全部推送并让系统执行其操作。这个 C 版本在 Linux 中会特别快,因为 Linux 允许写入缓冲区扩展到所有可用内存,因此您实际上并不是在写入文件,而只是写入内存。操作系统正在后台写入实际文件。
***.com/q/22759885/2724703的可能重复
你的 c++ 实现也是错误的。它有效,但您不应该一次复制一个字节。在 C++ 中执行此操作的方法是使用 rdbuf()。 std::ifstream src("test", std::ios::binary); std::ofstream dst("to", std::ios::binary); dst << src.rdbuf();
【参考方案1】:
首先,您还应该针对在同一台机器上使用 CLI 复制同一文件进行基准测试。
其次,如果您想要获得最佳性能,您需要使用特定于平台的 API。在 Windows 上可能是 CopyFile/CopyFileEx,在 Mac OS 上是 copyfile,在 Linux 上是 sendfile。其中一些(肯定是 sendfile)提供了使用 C 或 C++ 中的基本可移植东西无法实现的性能。其中一些(CopyFileEx 和 copyfile)提供额外的功能,例如复制文件系统属性和可选的进度回调。
您可以在此处查看一些基准,显示 sendfile 的速度有多快:Copy a file in a sane, safe and efficient way
最后,虽然 C++ iostream 在许多平台上不如 C 文件 I/O 快,但这是事实。如果您非常关心性能,那么使用 C 函数可能会更好。我在进行运行时速度很重要的编程竞赛时遇到过这种情况:使用 scanf 和 printf 而不是 cin 和 cout 在许多系统上都有很大的不同。
【讨论】:
平台特定函数的性能是否总是明显优于上面的 C 代码?如果可能的话,我想避免必须跟上文件操作的 3 种不同实现,但如果它真的更好,那么我会继续使用它。至于 CLI 复制,复制大约需要 2-4 毫秒,这比上面发布的任何内容都要快。 如果有人感兴趣的话:OS X 上的 copyfile() 是开源的 (opensource.apple.com/source/copyfile/copyfile-42/copyfile.c),它使用普通的 read()/write() 循环来复制 copyfile_data( ),缓冲区大小等于文件系统缓冲区大小。 - copyfile 中的所有其他内容都用于复制元数据(扩展属性,...)。 @ozdrgnaDiies 你正在运行什么系统设置,你可以在 2-4 毫秒内复制一个 500mb 的文件?也许它并没有像你想象的那样做。 @bh3244 - 是的,我确信它可以在 2-4 毫秒内排队复制请求。以上是关于为啥用 C 复制文件比 C++ 快得多?的主要内容,如果未能解决你的问题,请参考以下文章