Отличная производительность ввода / вывода в C ++ в Windows - PullRequest
3 голосов
/ 05 января 2011

Я новый пользователь, и мой английский не очень хорош, поэтому я надеюсь, что будет ясно.Мы сталкиваемся с проблемой производительности, используя большие файлы (1 ГБ или более), особенно (как кажется), когда вы пытаетесь увеличить их размер.

В любом случае ... чтобы проверить наши ощущения, мы попробовали следующее (наWin 7 64Bit, 4core, 8GB Ram, 32-битный код, скомпилированный с VC2008)

a) Откройте несуществующий файл.Запишите его с начала до 1 ГБ в слотах 1 МБ.
Теперь у вас есть файл 1 ГБ.
Теперь рандомизируйте 10000 позиций в этом файле, ищите эту позицию и записывайте 50 байтов в каждой позиции, независимо от того, что вы пишете.
Закройте файл и посмотрите на результаты.
Время создания файла довольно быстрое (около 0,3 "), время записи 10000 раз быстро все равно (примерно 0.03 ").

Очень хорошо, это начало.
Теперь попробуйте что-нибудь еще ...

b) Откройте несуществующий файл, ищите 1 ГБ-1 байти запишите всего 1 байт.
Теперь у вас есть еще один файл размером 1 ГБ.
Выполните следующие шаги точно так же, как в случае 'a', закройте файл и посмотрите на результаты.
Время для создания файла:тем быстрее вы можете себе представить (примерно 0,00009 "), но время записи - это то, во что вы не можете поверить .... около 90" !!!!!
b.1) Откройте несуществующий файл, не записывайте ни одного байта.
Действуйте, как и прежде, размазывая, ища и записывая, закрываяФайл и посмотрите на результат.
Время записи все равно одинаково: около 90 "!!!!!

Ok ..Это довольно удивительно.Но это еще не все!

c) Снова откройте файл, который вы создали, в случае 'a', не обрезайте его ... снова перемешайте 10000 позиций и действуйте как раньше.Вы быстры, как и раньше, около 0,03 ", чтобы написать 10000 раз.

Это звучит хорошо ... попробуйте другой шаг.

d) Теперь откройтефайл, который вы создали в случае 'b', не обрезайте его ... перемешайте снова 10000 позиций и действуйте как раньше. Вы снова и снова медленны, но время сокращается до ... 45"!! Может быть, попробовав еще раз, время сократится.

Мне действительно интересно, почему ... Есть идея?

Следующее является частью кода, который я использовал для проверки того, что я говорил в предыдущих случаях (вам придется изменить что-то, чтобы получить чистую компиляцию, я просто вырезал и вставлял из некоторого исходного кода, извините).
Образец может считывать и записывать в случайном, упорядоченном или обратном упорядоченном режиме, но наиболее ясным тестом является запись только в случайном порядке.
Мы пытались использовать 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
Параметры для case '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 ;
  }

Ответы [ 3 ]

4 голосов
/ 05 января 2011

Если вы не сбрасываете изменения и у вас достаточно свободного ОЗУ для его кэширования, вы просто измеряете скорость своего ОЗУ.

Также имейте в виду, что некоторые файловые системы поддерживают "дыры"msgstr ", области файла, которые не выделены.Если файловая система имеет такую ​​поддержку (я не знаю, делают ли они что-либо на Windows, но они могут), тогда тест, который «ищет» 1G, затем записывает 1 байт, создаст файл, который в основном является «дырой»следовательно, требуется записать несколько блоков.

Наконец, вы должны повторять каждый тест много и много раз, каждый раз сбрасывая весь кэш (это возможно без перезагрузки в Windows с помощью инструмента sysinternals) и работать нав противном случае бездействующий компьютер с отключенным программным обеспечением AV при доступе.Если вы видите большие расхождения в производительности, происходит что-то странное.


Будьте абсолютно уверены, что все записи, сделанные в предыдущем тесте, полностью сбрасываются на диск перед началом следующего теста, в противном случае это приведет к сбоям.данные полностью.

Наконец, не обращайте внимания на данные о производительности на виртуальной машине.Выполните все тесты производительности на реальном оборудовании, если вы хотите получить согласованные результаты (даже если вы развертываете на виртуальной машине).

3 голосов
/ 05 января 2011

Все дело в кэшировании диска и структуре файловой системы.Некоторые операции чтения / записи могут потребоваться только для работы с кешем, если данные уже находятся там и будут сброшены позже.Другие операции должны выполняться на диске, так как данные не находятся в кэше и будут намного медленнее.

Расположение блоков в файловой системе также может повлиять на эти вещи.В крайнем случае, добавление этого одного байта в файл GB может привести к перемещению значительной его части на диск.Другие факторы, такие как фрагментация, размер блока и способ оптимизации чтения и записи и кэширования, могут вызвать видимые эффекты.

3 голосов
/ 05 января 2011

Это вполне может зависеть от файловой системы, которую вы используете.Я не очень много знаю о файловой системе NTFS, но многие ФС оптимизируют свободное пространство в разреженных файлах , где пустое пространство определяется как блоки в файле, для которых ничего не было написано.Обратите внимание, что чтение вернет 0 для этих позиций, но ничего не было записано.

Первый тест создает и выделяет все необходимые блоки в файле.Затем случайные модификации просто модифицируют содержимое уже выделенных блоков диска.Остальные тесты создают файл, но никогда не записывают его, поэтому FS может содержать размер файла, но не иметь выделенных блоков.Тест в этом случае должен искать, распределять блок с диска в файл, инициализировать его все в 0 и затем записывать данные.Вы можете проверить фактическое пространство, выделенное на диске во всех случаях, как показано здесь .

Третий блок результатов 45 ", также может быть связан со случайными местоположениями, попадающими в балансвыделенные (быстрая запись) и нераспределенные (медленная запись) блоки.

РЕДАКТИРОВАТЬ: Кажется, что NTFS имеет этот тип Разреженный файл оптимизация.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...