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 |
|
|