1186 / 542 / 78
Регистрация: 01.07.2009
Сообщений: 3,517
1

Максимально эфективное бинарное чтение из файла под Windows

14.12.2012, 02:50. Показов 5041. Ответов 40
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Задача: максимально эфективно (быстро) читать данные из файла. Каким это будет происходить образом - в виде си функции, с++ или винапи функции не имеет значения, имеет значение лишь результат.
Как мне известно размер странички в Windows = 4Кб так что быстрее всего по идее чтение должно происходить если читать по 4 кб, но как лучше всего это сделать?

Вообще в итоге я буду использоать 64битные значения после того как считаю кусок файла. Вот мой маленький набросок, который првда закончился фиаско потому что стандартная fread, как оказалось, имеет буфер под чтение меньше 4 кб так что нужно что-то другое быстрое
C++
1
2
3
4
5
6
7
    FILE* readFrom = fopen("input.bin","rb"); _ASSERT(readFrom);
    uint64_t bufferRead[64];//мой буффер на 4 кб
    size_t countBlocks;//считано блоков
 
        // вернёт 0 потому что fread не может считать 4096 байтиков
    countBlocks = fread(bufferRead,1,4096,readFrom);
    fclose(readFrom);
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
14.12.2012, 02:50
Ответы с готовыми решениями:

Бинарное чтение файла
<?php @error_reporting ( E_ALL ); @ini_set ( 'display_errors', true ); @ini_set ( 'html_errors',...

Бинарное чтение файла
Что за каракули в конце файла,? Спасибо.

Бинарное чтение из файла
Всем доброго времени суток. Столкнулся с проблемой чтения из файла в бинарном режиме. У меня есть...

Бинарное чтение файла
FILE *ftm; ftm = _wfopen(fff, L"r+"); //fff - это переменная wchar_t содержащая имя файла! ...

40
Почетный модератор
7393 / 2639 / 281
Регистрация: 29.07.2006
Сообщений: 13,696
14.12.2012, 03:24 21
Author24 — интернет-сервис помощи студентам
Gepar, потому что вызывать много раз операцию чтения с диска не даст никакого прироста к производительности.
Еще хорошо бы установить флаг FILE_FLAG_SEQUENTIAL_SCAN, чтобы упреждающее чтение работало лучше. Это тоже даст прирост.

Добавлено через 3 минуты
Gepar, и, конечно, это зависит от самого диска и и оси. Поэтому говорить, что 4к - это самый быстрый вариант - ошибка. Бери больше, и проверяй, а не верь плохим программистам.
1
1186 / 542 / 78
Регистрация: 01.07.2009
Сообщений: 3,517
14.12.2012, 03:24  [ТС] 22
Ну ... если хапать по n мб то как тогда узнать сколько там оп свободно (чтобы при чтении не перестараться и не привести к перекачке данных в своп) ?
0
Почетный модератор
7393 / 2639 / 281
Регистрация: 29.07.2006
Сообщений: 13,696
14.12.2012, 03:29 23
Цитата Сообщение от Gepar Посмотреть сообщение
то как тогда узнать сколько там оп свободно
Зачем узнавать? Компы с объемом меньше 1 гига уже редкость. А буфер в метр еще в своп никого не загонял.
1
Форумчанин
Эксперт CЭксперт С++
8215 / 5045 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
14.12.2012, 03:38 24
Gepar, я к тому сказал, что память сейчас оперативная взлетает в объемах по експоненте и беспокоится о её нехватке бесмысленно.
Вот, что я предлагаю:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
    FILE* pFile;
    pFile = fopen ("test.txt" , "rb");
    if (pFile == NULL) exit(1);
    fseek (pFile , 0 , SEEK_END);
    unsigned long long size = ftell (pFile); // не знаю, существует ли он в С
    rewind (pFile);
    char *buf = (char *) malloc(sizeof(char) * size);
    if (buf == NULL) exit(2);
    size_t result = fread (buf, 1, size, pFile);
    if (result != size) exit (3);
    //printf("%s", buf);
    free(buf);
    fclose(pFile);
    return 0;
}
Добавлено через 3 минуты
Мегабайтный файл считывает за 0.004 секунды (на моем компе естественно)
0
256 / 46 / 4
Регистрация: 24.11.2012
Сообщений: 466
14.12.2012, 03:48 25
Цитата Сообщение от Gepar Посмотреть сообщение
Vourhey, ну проаргументируй, тема то интересная.
можно я? какая скорость чтения с HDD и какая из RAM? Что мешает загружать в RAM по гигу или 2 и обрабатывать данные из оперативной памяти? Странный конечно подход... по 4Кб.
Цитата Сообщение от Gepar Посмотреть сообщение
MrGluck, ну блин, предлагаешь сразу хапонуть 2 гб в оперативе, пошифровать их там (использовав ещё + 100 мб) и потом записать?
очевидно же.
0
Форумчанин
Эксперт CЭксперт С++
8215 / 5045 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
14.12.2012, 04:04 26
И я уж молчу про http://help.3l.com/3L/index.ht... read_h.htm из С11
0
836 / 343 / 67
Регистрация: 20.11.2012
Сообщений: 795
14.12.2012, 09:50 27
Самое эффективное - читать функцией ReadFile (winapi) большими блоками (>= 64 кб) с флагом FILE_FLAG_NO_BUFFERING. Это чтобы не было 100500 промежуточных буферов, как в случае с функами с и с++. Если время обработки довольно большое, то имеет смысл читать асинхронно и обрабатывать покусочно по мере поступения.
1
1186 / 542 / 78
Регистрация: 01.07.2009
Сообщений: 3,517
14.12.2012, 15:50  [ТС] 28
WhiteP, а можно небольшой пример чтения ею бинарных данных (с правильным получением хендлера файла для этой функции), допустим для файла input.bin и буффера на 64 кб uint64_t[8192] ну и соответственно с получением информации сколько реально байт было считано (ато она я смотрю просто BOOL возвращает).
0
836 / 343 / 67
Регистрация: 20.11.2012
Сообщений: 795
14.12.2012, 17:32 29
Вот как-то так. Естественно, чтобы был прирост скорости - нужно избавиться от частых вызовов тормозных потоков C++ (std::cout). Тут они только для наглядности.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <windows.h>
 
#define BUFSIZE 65536 //должен быть кратен размеру сектора (обычно 512, точнее - GetDiskFreeSpace)
 
int main()
{
    //буфер должен быть выровнен по границе сектора. VirtualAlloc выравнивает начало буфера
    //автоматически по адресу кратному 64.
    BYTE * pBuf = (PBYTE)VirtualAlloc(NULL, BUFSIZE, MEM_COMMIT, PAGE_READWRITE);
 
    if(!pBuf)
    {
        std::cout<<"VirtualAlloc error. Code: "<<GetLastError()<<std::endl;
        return -1;
    }
 
    std::cout<<"Memory successfully allocated. Address: "<<(int)pBuf<<std::endl;
        
    HANDLE hFile = CreateFileW(L"c:\\input.bin", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING, 0);
    
    if(hFile == INVALID_HANDLE_VALUE)
    {
        std::cout<<"CreateFile error. Code: "<<GetLastError()<<std::endl;
        VirtualFree(pBuf, BUFSIZE, MEM_RELEASE);
        return -1;
    }
 
    std::cout<<"File successfully opened. Start reading."<<std::endl;
 
    DWORD readed = 0, fSize = 0, step = 1;
    BOOL isOk = 0;
 
    //двигать файловый указатель можно только по границам секторов!
    while(isOk = ReadFile(hFile, pBuf, BUFSIZE, &readed, 0))
    {
        std::cout<<"Step "<<step++<<". Readed bytes: "<<readed<<std::endl;
        fSize+=readed;
        if(readed < BUFSIZE)
            break;
    }
    if(isOk == FALSE)
        std::cout<<GetLastError()<<std::endl;
 
    std::cout<<" Total readed bytes: "<<fSize<<". FileSize is "<<GetFileSize(hFile, 0)<<std::endl;  
 
    CloseHandle(hFile);
    VirtualFree(pBuf, BUFSIZE, MEM_RELEASE);
 
    return 0;
}
1
1186 / 542 / 78
Регистрация: 01.07.2009
Сообщений: 3,517
17.12.2012, 01:13  [ТС] 30
WhiteP, не получается у меня использовать этот твой винапи вариант - почему-то WriteFile нифига не пишет и чесно признаётся что записывает каждый раз 0 байт. Какого хрена оно так ?
Вот код где я его использую, вот почему он каждый раз нифигашеньки не запсывает WriteFile этот
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#define BUFSIZE 65536 //должен быть кратен размеру сектора (обычно 512, точнее - GetDiskFreeSpace)
 
void CryptoSaveFastHelper(Cryptographer* crypto,CryptoMode mode, uint64_t* keys, char* from, char* to)
{
    //буфер должен быть выровнен по границе сектора. VirtualAlloc выравнивает начало буфера
    //автоматически по адресу кратному 64.
    uint64_t * pBuf = (uint64_t*)VirtualAlloc(NULL, BUFSIZE, MEM_COMMIT, PAGE_READWRITE);
 
    if(!pBuf)
    {
        std::cout<<"VirtualAlloc error. Code: "<<GetLastError()<<std::endl;
        throw -1;
    }
 
    int* key; //ключ (и)
 
    //uint64_t bufferRead[512];//мой буффер на 4 кб
    uint64_t lastBlock = 0ULL;//последний блок который нужно будет дописать в конец файла
 
    HANDLE inputData = CreateFile(from, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN, 0);
    HANDLE outputData = CreateFile(to, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING, 0);
 
    DWORD readed = 0;//сколько байт считано
    DWORD writed = 0;//сколько байт записано
    BOOL isOk = 0;
 
    switch(mode)
    {
    case _DESEBC:
        {
            while(isOk = ReadFile(inputData, pBuf, BUFSIZE, &readed, 0))
            {
                //шифруем считанные блоки
                crypto->EnCrypt(pBuf,readed/8);
 
                _ASSERT(WriteFile(outputData,pBuf,readed-(readed%8),&writed,0)==0);//отрабатывает без ошибок, но нифига не пишеет!
                if(readed < BUFSIZE)
                    break;
            }
 
            //если число байтиков в файле делилось на 4 без остатка
            if(!(readed % 8))
            {
                lastBlock = crypto->EnCrypt(lastBlock);
                WriteFile(outputData,&lastBlock,(DWORD)8,&writed,0);
            }
            else//иначе добиваем нолями предпоследний а в последний пишем размер
            {
                lastBlock = readed%8;
                lastBlock = crypto->EnCrypt(lastBlock);
                char* ptr = (char*)(pBuf+(readed/8));
                ptr += (readed%8);
                for(int i=0;i<8-(readed%8); i++)
                    *ptr++=0;
 
                pBuf[(readed/8)] = crypto->EnCrypt(pBuf[(readed/8)]);
 
                WriteFile(outputData,&pBuf[readed/8],(DWORD)8,&writed,0);
                WriteFile(outputData,&lastBlock,(DWORD)8,&writed,0);
            }
        }
        break;
    }
    CloseHandle(inputData);
    CloseHandle(outputData);
}
0
Модератор
Эксперт по электронике
8908 / 6677 / 918
Регистрация: 14.02.2011
Сообщений: 23,512
17.12.2012, 01:24 31
Gepar, если тебе нужна скорость, то почему ты не задействуешь файлы проецируемые в память
грубо говоря создается копия файла в озу
ты работаешь с этой копией (скорость обращения к памяти намного больше чем к винту) точно так же как с файлом
потом при закрытии он скидывается на диск

Добавлено через 3 минуты
вот вроде неплохая статейка
http://www.developing.ru/com/m... es_01.html
1
1186 / 542 / 78
Регистрация: 01.07.2009
Сообщений: 3,517
17.12.2012, 01:47  [ТС] 32
ValeryS, я почитаю сейчас, но почему WriteFile может писать по 0 байт? Не, ну хотябы мусор там какой-то писала или ещё чего, а так файл получается в 0 байт независимо от того сколько я пытался писать ею, почему же?
*Вообще я смотрю 98% времени таки тратиться на шифрование, даже если читать по 4 кб максимум так что я думаю на чтении файла не стоит совсем сильно заострять внимание, чтение через винапи функции без буферизации должно быть достаточно, но нужно исправить эту багу с WriteFile ...
0
Модератор
Эксперт по электронике
8908 / 6677 / 918
Регистрация: 14.02.2011
Сообщений: 23,512
17.12.2012, 02:22 33
Цитата Сообщение от Gepar Посмотреть сообщение
но почему WriteFile может писать по 0 байт?
не может
это значит какая то ошибка при записи вызывай GetLastError.
http://vsokovikov.narod.ru/New... tefile.htm
особенно посмотри раздел Замечания

Добавлено через 4 минуты
я кстати в коде не нашел проверки outputData
файл ведь может не открыться
1
1186 / 542 / 78
Регистрация: 01.07.2009
Сообщений: 3,517
17.12.2012, 03:00  [ТС] 34
Цитата Сообщение от ValeryS Посмотреть сообщение
это значит какая то ошибка при записи вызывай GetLastError.
Отдаёт код 57 после записи файла (до записи 0, те ошибка была при записи). Как определить что произошло, я уже позабывал эти винапишные штучки, там вроде что-то было для этого.

Добавлено через 44 секунды
Цитата Сообщение от ValeryS Посмотреть сообщение
я кстати в коде не нашел проверки outputData
файл ведь может не открыться
Оно бы вываливалось тогда при записи, файл открывается, я в отладчике просто смотрел что хендлер получен корректный.
0
256 / 46 / 4
Регистрация: 24.11.2012
Сообщений: 466
17.12.2012, 03:09 35
Цитата Сообщение от Gepar Посмотреть сообщение
Как определить что произошло
исключения же
0
Модератор
Эксперт по электронике
8908 / 6677 / 918
Регистрация: 14.02.2011
Сообщений: 23,512
17.12.2012, 03:14 36
Цитата Сообщение от Gepar Посмотреть сообщение
Как определить что произошло,
Примерно так
C++
1
2
 if(!(WriteFile(outputData,&pBuf[readed/8],(DWORD)8,&writed,0))
      GetLastError();
потом в меню (в студии) сервис - поиск ошибки набиваешь номер и читаешь расшифровку

Цитата Сообщение от Gepar Посмотреть сообщение
Отдаёт код 57 после записи файла
аппаратная неисправность сетевой платы
а вот 0х57 уже ближе
Параметр задан неверно.
0
836 / 343 / 67
Регистрация: 20.11.2012
Сообщений: 795
17.12.2012, 08:20 37
При записи файла, открытого с флагом FILE_FLAG_NO_BUFFERING нужно писать также как и читать - размером кратным размеру сектора (512 байт). Буфер для записи нужно выровнять (выделить память VirtualAlloc). В конце (если размер выходного файла не кратен 512) нужно вызвать SetEndOfFile. Имеет смысл формировать буфер как можно больший - т.к. чем меньше обращений к диску, тем лучше. Минимум, если размер файла позвоялет - 64Кб.
При чтении файлов с флагом FILE_FLAG_NO_BUFFERING и чтении файла через проекцию (FileMapping) - скорость первого примерно на 15%-30% выше.
Писать возможно и правда лучше и удобней в проецируемый файл.
0
1186 / 542 / 78
Регистрация: 01.07.2009
Сообщений: 3,517
17.12.2012, 12:27  [ТС] 38
Цитата Сообщение от WhiteP Посмотреть сообщение
При записи файла, открытого с флагом FILE_FLAG_NO_BUFFERING нужно писать также как и читать - размером кратным размеру сектора (512 байт).
Так я же так и делаю - я записываю тот буфер полностью, те 64 кб, но после той записи в DWORD writed по прежнему 0, но почему ? Или оно не скинеться на диск пока я не вызову SetEndOfFile? Но тогда где оно держит по 50 мб которые я пытался шифровать, не в оп же, я же смотрел что моё приложение так много оп не жрёт. Можешь ещё раз посмотреть на мой код, может заметишь что не так. Вроде же всё просто: считал в буфер 64 кб, записал их в файл, если считал меньше 64 кб - поредактировал последний блок и снова записал в файл. По крайней мере первые же записи что по полных 64 кб должны писаться, но я отладчиком гоняю и writed постоянно = 0 и не меняется. Столько проблем с десом этим
0
Модератор
Эксперт по электронике
8908 / 6677 / 918
Регистрация: 14.02.2011
Сообщений: 23,512
17.12.2012, 14:31 39
Цитата Сообщение от Gepar Посмотреть сообщение
я записываю тот буфер полностью, те 64 кб,
чей то я не увидел
Цитата Сообщение от Gepar Посмотреть сообщение
WriteFile(outputData,&lastBlock,(DWORD)8,&writed,0);
8 байт
64 кб это все таки 65536
0
836 / 343 / 67
Регистрация: 20.11.2012
Сообщений: 795
17.12.2012, 14:45 40
C++
1
_ASSERT(WriteFile(outputData,pBuf,readed-(readed%8),&writed,0)==0);//отрабатывает без ошибок, но нифига не пишеет!
Если размер файла меньше 512 байт, например, то писать и не будет. Последний блок также может не записаться и в других случаях, т.к. не все то, что кратно 8 кратно 512.
Тут надо
C
1
2
#define ALIGN_UP(x, align) ((x)+((align)-1))&(~((align)-1))
WriteFile(outputData,pBuf,ALIGN_UP(readed, 512),&writed,0)
Запустил в студии вот такой код. Файл скопировался без проблем.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    
    HANDLE hFile = CreateFileW(L"c:\\input.bin", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING, 0);
 
    HANDLE hOutFile = CreateFileW(L"d:\\out.bin", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING, 0);
...
while(isOk = ReadFile(hFile, pBuf, BUFSIZE, &readed, 0))
    {
        //std::cout<<"Step "<<step++<<". Readed bytes: "<<readed<<std::endl;
        fSize+=readed;
 
        //WriteFile(hOutFile, pBuf, (ALIGN_UP(readed, 512)),&written, NULL);
        std::cout << written <<"bytes writed."<<std::endl;
 
        if(readed < BUFSIZE)
            break;
    }
0
17.12.2012, 14:45
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
17.12.2012, 14:45
Помогаю со студенческими работами здесь

Бинарное чтение файла
Всем доброго времени суток. Стоит задача реализовать зашифровать файл RSA ключом в 1024 бит. Но...

Максимально быстрое чтение очень большого файла
Добрый день, задача прочитать очень большой файл (1GB-10GB+). Нужно провести операцию с каждой...

Бинарное чтение из файла с пoмощью функции fread()
Подскажите, пожалуйста, почему feof() может возвращать конец файла далеко до его реального конца?...

Бинарное дерево поиска. Как осуществить запись в файл и чтение из файла
Добрый день! Если кому не жаль своего времени окажите помощь! Необходимо осуществить запись в...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru