2011-01-05 54 views
3

我是一个新用户,我的英文不太好,所以我希望能够清楚。 当你试图扩大它们的尺寸时,我们正面临着使用大文件(1GB或更多)的性能问题,尤其是(看起来)。Windows上C++的I/O性能差异很大

反正...来验证我们的感觉,我们tryed以下

一个(在Win 7 64位,4core,8GB的内存,32位与VC2008编译的代码))打开一个unexisting文件。从1Mb插槽开始写入到1Gb。
现在你有一个1Gb文件。
现在随机化该文件中的10000个位置,寻找该位置并在每个位置写入50个字节,无论您写什么。
关闭文件并查看结果。
创建该文件的时间相当快(约为0.3“),写入10000次的时间总是相同(约为0.03”)。

非常好,这是beginnig。
现在尝试其他的东西...

b)打开一个不存在的文件,寻找1Gb-1字节,只写1个字节。
现在你有另一个1Gb文件。
按照下面的步骤完全相同的方式处理'a',关闭文件并查看结果。
时间创建的文件是你能想象的更快(约0.00009"),但写的时候是你无法相信.... 90" !!!!!
B.1)打开一个文件unexisting,不写任何字节。
像以前一样行动起来,寻求和写作,关闭文件并看看结果。
时间写长都是一样的:90" !!!!!

好吧......这是相当惊人的,但再有更多的

C)打开文件!你创造了'a'的情况下,不要截断它...再次随机化10000个位置并像以前一样行动,你的速度与之前一样,大约为0,03“”写入10000次。

听起来不错...尝试另一个步骤。

d)现在打开你的情况下,“B”创建的文件,也不会截断它...再次随机位置10000和之前采取行动。你慢一遍又一遍,但时间减少到...... 45" !!也许,试图再次,时间将减少。

其实我不知道为什么...... 任何想法?

以下是我用来测试我在previuos案例中所说的代码的一部分(为了有一个干净的编译,我只是从一些源代码中剪下&粘贴,对不起)。
样本可以随机读取和写入,有序或反向排序模式,但只写入随机顺序是最清晰的测试。
我们尝试过使用std :: fstream,但也直接使用CreateFile(),WriteFile()等等结果是一样的(即使std :: fstream实际上慢一点)。

参数的情况下的 'A'=> -f_tempdir_ \ casea.dat -n10000 -t -p -w
参数情况下 'B'=> -f_tempdir_ \ caseb.dat -n10000 -t -v -w
参数情况下 'B.1'=> -f_tempdir_ \ caseb.dat -n10000 -t -w
参数情况下的 'C'=> -f_tempdir_ \ casea.dat -n10000 -w
参数的情况下' d” => -f_tempdir_ \ caseb.dat -n10000 -w

运行测试(甚至其他)和见...

// iotest.cpp : Defines the entry point for the console application. 
    // 

    #include <windows.h> 
    #include <iostream> 
    #include <set> 
    #include <vector> 
    #include "stdafx.h" 

    double RealTime_Microsecs() 
    { 
    LARGE_INTEGER fr = {0, 0}; 
    LARGE_INTEGER ti = {0, 0}; 
    double time = 0.0; 

    QueryPerformanceCounter(&ti); 
    QueryPerformanceFrequency(&fr); 

    time = (double) ti.QuadPart/(double) fr.QuadPart; 
    return time; 
    } 

    int main(int argc, char* argv[]) 
    { 
    std::string sFileName ; 
    size_t stSize, stTimes, stBytes ; 
    int retval = 0 ; 

    char *p = NULL ; 
    char *pPattern = NULL ; 
    char *pReadBuf = NULL ; 

    try { 
     // Default 
     stSize = 1<<30 ; // 1Gb 
     stTimes = 1000 ; 
     stBytes = 50 ; 

     bool bTruncate = false ; 
     bool bPre = false ; 
     bool bPreFast = false ; 
     bool bOrdered = false ; 
     bool bReverse = false ; 
     bool bWriteOnly = false ; 

     // Comsumo i parametri 
     for(int index=1; index < argc; ++index) 
     { 
      if ('-' != argv[index][0]) throw ; 
      switch(argv[index][1]) 
      { 
      case 'f': sFileName = argv[index]+2 ; 
       break ; 
      case 's': stSize = xw::str::strtol(argv[index]+2) ; 
       break ; 
      case 'n': stTimes = xw::str::strtol(argv[index]+2) ; 
       break ; 
      case 'b':stBytes = xw::str::strtol(argv[index]+2) ; 
       break ; 
      case 't': bTruncate = true ; 
       break ; 
      case 'p' : bPre = true, bPreFast = false ; 
       break ; 
      case 'v' : bPreFast = true, bPre = false ; 
       break ; 
      case 'o' : bOrdered = true, bReverse = false ; 
       break ; 
      case 'r' : bReverse = true, bOrdered = false ; 
       break ; 
      case 'w' : bWriteOnly = true ; 
       break ; 
      default: throw ; 
       break ; 
      } 
     } 

     if (sFileName.empty()) 
     { 
      std::cout << "Usage: -f<File Name> -s<File Size> -n<Number of Reads and Writes> -b<Bytes per Read and Write> -t -p -v -o -r -w" << std::endl ; 
      std::cout << "-t truncates the file, -p pre load the file, -v pre load 'veloce', -o writes in order mode, -r write in reverse order mode, -w Write Only" << std::endl ; 
      std::cout << "Default: 1Gb, 1000 times, 50 bytes" << std::endl ; 
      throw ; 
     } 

     if (!stSize || !stTimes || !stBytes) 
     { 
      std::cout << "Invalid Parameters" << std::endl ; 
      return -1 ; 
     } 

     size_t stBestSize = 0x00100000 ; 


     std::fstream fFile ; 
     fFile.open(sFileName.c_str(), std::ios_base::binary|std::ios_base::out|std::ios_base::in|(bTruncate?std::ios_base::trunc:0)) ; 

     p = new char[stBestSize] ; 
     pPattern = new char[stBytes] ; 
     pReadBuf = new char[stBytes] ; 
     memset(p, 0, stBestSize) ; 
     memset(pPattern, (int)(stBytes&0x000000ff), stBytes) ; 

     double dTime = RealTime_Microsecs() ; 

     size_t stCopySize, stSizeToCopy = stSize ; 

     if (bPre) 
     { 
      do { 
       stCopySize = std::min(stSizeToCopy, stBestSize) ; 
       fFile.write(p, stCopySize) ; 
       stSizeToCopy -= stCopySize ; 
      } while (stSizeToCopy) ; 
      std::cout << "Creating time is: " << xw::str::itoa(RealTime_Microsecs()-dTime, 5, 'f') << std::endl ; 
     } 
     else if (bPreFast) 
     { 
      fFile.seekp(stSize-1) ; 
      fFile.write(p, 1) ; 
      std::cout << "Creating Fast time is: " << xw::str::itoa(RealTime_Microsecs()-dTime, 5, 'f') << std::endl ; 
     } 

     size_t stPos ; 

     ::srand((unsigned int)dTime) ; 

     double dReadTime, dWriteTime ; 

     stCopySize = stTimes ; 

     std::vector<size_t> inVect ; 
     std::vector<size_t> outVect ; 
     std::set<size_t> outSet ; 
     std::set<size_t> inSet ; 

     // Prepare vector and set 
     do { 
      stPos = (size_t)(::rand()<<16) % stSize ; 
      outVect.push_back(stPos) ; 
      outSet.insert(stPos) ; 

      stPos = (size_t)(::rand()<<16) % stSize ; 
      inVect.push_back(stPos) ; 
      inSet.insert(stPos) ; 
     } while (--stCopySize) ; 

     // Write & read using vectors 
     if (!bReverse && !bOrdered) 
     { 
     std::vector<size_t>::iterator outI, inI ; 
     outI = outVect.begin() ; 
     inI = inVect.begin() ; 
     stCopySize = stTimes ; 
     dReadTime = 0.0 ; 
     dWriteTime = 0.0 ; 
     do { 
      dTime = RealTime_Microsecs() ; 
      fFile.seekp(*outI) ; 
      fFile.write(pPattern, stBytes) ; 
      dWriteTime += RealTime_Microsecs() - dTime ; 
      ++outI ; 

      if (!bWriteOnly) 
      { 
       dTime = RealTime_Microsecs() ; 
       fFile.seekg(*inI) ; 
       fFile.read(pReadBuf, stBytes) ; 
       dReadTime += RealTime_Microsecs() - dTime ; 
       ++inI ; 
      } 
     } while (--stCopySize) ; 
     std::cout << "Write time is " << xw::str::itoa(dWriteTime, 5, 'f') << " (Ave: " << xw::str::itoa(dWriteTime/stTimes, 10, 'f') << ")" << std::endl ; 
     if (!bWriteOnly) 
     { 
      std::cout << "Read time is " << xw::str::itoa(dReadTime, 5, 'f') << " (Ave: " << xw::str::itoa(dReadTime/stTimes, 10, 'f') << ")" << std::endl ; 
     } 
     } // End 

     // Write in order 
     if (bOrdered) 
     { 
      std::set<size_t>::iterator i = outSet.begin() ; 

      dWriteTime = 0.0 ; 
      stCopySize = 0 ; 
      for(; i != outSet.end(); ++i) 
      { 
       stPos = *i ; 
       dTime = RealTime_Microsecs() ; 
       fFile.seekp(stPos) ; 
       fFile.write(pPattern, stBytes) ; 
       dWriteTime += RealTime_Microsecs() - dTime ; 
       ++stCopySize ; 
      } 
      std::cout << "Ordered Write time is " << xw::str::itoa(dWriteTime, 5, 'f') << " in " << xw::str::itoa(stCopySize) << " (Ave: " << xw::str::itoa(dWriteTime/stCopySize, 10, 'f') << ")" << std::endl ; 

      if (!bWriteOnly) 
      { 
       i = inSet.begin() ; 

       dReadTime = 0.0 ; 
       stCopySize = 0 ; 
       for(; i != inSet.end(); ++i) 
       { 
       stPos = *i ; 
       dTime = RealTime_Microsecs() ; 
       fFile.seekg(stPos) ; 
       fFile.read(pReadBuf, stBytes) ; 
       dReadTime += RealTime_Microsecs() - dTime ; 
       ++stCopySize ; 
       } 
       std::cout << "Ordered Read time is " << xw::str::itoa(dReadTime, 5, 'f') << " in " << xw::str::itoa(stCopySize) << " (Ave: " << xw::str::itoa(dReadTime/stCopySize, 10, 'f') << ")" << std::endl ; 
      } 
     }// End 

     // Write in reverse order 
     if (bReverse) 
     { 
      std::set<size_t>::reverse_iterator i = outSet.rbegin() ; 

      dWriteTime = 0.0 ; 
      stCopySize = 0 ; 
      for(; i != outSet.rend(); ++i) 
      { 
       stPos = *i ; 
       dTime = RealTime_Microsecs() ; 
       fFile.seekp(stPos) ; 
       fFile.write(pPattern, stBytes) ; 
       dWriteTime += RealTime_Microsecs() - dTime ; 
       ++stCopySize ; 
      } 
      std::cout << "Reverse ordered Write time is " << xw::str::itoa(dWriteTime, 5, 'f') << " in " << xw::str::itoa(stCopySize) << " (Ave: " << xw::str::itoa(dWriteTime/stCopySize, 10, 'f') << ")" << std::endl ; 

      if (!bWriteOnly) 
      { 
       i = inSet.rbegin() ; 

       dReadTime = 0.0 ; 
       stCopySize = 0 ; 
       for(; i != inSet.rend(); ++i) 
       { 
       stPos = *i ; 
       dTime = RealTime_Microsecs() ; 
       fFile.seekg(stPos) ; 
       fFile.read(pReadBuf, stBytes) ; 
       dReadTime += RealTime_Microsecs() - dTime ; 
       ++stCopySize ; 
       } 
       std::cout << "Reverse ordered Read time is " << xw::str::itoa(dReadTime, 5, 'f') << " in " << xw::str::itoa(stCopySize) << " (Ave: " << xw::str::itoa(dReadTime/stCopySize, 10, 'f') << ")" << std::endl ; 
      } 
     }// End 

     dTime = RealTime_Microsecs() ; 
     fFile.close() ; 
     std::cout << "Flush/Close Time is " << xw::str::itoa(RealTime_Microsecs()-dTime, 5, 'f') << std::endl ; 

     std::cout << "Program Terminated" << std::endl ; 

    } 
    catch(...) 
    { 
     std::cout << "Something wrong or wrong parameters" << std::endl ; 
     retval = -1 ; 
    } 

    if (p) delete []p ; 
    if (pPattern) delete []pPattern ; 
    if (pReadBuf) delete []pReadBuf ; 

    return retval ; 
    } 
+0

您是否测量了所有不同测试的完整运行时间? (包括为情况a创建1Gb文件) – 2011-01-05 12:24:57

回答

4

如果你没有冲洗这些变化,并且你有足够的免费RAM来缓存它,那么你只是在测量你的RAM的速度。

此外,请注意,某些文件系统支持未分配的文件区域的“孔”。如果文件系统有这样的支持(我不知道在Windows上是否有这样做,但他们可能),那么对1G进行“寻找”的测试然后写入1个字节,将会创建一个大部分是“洞”的文件, ,因此需要写入很少的块。

最后,您应该重复执行每次测试和多次,每次刷新整个缓存(这可以不使用sysinternals工具在Windows上重新启动),并使用任何随访AV软件在空闲机器上运行禁用。如果您发现性能存在很大差异,则会发生一些奇怪的事情。


一定要确保通过前面的测试做任何写操作都开始下一个测试之前完全刷新到磁盘,否则会完全拧你的数据了。

最后,不要关注虚拟机上的性能数据。如果您希望获得一致的结果(即使您正在部署到VM中),请在真实硬件上执行所有性能测试。

+2

+1。粗略的搜索表明,Windows NTFS中的默认FS在他们的文档定义为“稀疏文件优化”时确实支持“漏洞” – 2011-01-05 12:06:53

3

它可能很大程度上取决于您正在使用的文件系统。我对NTFS文件系统知之甚少,但许多FS优化了空间空间sparse files,其中空白空间被定义为文件中没有任何内容的块。请注意,这些位置的读数将返回0,但什么也没有写

第一个测试会在文件中创建并分配所有必需的块。然后随机修改只是修改已经分配的磁盘块的内容。其余的测试会创建该文件,但从不实际写入文件,因此FS可能包含文件的大小,但不包含分配的块。在这种情况下,测试必须寻找,将磁盘块分配给文件,将其全部初始化为0,然后写入数据。您可以在here中查看所有情况下从磁盘分配的实际空间。

结果45" 的第三块,还不如涉及到击中分配(快速写入)和未分配(慢写)块的平衡的随机位置

编辑:看来,NTFS确实有这种类型的Sparse File优化

+0

GetCompressedFileSize()在'a'和'b'两种情况下给出了相同的结果。 – 2011-01-05 12:22:54

+0

我检查了稀疏文件文档,并使用了一个“sparse_checker”实用程序来分析文件,说明它们是否稀疏以及可以保存多少空间。 我首先想到的是任何文件都可能很稀疏,NTFS会自行决定如何操作。但是我错了。 你必须请求一个文件是稀疏的,或者它的行为像任何其他文件一样。所以...稀疏文件似乎不是这种行为的原因 – 2011-01-05 13:47:38

3

这一切都是由磁盘缓存和文件系统结构决定的,一些读写操作只需要在缓存中操作,如果数据已经存在并在稍后刷新,其他操作必须运行在磁盘上,因为数据不在缓存中,并且会更慢。

文件系统中块的排列也会影响这些事情。在极端情况下,将一个字节添加到GB文件可能会导致它的很大一部分被移动到磁盘上。其他因素(如碎片,块大小和读取/写入方式)会进行优化,并且缓存可能会导致您看到的效果。