Асинхронный ввод-вывод в c с помощью Windows API: какой метод использовать и почему мой код выполняется синхронно?

у меня есть приложение C, которое генерирует много вывода и для которого скорость имеет решающее значение. Программа в основном представляет собой цикл над большим (8-12GB) двоичным входным файлом, который должен быть прочитан последовательно. На каждой итерации байты чтения обрабатываются, а выходные данные генерируются и записываются в несколько файлов, но никогда в несколько файлов одновременно. Поэтому, если вы находитесь в точке, где генерируется вывод, и есть 4 выходных файла, которые вы пишете в файл 0 или 1 или 2 или 3. В конце итерация теперь я пишу вывод, используя fwrite(), тем самым ожидая завершения операции записи. Общее количество выходных операций велико, до 4 миллионов на файл, а размер выходных файлов колеблется от 100 МБ до 3,5 ГБ. Программа работает на базовом многоядерном процессоре.

я хочу написать вывод в отдельном потоке, и я знаю, что это можно сделать с

  1. Asyncronous I / O
  2. создание темы
  3. завершение ввода-вывода порты

у меня есть 2 типа вопросов, а именно концептуальных и специфические.

Концептуальный Вопрос

какой был бы лучший подход. Обратите внимание, что приложение должно быть портативным для Linux, однако я не вижу, как это было бы очень важно для моего выбора для 1-3, так как я бы написал обертку вокруг чего-либо конкретного ядра/API. Для меня самый важный критерий-скорость. Я прочитал, что Вариант 1 вряд ли увеличит производительность программы и что ядро в любом случае создает новые потоки для операции ввода-вывода, так почему бы сразу не использовать опцию (2) с тем преимуществом, что кажется проще программировать (также, поскольку мне не удалось с опцией (1), см. проблемы с кодом ниже).

обратите внимание, что я читал https://stackoverflow.com/questions/3689759/how-can-i-run-a-specific-function-of-thread-asynchronously-in-c-c, но я не вижу мотивации в том, что использовать, основываясь на природе приложение. Поэтому я надеюсь, что кто-то может дать мне совет, что было бы лучше в моей ситуации. Также из книги" системное программирование Windows " Джонсона М. Харта я знаю, что рекомендация использует потоки, главным образом из-за простоты. Однако, будет ли это также быстрее?

Вопрос Код

этот вопрос включает в себя попытки, которые я сделал до сих пор, чтобы сделать асинхронный ввод-вывод работы. Я понимаю, что это большой кусок кода, так что это не так просто смотреть в. В любом случае, я был бы очень признателен за любую попытку.

чтобы уменьшить время выполнения, я пытаюсь записать вывод с помощью нового потока с помощью WINAPI через CreateFile() С FILE_FLAGGED_OVERLAP С структуру overlapped. Я создал пример программы, в которой я пытаюсь заставить это работать. Тем не менее, я столкнулся с 2 проблемами:

  1. файл открывается только в перекрывающемся режиме, когда я удаляю уже существующий файл (я попытался использовать CreateFile в разных режимы (CREATE_ALWAYS, CREATE_NEW, OPEN_EXISTING), но это не помогает).

  2. только первый WriteFile выполняется асинхронно. Остаток WriteFile команды синхронно. По этой проблеме я уже консультировался http://support.microsoft.com/kb/156932. Похоже, что проблема у меня связана с тем, что "любая операция записи в файл, которая расширяет его длину, будет синхронной". Я уже пытался решить эту проблему, увеличив размер файла / valid размер данных (закомментированная область в коде). Однако я до сих пор не могу заставить его работать. Я знаю о том, что это может быть так, чтобы получить максимальную отдачу от асинхронного ввода-вывода, я должен CreateFile С FILE_FLAG_NO_BUFFERING, однако я не могу заставить это работать.

обратите внимание, что программа создает файл около 120 мб в пути выполнения. Также обратите внимание, что операторы печати "не в порядке" нежелательны, я хотел бы видеть, что "может работать в фоновом режиме" появляется на моем экране... Что здесь не так?

#include <windows.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ASYNC  // remove this definition to run synchronously (i.e. using fwrite)

#ifdef ASYNC
    struct _OVERLAPPED *pOverlapped;
    HANDLE *pEventH;
    HANDLE *pFile;
#else
    FILE *pFile;
#endif

#define DIM_X   100
#define DIM_Y   150000

#define _PRINTERROR(msgs)
 {printf("file: %s, line: %d, %s",__FILE__,__LINE__,msgs);
 fflush(stdout);
 return 0;}    

#define _PRINTF(msgs)
 {printf(msgs);
  fflush(stdout);}      

#define _START_TIMER       
 time_t time1,time2;       
 clock_t clock1;        
 time(&time1);        
 printf("start time: %s",ctime(&time1));  
 fflush(stdout);

#define _END_TIMER
 time(&time2);
 clock1 = clock();
 printf("end time: %s",ctime(&time2));
 printf("elapsed processor time: %.2fn",(((float)clock1)/CLOCKS_PER_SEC));
 fflush(stdout);

double  aio_dat[DIM_Y] = {0};

double do_compute(double A,double B, int arr_len);

int main()
{
 _START_TIMER;

 const char *pName = "test1.bin";

    DWORD dwBytesToWrite;
    BOOL bErrorFlag = FALSE;

 int j=0;
 int i=0;
 int fOverlapped=0;

    #ifdef ASYNC
     // create / open the file
        pFile=CreateFile(pName,
            GENERIC_WRITE,   // open for writing
            0,               // share write access
            NULL,            // default security
            CREATE_ALWAYS,   // create new/overwrite existing
         FILE_FLAG_OVERLAPPED,  // | FILE_FLAG_NO_BUFFERING,   // overlapped file
         NULL);           // no attr. template

  // check whether file opening was ok
     if(pFile==INVALID_HANDLE_VALUE){
   printf("%xn",GetLastError());
   _PRINTERROR("file not opened properlyn");
  }

  // make the overlapped structure
     pOverlapped = calloc(1,sizeof(struct _OVERLAPPED)); 
  pOverlapped->Offset = 0;
  pOverlapped->OffsetHigh = 0;

   // put event handle in overlapped structure
  if(!(pOverlapped->hEvent = CreateEvent(NULL,TRUE,FALSE,NULL))){
   printf("%xn",GetLastError());
   _PRINTERROR("error in createeventn");
  }
 #else
  pFile = fopen(pName,"wb");
 #endif 

 // create some output 
 for(j=0;j<DIM_Y;j++){
     aio_dat[j] = do_compute(i, j, DIM_X);
 }

 // determine how many bytes should be written
  dwBytesToWrite = (DWORD)sizeof(aio_dat);

 for(i=0;i<DIM_X;i++){ // do this DIM_X times

        #ifdef ASYNC
         //if(i>0){
      //SetFilePointer(pFile,dwBytesToWrite,NULL,FILE_CURRENT);
      //if(!(SetEndOfFile(pFile))){
      // printf("%in",pFile);
      // _PRINTERROR("error in set end of filen");
      //}
      //SetFilePointer(pFile,-dwBytesToWrite,NULL,FILE_CURRENT);
      //}

      // write the bytes
      if(!(bErrorFlag = WriteFile(pFile,aio_dat,dwBytesToWrite,NULL,pOverlapped))){
       // check whether io pending or some other error
       if(GetLastError()!=ERROR_IO_PENDING){
    printf("lasterror: %xn",GetLastError());
    _PRINTERROR("error while writing filen");
       }
       else{
        fOverlapped=1;
       }
      }
          else{
       // if you get here output got immediately written; bad!
       fOverlapped=0;
      }

      if(fOverlapped){   
       // do background, this msgs is what I want to see
       for(j=0;j<DIM_Y;j++){
        aio_dat[j] = do_compute(i, j, DIM_X);
       }
       for(j=0;j<DIM_Y;j++){
        aio_dat[j] = do_compute(i, j, DIM_X);
       }

       _PRINTF("can do work in backgroundn");
      }
      else{
       // not overlapped, this message is bad
       _PRINTF("not okn");
      } 

                // wait to continue
      if((WaitForSingleObject(pOverlapped->hEvent,INFINITE))!=WAIT_OBJECT_0){
       _PRINTERROR("waiting did not succeedn");
      }

      // reset event structure
      if(!(ResetEvent(pOverlapped->hEvent))){
       printf("%xn",GetLastError());
       _PRINTERROR("error in reseteventn");
      }

      pOverlapped->Offset+=dwBytesToWrite;

  #else
      fwrite(aio_dat,sizeof(double),DIM_Y,pFile);
   for(j=0;j<DIM_Y;j++){
    aio_dat[j] = do_compute(i, j, DIM_X);
   }
   for(j=0;j<DIM_Y;j++){
    aio_dat[j] = do_compute(i, j, DIM_X);
   }
  #endif
 }

 #ifdef ASYNC
     CloseHandle(pFile);
  free(pOverlapped);
 #else
     fclose(pFile);
 #endif

 _END_TIMER;

 return 1;
} 

double do_compute(double A,double B, int arr_len)
{
  int i;
  double   res = 0;
  double  *xA = malloc(arr_len * sizeof(double));
  double  *xB = malloc(arr_len * sizeof(double));

  if ( !xA || !xB )
   abort();

  for (i = 0; i < arr_len; i++) {
   xA[i] = sin(A);
   xB[i] = cos(B);
   res = res + xA[i]*xA[i];
  }

  free(xA);
  free(xB);

  return res;
}

полезное ссылки

I знайте, что это большой вопрос, и я хотел бы заранее поблагодарить всех, кто берет на себя труд прочитать его и, возможно, даже ответить!

3 ответов


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

вы на правильном пути: система запрещает вам писать асинхронно, потому что каждый файл записи расширяет размер файла. Тем не менее, вы делаете расширение размера файла неправильно. Просто вызов SetFileSize фактически не зарезервирует место в MFT. Используйте функцию SetFileValidData. Это выделит кластеры для вашего файла (обратите внимание, что они будут содержать любой мусор, который был на диске там), и вы должны иметь возможность выполнять WriteFile и ваши вычисления параллельно.

Я бы держался подальше от FILE_FLAG_NO_BUFFERING. Я полагаю, вы хотите больше производительности с параллелизмом? Не мешайте кэшу выполнять свою работу.


другой вариант, который вы не рассматривали, - это сопоставленный с памятью файл. Они доступны в Windows и Linux. Существует удобная абстракция Boost, которую вы можете использовать.

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

операционная система позаботится о записи сопоставленных страниц на диск, когда это необходимо или когда он получает вокруг него или когда вы закрываете файл. Может, когда закроешь файл. Теперь, когда я думаю об этом, некоторые операционные системы могут потребовать, чтобы вы позвонили msync, чтобы гарантировать это.


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

Если у вас есть какой-то причудливый диск, такой как SSD или виртуальный RAM-диск, параллельная запись может будет быстрее. Вы должны создать файл с полным размером, а затем выполнить параллель магия.

асинхронная запись хороша, но выполняется любой ОС в любом случае. Потенциальная выгода для вас заключается в том, что вы можете сделать другое вещи, чем запись на диск, как отображение индикатора выполнения. Это где multi-threading может помочь вам.

таким образом, imho вы должны использовать последовательную запись или параллельную запись на несколько дисков.

hth