[英]Why is this C++ program slower on Windows than Linux?
考虑以下程序:
#define _FILE_OFFSET_BITS 64 // Allow large files.
#define REVISION "POSIX Revision #9"
#include <iostream>
#include <cstdio>
#include <ctime>
const int block_size = 1024 * 1024;
const char block[block_size] = {};
int main()
{
std::cout << REVISION << std::endl;
std::time_t t0 = time(NULL);
std::cout << "Open: 'BigFile.bin'" << std::endl;
FILE * file;
file = fopen("BigFile.bin", "wb");
if (file != NULL)
{
std::cout << "Opened. Writing..." << std::endl;
for (int n=0; n<4096; n++)
{
size_t written = fwrite(block, 1, block_size, file);
if (written != block_size)
{
std::cout << "Write error." << std::endl;
return 1;
}
}
fclose(file);
std::cout << "Success." << std::endl;
time_t t1 = time(NULL);
if (t0 == ((time_t)-1) || t1 == ((time_t)-1))
{
std::cout << "Clock error." << std::endl;
return 2;
}
double ticks = (double)(t1 - t0);
std::cout << "Seconds: " << ticks << std::endl;
file = fopen("BigFile.log", "w");
fprintf(file, REVISION);
fprintf(file, " Seconds: %f\n", ticks);
fclose(file);
return 0;
}
std::cout << "Something went wrong." << std::endl;
return 1;
}
它只是将4GB的零写入磁盘上的文件并计算所需的时间。
在Linux下,平均需要148秒。 在Windows下,在同一台PC上,平均需要247秒。
我到底做错了什么?!
代码是在GCC for Linux和Visual Studio for Windows下编译的,但是我无法想象编译器使用的宇宙应该对纯I / O基准产生任何可测量的差异。 在所有情况下使用的文件系统是NTFS。
我只是不明白为什么存在如此巨大的性能差异。 我不知道为什么Windows运行这么慢。 如何强制Windows以磁盘显然能够的全速运行?
(以上数字适用于旧戴尔笔记本电脑上的OpenSUSE 13.1 32位和Windows XP 32位。但我观察到办公室周围的几台PC上存在类似的速度差异,运行各种版本的Windows。)
编辑:可执行文件及其写入的文件都驻留在外部USB硬盘上,该硬盘格式为NTFS并且几乎完全为空。 碎片几乎肯定不是问题。 它可能是某种驱动程序问题,但我在运行不同版本Windows的其他几个系统上看到了相同的性能差异。 没有安装防病毒软件。
只是为了咯咯笑,我尝试将其更改为直接使用Win32 API。 (显然这只适用于Windows。)时间变得稍微不稳定,但仍然只是之前的百分之几。 除非我指定FILE_FLAG_WRITE_THROUGH
; 然后它变得非常慢。 一些其他的标志使它变慢,但我找不到让它变得更快的那个...
您需要将文件内容同步到磁盘,否则您只是测量操作系统正在执行的缓存级别。
在关闭文件之前调用fsync
。
如果不这样做,大部分执行时间很可能花在等待刷新缓存上,以便新数据可以存储在其中,但当然你写的一部分数据不会写入磁盘关闭文件的时间。 那么,执行时间的差异可能是由于linux在可用缓存空间用完之前缓存了更多的写入。 相反,如果在关闭文件之前调用fsync
,则应在进行时间测量之前将所有写入的数据刷新到磁盘。
我怀疑如果你添加一个fsync
调用,两个系统上的执行时间差别不会太大。
您的测试不是测量性能的好方法,因为不同的OS和库中的不同优化可以产生巨大的差异(编译器本身不必产生很大的不同)。
首先,我们可以认为fwrite
(或任何在FILE*
上运行的)是OS层之上的库层。 可以有不同的缓冲策略有所作为。 例如,一种实现fwrite
智能方法是刷新缓冲区,然后将数据块直接发送到OS而不是通过缓冲层。 这可以在下一步产生巨大的优势
其次,我们有可以不同地处理写入的OS /内核。 一种智能优化方法是通过对页面进行别名来复制页面,然后在其中一个别名中使用copy-on-write。 Linux在为进程分配内存(包括数组所在的BSS部分)时已经(差不多)这样做 - 它只是将页面标记为零并且可以为所有这些页面保留单个这样的页面,然后每当创建一个新页面有人在零页面上进行了更改。 再次执行此技巧意味着内核可以在磁盘缓冲区中对此类页面进行别名。 这意味着在写入这样的零块时内核不会在磁盘高速缓存上运行不足,因为它只占用4KiB的实际内存(页表除外)。 如果数据块中存在实际数据,则也可以采用此策略。
这意味着写入可以非常快速地完成,而实际上不需要将任何数据传输到磁盘(在fwrite
完成之前),即使数据甚至不必从内存中的一个地方复制到另一个地方。
因此,您使用不同的库和不同的OS,并且它们在不同的时间执行不同的任务并不奇怪。
对于全为零的页面有特殊优化。 在写出之前,您应该使用随机数据填充页面。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.