C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Записать буфер в файл, с использованием указателя на его начало
Slader
Отправлено: 16.01.2005, 14:09


Ученик-кочегар

Группа: Участник
Сообщений: 27



Помогите пожалуйста! Есть указатель на начало буфера определенной длины, и этот буфер нужно записать в файл. Причем, чем быстрее — тем лучше. Функции WriteFile() и fwrite() с указателем не работают — они записывают значение самого указателя (4 байта).
Есть ли в С вообще какие-нибудь функции для блочной записи файлов?
Slader
Отправлено: 16.01.2005, 18:24


Ученик-кочегар

Группа: Участник
Сообщений: 27



Уточню свой вопрос: как это сделать при помощи структуры OVERLAPPED?
Я делаю так:
CODE


 hFile=CreateFile("data.dat", GENERIC_WRITE,FILE_SHARE_WRITE,
                              NULL,CREATE_ALWAYS,FILE_FLAG_OVERLAPPED,NULL);

  fdata = SetFilePointer(hFile, 0, NULL, FILE_BEGIN);

  OVERLAPPED ovlp;
               
                       ovlp.OffsetHigh=0;
                       ovlp.hEvent=NULL;
                       ovlp.Offset=fdata;

  DWORD *tmp;
  DWORD halfbuffer = ...;
  DWORD *ADCdata;
  DWORD fl1;

  ........

  tmp = ADCdata+(halfbuffer*fl1);    //  указатель на первую или
                                                         //  на вторую половину буфера,
                                                         //  ADCdata-указатель на начало    
                                                         //  буфера; fl=(0:1); halfbuffer -
                                                         //  размер половины буфера  (не в
                                                         //  байтах, а в количестве слов —        
                                                         //  WORD)    
 
 ........

 WriteFile(hFile, tmp, halfbuffer*sizeof(WORD), &written, &ovlp);

 ovlp.Offset+=halfbuffer;

 .........



Программа пишет "Ошибка записи в файл", то есть, значение функции WriteFile = 0 (сделана соответствующая проверка). Я так думаю, что проблема связана со структурой OVERLAPPED. Без нее программа пишет данные в файл, хотя и криво. Где здесь ошибка?
Георгий
Отправлено: 17.01.2005, 00:08


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



набросал 3 варианта работы с файлами через Win32API — один быстрее другого.

1й — открыли файл, записыли, сбросили буферы, закрыли файл — ничего необычного
2й — используем блочный ввод/вывод — в CreateFile указываем флажки записи без буферизации, последовательного ввода/вывода и сквозной записи — без буферизации на уровне драйвера диска. для реализации последнего пункта — записи напрямую через драйвер необходимо данные размещать в буфере выровненном на границу страницы для чего и используется функция VirtualAlloc. в остальоном всё тоже самое, что и первый вариант.
3й — используем асинхронную запись — заказываем у ОС операцию записи, а сами, пока данные пишутся, занимается своими делами. для реализации асинхронности испольуется структура OVERLAPPED, а в остальном тоже самое, что и 2й вариант.

проведя небольшой опыт убедился, что разница в скорости между 1 и 2 вариантом есть, но она не очень большая — около 10-15%
а результаты 3го варианта практически не отличались от 2го и, как вывод, его можно использовать тогда, когда нужна халявная паралельность.
Также надо отметить, что во 2м и 3м варианте при записи на диск загрузка процессора была близка к 5%, в отличии от 1го варианта где она была около 30-40%.
кстати — вспомнил Ваш предыдущий вопрос и подозреваю, что экономия почти 30% машинного времени будут совсем не лишними. т.к. перекачка данных с железки, визуализация и, возможно, какая-нибудь дополнительная обработка сожрут практически всё время.

PS. что возвращает WriteFile при использовании overlapped не смотрел, но по идее она и должно вернуть 0 т.к. операция записи не завершена, а всего лишь поставлена в очередь к драйверу дискового ввода вывода.

CODE
const nBlocks = 100000;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
const size_t len = 512*nBlocks;
DWORD writen=0;
char *buffer=new char[len];
for (int i=0;i<len;++i)buffer[i]=(char)i;

const HANDLE hFile=CreateFile("c:/tst1.bin",GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0);

int t = GetTickCount();
WriteFile(hFile,buffer,len*sizeof(buffer[0]),&writen,0);
FlushFileBuffers( hFile );
t=GetTickCount()-t;
delete buffer;
CloseHandle(hFile);

this->Edit1->Text=t;

}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
DWORD writen=0;

//узнаём размер сектора
DWORD BytesPerSector=0;
GetDiskFreeSpace("c:",0,&BytesPerSector,0,0);

size_t len = BytesPerSector*nBlocks;
//захват памяти выровненной на границу страницы
char *buffer=(char*)VirtualAlloc(0,len,MEM_COMMIT,PAGE_READWRITE);

for (int i=0;i<len;++i)buffer[i]=(char)i;

const HANDLE hFile=CreateFile("c:/tst2.bin",GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,
FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN|FILE_FLAG_WRITE_THROUGH,0);
int t = GetTickCount();
WriteFile(hFile,buffer,len*sizeof(buffer[0]),&writen,0);
FlushFileBuffers( hFile );
t=GetTickCount()-t;
VirtualFree(buffer,0,MEM_RELEASE);
CloseHandle(hFile);
this->Edit2->Text=t;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
DWORD writen=0;

//узнаём размер сектора
DWORD BytesPerSector=0;
GetDiskFreeSpace("c:",0,&BytesPerSector,0,0);

size_t len = BytesPerSector*nBlocks;
//захват памяти выровненной на границу страницы
char *buffer=(char*)VirtualAlloc(0,len,MEM_COMMIT,PAGE_READWRITE);

for (int i=0;i<len;++i)buffer[i]=(char)i;

OVERLAPPED over;
ZeroMemory(&over,sizeof(over));

const HANDLE hFile=CreateFile("c:/tst3.bin",GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,
FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN|FILE_FLAG_WRITE_THROUGH|FILE_FLAG_OVERLAPPED,0);

int t = GetTickCount();
WriteFile(hFile,buffer,len*sizeof(buffer[0]),&writen,&over);

// ожидание реального окончания записи на диск
// в принципе такую проверку надо делать только перед
// записью следующего блока (перед следующим вызовом WriteFile)
GetOverlappedResult(hFile,&over,&writen,true);
FlushFileBuffers( hFile );
t=GetTickCount()-t;

VirtualFree(buffer,0,MEM_RELEASE);
CloseHandle(hFile);
this->Edit3->Text=t;

}
//---------------------------------------------------------------------------


Отредактировано Георгий — 17/01/2005, 04:47
Slader
Отправлено: 17.01.2005, 20:20


Ученик-кочегар

Группа: Участник
Сообщений: 27



Спасибо, Георгий! Это почти то, что нужно! НО: как быть с захватом памяти, выровненной на границу страницы, если буфер УЖЕ выделен? Дело в том, что дрова железяки написаны таким образом, что она сама выделяет буфер и возвращает только указатель на него. Я запрашиваю непосредственно размер буфера, и все... Причем, запрос размера и возврат указателя происходят в разных функциях, то есть заменить этот процесс не получится, и обойти, к сожалению, тоже. В заголовочном файле драйвера функция запроса буфера обозначена как

QUOTE

DllExport(DWORD) RequestBuffer(LPVOID dev, DWORD *Size); //in words

// здесь dev — структура с параметрами железяки


В примерах к железке запись в файл идет через Mapping, но этот метод достаточно медленный (~25% медленнее, чем обычные АПИ-функции записи). И как здесь быть?
Георгий
Отправлено: 17.01.2005, 22:17


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



знаешь — подозреваю, что дрова железки написаны с плотным использованием API и наверняка память выделяется через VirtualAlloc, а не из кучи твоей задачи.

Кстати — железка через DMA работает? если DMA, то буфер точно выровнян — контроллер DMA хочет физический адрес на границе параграфа и чтоб этого добиться наверняка не стали заморачиваться и захватили память начиная со страницы. Наверное поэтому и захват памяти под буфер сделан как специальная функция.

На рабочей машине винт быстрый? попробуй для проверки писать 2мя способами одновременно — 1м способом и 2м в разные файлы, если содержимое файлов совпадёт и во время работы никаких "недопустимых операций" не будет, то догадки оказались справедливыми.

На худой конец я б попробовал буфер железки копировать (MemoryCopy) в выровненный. копирование память-память идёт со скоростью около ~1GBps так что не намного худше сталобы. Но боюсь это и загрузку процессора поднимет и 10% выигрыша по скорости записи съест.

PS. Можно отладчиком пройтись по коду функции захватывающей память под буфер и посмотреть чтоже она использует.

Отредактировано Георгий — 17/01/2005, 23:21

Вернуться в Вопросы программирования в C++Builder