考虑以下计划:
#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下编译的,但我无法想象编译器使用的Universe应该对纯I / O基准测试产生任何可衡量的差异。在所有情况下使用的文件系统都是NTFS。
我只是不明白为什么存在这样的巨大的性能差异。我不知道Windows运行速度如此之慢的原因。如何强制Windows以磁盘显然能够的全速运行?
(以上数字适用于旧戴尔笔记本电脑上的OpenSUSE 13.1 32位和Windows XP 32位。但我在办公室的几台PC上观察到类似的速度差异,运行各种版本的Windows。)
编辑:可执行文件及其写入的文件都驻留在外部USB硬盘上,该硬盘格式为NTFS,几乎完全为空。碎片几乎肯定不是问题。 可能是某种驱动程序问题,但我在运行不同版本Windows的其他几个系统上看到了相同的性能差异。没有安装防病毒软件。
只是为了咯咯笑,我尝试将其更改为直接使用Win32 API。 (显然这只适用于Windows。)时间变得稍微不稳定,但仍然只是之前的百分之几。除非我指定FILE_FLAG_WRITE_THROUGH
;然后它显着更慢。其他一些标志使它变慢,但我找不到那个让它快速更快 ...
答案 0 :(得分:3)
您需要将文件内容同步到磁盘,否则您只是测量操作系统正在执行的缓存级别。
在关闭文件之前调用fsync
。
如果不这样做,大部分执行时间很可能花在等待刷新缓存上,以便新数据可以存储在其中,但肯定你写的一部分数据不会被写出来关闭文件时到磁盘。那么,执行时间的差异可能是由于linux在可用缓存空间用完之前缓存了更多的写入。相反,如果在关闭文件之前调用fsync
, all ,在进行时间测量之前,应将写入的数据刷新到磁盘。
我怀疑如果你添加一个fsync
电话,两个系统上的执行时间差异不会太大。
答案 1 :(得分:0)
您的测试不是测量性能的好方法,因为不同操作系统和库中的不同优化可以产生巨大差异(编译器本身不必进行测试)很大的区别)。
首先,我们可以认为fwrite
(或对FILE*
进行操作的任何内容)是OS层之上的库层。可以有不同的缓冲策略有所作为。例如,实现fwrite
的一种智能方法是刷新缓冲区,然后将数据块直接发送到OS而不是通过缓冲层。这可以在下一步产生巨大的优势
其次,我们有可以不同方式处理写入的OS /内核。一种智能优化方法是通过对页面进行别名来复制页面,然后在其中一个别名中使用copy-on-write。 Linux在为进程分配内存(包括数组所在的BSS部分)时已经(差不多)这样做 - 它只是将页面标记为零并且可以为所有这些页面保留单个这样的页面,然后每当创建一个新页面有人在零页面上进行了更改。再次执行此技巧意味着内核可以在磁盘缓冲区中对此类页面进行别名。这意味着在写入这样的零块时内核不会在磁盘高速缓存上运行不足,因为它只占用4KiB的实际内存(页表除外)。如果数据块中有实际数据,也可以采用这种策略。
这意味着写入可以快速完成非常,而实际上不需要将任何数据传输到磁盘(fwrite
完成之前),即使数据甚至不需要从中复制记忆中的一个地方。
因此,您使用不同的库和不同的操作系统,并且它们在不同的时间执行不同的任务并不奇怪。
答案 2 :(得分:0)
对于全部为零的页面,有一些特殊的优化。在写出来之前,你应该用随机数据填充页面。