我正试图将大量的数据写入我的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倍。这就是为什么我认为我应该能跑得更快。

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


当前回答

如果你想快速写入文件流,那么你可以让stream读缓冲区更大:

wfstream f;
const size_t nBufferSize = 16184;
wchar_t buffer[nBufferSize];
f.rdbuf()->pubsetbuf(buffer, nBufferSize);

此外,当向文件写入大量数据时,逻辑扩展文件大小有时比物理扩展文件大小更快,这是因为在逻辑扩展文件时,文件系统在写入之前不会将新空间归零。明智的做法是在逻辑上对文件进行比实际需要更多的扩展,以防止大量的文件扩展。在Windows上通过调用SetFileValidData或xfsctl在XFS系统上使用XFS_IOC_RESVSP64支持逻辑文件扩展名。

其他回答

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

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

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

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

尝试使用内存映射文件。

fstreams本身并不比C流慢,但是它们使用更多的CPU(特别是在没有正确配置缓冲的情况下)。当CPU饱和时,会限制I/O速率。

至少MSVC 2015实现在没有设置流缓冲区时一次复制1个字符到输出缓冲区(参见streambuf::xsputn)。所以一定要设置一个流缓冲区(>0)。

使用以下代码,我可以用fstream获得1500MB/s的写入速度(我的M.2 SSD的全速):

#include <iostream>
#include <fstream>
#include <chrono>
#include <memory>
#include <stdio.h>
#ifdef __linux__
#include <unistd.h>
#endif
using namespace std;
using namespace std::chrono;
const size_t sz = 512 * 1024 * 1024;
const int numiter = 20;
const size_t bufsize = 1024 * 1024;
int main(int argc, char**argv)
{
  unique_ptr<char[]> data(new char[sz]);
  unique_ptr<char[]> buf(new char[bufsize]);
  for (size_t p = 0; p < sz; p += 16) {
    memcpy(&data[p], "BINARY.DATA.....", 16);
  }
  unlink("file.binary");
  int64_t total = 0;
  if (argc < 2 || strcmp(argv[1], "fopen") != 0) {
    cout << "fstream mode\n";
    ofstream myfile("file.binary", ios::out | ios::binary);
    if (!myfile) {
      cerr << "open failed\n"; return 1;
    }
    myfile.rdbuf()->pubsetbuf(buf.get(), bufsize); // IMPORTANT
    for (int i = 0; i < numiter; ++i) {
      auto tm1 = high_resolution_clock::now();
      myfile.write(data.get(), sz);
      if (!myfile)
        cerr << "write failed\n";
      auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count());
      cout << tm << " ms\n";
      total += tm;
    }
    myfile.close();
  }
  else {
    cout << "fopen mode\n";
    FILE* pFile = fopen("file.binary", "wb");
    if (!pFile) {
      cerr << "open failed\n"; return 1;
    }
    setvbuf(pFile, buf.get(), _IOFBF, bufsize); // NOT important
    auto tm1 = high_resolution_clock::now();
    for (int i = 0; i < numiter; ++i) {
      auto tm1 = high_resolution_clock::now();
      if (fwrite(data.get(), sz, 1, pFile) != 1)
        cerr << "write failed\n";
      auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count());
      cout << tm << " ms\n";
      total += tm;
    }
    fclose(pFile);
    auto tm2 = high_resolution_clock::now();
  }
  cout << "Total: " << total << " ms, " << (sz*numiter * 1000 / (1024.0 * 1024 * total)) << " MB/s\n";
}

我在其他平台(Ubuntu, FreeBSD)上尝试了这段代码,没有注意到I/O率的差异,但CPU使用率的差异约为8:1 (fstream使用了8倍多的CPU)。所以可以想象,如果我有一个更快的磁盘,fstream写速度会比stdio版本慢。

如果你想快速写入文件流,那么你可以让stream读缓冲区更大:

wfstream f;
const size_t nBufferSize = 16184;
wchar_t buffer[nBufferSize];
f.rdbuf()->pubsetbuf(buffer, nBufferSize);

此外,当向文件写入大量数据时,逻辑扩展文件大小有时比物理扩展文件大小更快,这是因为在逻辑扩展文件时,文件系统在写入之前不会将新空间归零。明智的做法是在逻辑上对文件进行比实际需要更多的扩展,以防止大量的文件扩展。在Windows上通过调用SetFileValidData或xfsctl在XFS系统上使用XFS_IOC_RESVSP64支持逻辑文件扩展名。