Асинхронный ввод-вывод в c с помощью Windows API: какой метод использовать и почему мой код выполняется синхронно?
у меня есть приложение C, которое генерирует много вывода и для которого скорость имеет решающее значение. Программа в основном представляет собой цикл над большим (8-12GB) двоичным входным файлом, который должен быть прочитан последовательно. На каждой итерации байты чтения обрабатываются, а выходные данные генерируются и записываются в несколько файлов, но никогда в несколько файлов одновременно. Поэтому, если вы находитесь в точке, где генерируется вывод, и есть 4 выходных файла, которые вы пишете в файл 0 или 1 или 2 или 3. В конце итерация теперь я пишу вывод, используя fwrite()
, тем самым ожидая завершения операции записи. Общее количество выходных операций велико, до 4 миллионов на файл, а размер выходных файлов колеблется от 100 МБ до 3,5 ГБ. Программа работает на базовом многоядерном процессоре.
я хочу написать вывод в отдельном потоке, и я знаю, что это можно сделать с
- Asyncronous I / O
- создание темы
- завершение ввода-вывода порты
у меня есть 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 проблемами:
файл открывается только в перекрывающемся режиме, когда я удаляю уже существующий файл (я попытался использовать
CreateFile
в разных режимы (CREATE_ALWAYS
,CREATE_NEW
,OPEN_EXISTING
), но это не помогает).только первый
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;
}
полезное ссылки
http://www.ibm.com/developerworks/linux/library/l-async/?ca=dgr-lnxw02aUsingPOISIXAIOAPI
http://www.flounder.com/asynchexplorer.htm#Asynchronous%20I/O
I знайте, что это большой вопрос, и я хотел бы заранее поблагодарить всех, кто берет на себя труд прочитать его и, возможно, даже ответить!
3 ответов
вы должны иметь возможность заставить это работать, используя перекрывающуюся структуру.
вы на правильном пути: система запрещает вам писать асинхронно, потому что каждый файл записи расширяет размер файла. Тем не менее, вы делаете расширение размера файла неправильно. Просто вызов SetFileSize фактически не зарезервирует место в MFT. Используйте функцию SetFileValidData. Это выделит кластеры для вашего файла (обратите внимание, что они будут содержать любой мусор, который был на диске там), и вы должны иметь возможность выполнять WriteFile и ваши вычисления параллельно.
Я бы держался подальше от FILE_FLAG_NO_BUFFERING. Я полагаю, вы хотите больше производительности с параллелизмом? Не мешайте кэшу выполнять свою работу.
другой вариант, который вы не рассматривали, - это сопоставленный с памятью файл. Они доступны в Windows и Linux. Существует удобная абстракция Boost, которую вы можете использовать.
с файлом, сопоставленным с памятью, каждый поток в вашем процессе может записать свой вывод в файл в свое время, предполагая, что размеры записи известны, и каждый поток имеет свою собственную область вывода.
операционная система позаботится о записи сопоставленных страниц на диск, когда это необходимо или когда он получает вокруг него или когда вы закрываете файл. Может, когда закроешь файл. Теперь, когда я думаю об этом, некоторые операционные системы могут потребовать, чтобы вы позвонили msync, чтобы гарантировать это.
Я не понимаю, почему вы хотите писать асинхронно. Параллельные действия не всегда ускоряют их. Если вы пишете два файла одновременно на один и тот же диск, это почти всегда будет намного быстрее. Если это так, просто напишите их один за другим.
Если у вас есть какой-то причудливый диск, такой как SSD или виртуальный RAM-диск, параллельная запись может будет быстрее. Вы должны создать файл с полным размером, а затем выполнить параллель магия.
асинхронная запись хороша, но выполняется любой ОС в любом случае. Потенциальная выгода для вас заключается в том, что вы можете сделать другое вещи, чем запись на диск, как отображение индикатора выполнения. Это где multi-threading может помочь вам.
таким образом, imho вы должны использовать последовательную запись или параллельную запись на несколько дисков.
hth