我正试图将大量的数据写入我的SSD(固态硬盘)。我说的巨大是指80GB。

我在网上寻找解决方案,但我想到的最好的办法是:

#include <fstream>
const unsigned long long size = 64ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
    std::fstream myfile;
    myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
    //Here would be some error handling
    for(int i = 0; i < 32; ++i){
        //Some calculations to fill a[]
        myfile.write((char*)&a,size*sizeof(unsigned long long));
    }
    myfile.close();
}

使用Visual Studio 2010编译,完全优化,在Windows7下运行,该程序最大可达20MB/s左右。真正困扰我的是,Windows可以以150MB/s到200MB/s之间的速度将文件从另一个SSD复制到这个SSD。至少快7倍。这就是为什么我认为我应该能跑得更快。

有什么办法可以加快我的写作速度吗?


当前回答

尝试使用open()/write()/close() API调用并试验输出缓冲区的大小。我的意思是不要一次传递整个“多-多-字节”缓冲区,做几次写入(即TotalNumBytes / OutBufferSize)。OutBufferSize可以从4096字节到兆字节。

另一个尝试——使用WinAPI OpenFile/CreateFile并使用这篇MSDN文章来关闭缓冲(FILE_FLAG_NO_BUFFERING)。这篇关于WriteFile()的MSDN文章展示了如何获取驱动器的块大小以了解最佳缓冲区大小。

不管怎样,std::ofstream是一个包装器,可能会阻塞I/O操作。请记住,遍历整个n gb数组也需要一些时间。当您写入一个小缓冲区时,它会更快地到达缓存并工作。

其他回答

最好的解决方案是使用双缓冲实现异步写入。

看看时间轴:

------------------------------------------------>
FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|

“F”表示填充缓冲区的时间,“W”表示将缓冲区写入磁盘的时间。所以问题是在写缓冲区到文件之间浪费时间。然而,通过在一个单独的线程上实现写入,你可以像这样立即开始填充下一个缓冲区:

------------------------------------------------> (main thread, fills buffers)
FF|ff______|FF______|ff______|________|
------------------------------------------------> (writer thread)
  |WWWWWWWW|wwwwwwww|WWWWWWWW|wwwwwwww|

F -填充第一个缓冲区 F -填充第二缓冲区 写入文件的第一个缓冲区 写入第二个缓冲区文件 _ -等待操作完成

当填充缓冲区需要更复杂的计算(因此需要更多时间)时,使用缓冲区交换的这种方法非常有用。 我总是实现一个CSequentialStreamWriter类,它隐藏了异步写入,所以对于最终用户来说,接口只有写入函数。

缓冲区大小必须是磁盘集群大小的倍数。否则,通过将一个缓冲区写入两个相邻的磁盘集群,您将最终获得较差的性能。

正在写入最后一个缓冲区。 当您最后一次调用Write函数时,必须确保当前正在被填充的缓冲区也应该写入磁盘。因此CSequentialStreamWriter应该有一个单独的方法,比如Finalize(最后的缓冲区刷新),它应该把最后一部分数据写入磁盘。

错误处理。 当代码开始填充第二个缓冲区时,第一个缓冲区正在另一个线程上写入,但是由于某种原因写入失败了,主线程应该知道这个失败。

------------------------------------------------> (main thread, fills buffers)
FF|fX|
------------------------------------------------> (writer thread)
__|X|

让我们假设CSequentialStreamWriter的接口有Write函数返回bool值或抛出异常,因此在一个单独的线程上有一个错误,你必须记住那个状态,所以下次你在主线程上调用Write或finalize时,该方法将返回False或抛出异常。在什么时候停止填充缓冲区并不重要,即使在失败后提前写入了一些数据,文件很可能会损坏并且无用。

尝试使用open()/write()/close() API调用并试验输出缓冲区的大小。我的意思是不要一次传递整个“多-多-字节”缓冲区,做几次写入(即TotalNumBytes / OutBufferSize)。OutBufferSize可以从4096字节到兆字节。

另一个尝试——使用WinAPI OpenFile/CreateFile并使用这篇MSDN文章来关闭缓冲(FILE_FLAG_NO_BUFFERING)。这篇关于WriteFile()的MSDN文章展示了如何获取驱动器的块大小以了解最佳缓冲区大小。

不管怎样,std::ofstream是一个包装器,可能会阻塞I/O操作。请记住,遍历整个n gb数组也需要一些时间。当您写入一个小缓冲区时,它会更快地到达缓存并工作。

我建议尝试文件映射。我过去在UNIX环境中使用过mmapin,它所能实现的高性能给我留下了深刻的印象

如果你在资源管理器中将一些东西从磁盘A复制到磁盘B, Windows使用DMA。这意味着对于大多数的复制过程,CPU基本上什么也不做,只是告诉磁盘控制器在哪里放置数据,并从哪里获取数据,从而消除了整个链中的一个步骤,而且这个步骤根本不适合移动大量数据(我指的是硬件)。

你所做的事情涉及到CPU很多。 我想给你指出“一些计算来填充[]”部分。我认为这是必要的。你生成一个[],然后你从一个[]复制到一个输出缓冲区(这就是fstream::write所做的),然后你再次生成,等等。

怎么办呢?多线程!(我希望你有一个多核处理器)

叉。 使用一个线程生成一个[]数据 使用另一个将a[]中的数据写入磁盘 您将需要两个数组a1[]和a2[],并在它们之间进行切换 您将需要在线程(信号量、消息队列等)之间进行某种同步。 使用较低级别的、无缓冲的函数,比如Mehrdad提到的WriteFile函数

我编译我的程序在gcc在GNU/Linux和mingw在win7和winxp和工作良好

你可以用我的程序创建一个80gb的文件,只需要把第33行改为

makeFile("Text.txt",1024,8192000);

当退出程序时,文件将被销毁,然后检查文件时,它正在运行

要得到你想要的程序,只需改变程序

第一个是windows程序,第二个是GNU/Linux程序

http://mustafajf.persiangig.com/Projects/File/WinFile.cpp

http://mustafajf.persiangig.com/Projects/File/File.cpp