У меня есть приложение C, которое генерирует много выходных данных и для которых скорость является критической.Программа представляет собой цикл по большому (8-12 ГБ) двоичному входному файлу, который необходимо читать последовательно.На каждой итерации считанные байты обрабатываются, а выходные данные генерируются и записываются в несколько файлов, но никогда в несколько файлов одновременно.Поэтому, если вы находитесь в точке, где генерируется вывод, и есть 4 выходных файла, которые вы записываете либо в файл 0, либо в 1, либо в 2, либо 3. В конце итерации я теперь записываю вывод, используя fwrite()
, ожидая, таким образом,операция записи до конца.Общее количество операций вывода велико, до 4 миллионов на файл, а размер вывода файлов варьируется от 100 МБ до 3,5 ГБ.Программа работает на базовом многоядерном процессоре.
Я хочу записать вывод в отдельном потоке и знаю, что это можно сделать с помощью
- Асинхронный ввод / вывод
- Создание потоков
- Порты завершения ввода / вывода
У меня есть 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
с наложенной структурой.Я создал пример программы, в которой я пытаюсь заставить это работать.Однако я столкнулся с двумя проблемами:
Файл открывается только в режиме наложения при удалении уже существующего файла (я пробовал использовать CreateFile
в разных режимах (CREATE_ALWAYS
,CREATE_NEW
, OPEN_EXISTING
), но это не помогает).
Только первый WriteFile
выполняется асинхронно.Остальные команды WriteFile
являются синхронными.По этой проблеме я уже консультировался http://support.microsoft.com/kb/156932. Кажется, проблема, с которой я столкнулся, связана с тем фактом, что «любая операция записи в файл, которая увеличивает его длину, будет синхронной».Я уже пытался решить эту проблему, увеличив размер файла / допустимый размер данных (закомментированная область в коде).Тем не менее, я до сих пор не заставить его работать.Я осознаю тот факт, что может быть так, чтобы получить максимальную отдачу от асинхронного ввода-вывода, я должен 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: %.2f\n",(((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("%x\n",GetLastError());
_PRINTERROR("file not opened properly\n");
}
// 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("%x\n",GetLastError());
_PRINTERROR("error in createevent\n");
}
#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("%i\n",pFile);
// _PRINTERROR("error in set end of file\n");
//}
//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: %x\n",GetLastError());
_PRINTERROR("error while writing file\n");
}
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 background\n");
}
else{
// not overlapped, this message is bad
_PRINTF("not ok\n");
}
// wait to continue
if((WaitForSingleObject(pOverlapped->hEvent,INFINITE))!=WAIT_OBJECT_0){
_PRINTERROR("waiting did not succeed\n");
}
// reset event structure
if(!(ResetEvent(pOverlapped->hEvent))){
printf("%x\n",GetLastError());
_PRINTERROR("error in resetevent\n");
}
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;
}
Полезные ссылки
Я знаю, что этобольшой вопрос, и я хотел бы заранее поблагодарить всех, кто потрудился прочитать его и, возможно, даже ответить!