На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Модераторы: Rouse_, jack128, Krid
  
    > Быстрое чтение/запись файлов
      Довольно часто программе нужно прочитать один/несколько/много больших файлов (больше 100мб).
      После чего сделать некоторые расчеты.
      Есть два основных подхода для чтения файлов:
      Синхронный режим
      Асинхронный режим

      Синхронный режим несколько проще асинхронного, и даже иногда быстрее(в том случаи если файлов действительно много и они очень маленькие(несколько кб) ).
      Асинхронный режим немного сложнее синхронного, но дает значительный прирост в скорости при чтении файла.

      Рассмотрим почему же асинхронный режим быстрее.
      Дело в том что жесткий диск да и любая постоянная внешняя память очень медленная по сравнению с оперативной памятью/процессором.
      И когда программа делает запрос на чтение файла в синхронном режиме, то происходит остановка потока(который вызвал чтение) до тех пор пока не будет прочитаны все запрошенные данные.
      В итоге получается что программа лишнее время простаивает и ничего полезного не делает.
      А вот если б мы могли б сделать запрос на чтение и дальше заниматься другими делами, и по завершению запроса получить уведомление что чтение завершилось то было б значительное ускорение работы программы.
      Как раз такую возможность нам дает асинхронный режим.

      Алгоритм такого чтения следующий:
      0. Получаем размер файла разбиваем его на несколько блоков размером 1-10 мб(размер уже подбирается эмпирически).
      1. Делаем запрос на чтение первого "куска" файла, и ожидаем его полного прочтения.
      2. Делаем второй запрос на чтение, и сразу же переходим к обработке данных прочтенных на предыдущем запросе.
      3. ожидаем завершения запроса на чтение.
      4. переходим на пункт 2.

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

      И так от теории перейдем к практики:
      Список основных функций :
      CreateEvent - создает обьект событие
      CreateFile - Открывает файл, здесь и указывается какой режим чтения.
      ResetEvent - сброс события.
      ReadFile - чтение из файла
      GetOverlappedResult - ожидает результат выполнения асинхронной операции ввода/вывода.
      CloseHandle - закрывает дескриптор объектов.

      Собственно код:
      ExpandedWrap disabled
        function AsyncReadFile(const FileName: string;const Overlapped:TOverlapped): cardinal;
        const
         BufSize = 1024*1024;
        var
         Fi: THandle;
         pBuf:array [0..1] of pointer;//нужно два буфера, в первый читаем, а второй передаем для обработки
         BigSize:array [0..1] of Cardinal;
         Count:array[0..1] of Cardinal;
         index:Cardinal; // определяет в какой буфер читать, а какой передавать на обработку
         Size:^Int64; // размер файла
        begin
        Result:=$FFFFFFFF;
        Fi:=CreateFile(Pchar(FileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED or FILE_FLAG_SEQUENTIAL_SCAN,0); //режим доступа асинхронный + просим систему кешировать файл
        if Fi = INVALID_HANDLE_VALUE then exit;
        GetMem(pBuf[0],BufSize);
        GetMem(pBuf[1],BufSize);
        if (pBuf[0] = nil)or(pBuf[1] = nil) then  exit;
         BigSize[0]:=GetFileSize(fi,@BigSize[1]); //получаем размер файла
         Size:=@BigSize[0];
        if Size^ <> 0 then
        begin
        index:=0;
        //первоче чтение
         count[index and 1]:=BufSize;
         if Size^ < BufSize then count[index and 1]:=Size^;
           ResetEvent(Overlapped.hEvent);
           ReadFile(Fi,pBuf[index and 1]^,count[index and 1],count[index and 1],@Overlapped);
           GetOverlappedResult(Fi,Overlapped,count[index and 1],true);
         
        while count[index and 1]<>0 do
        begin
           Size^:=Size^ -  count[index and 1];
           inc(index);
        //следующий запрос на чтение
           ResetEvent(Overlapped.hEvent);
           count[index and 1]:=BufSize;
           if Size^ < BufSize then count[index and 1]:=Size^;
           ReadFile(Fi,pBuf[index and 1]^,count[index and 1],count[index and 1],@Overlapped);
           dec(index);
        {
            здесь любые ваши вычисления.
            pBuf[ (index and 1)]   - указатель на прочитанные данные.
            Count[ (index and 1)]  - размер прочитанных данных.
         
        }
        //ожидаем завершения чтения
           inc(index);
           GetOverlappedResult(Fi,Overlapped,count[index and 1],true);
        end;
        end;
        Result:=not Result;
        FreeMem(pBuf[0]);
        FreeMem(pBuf[1]);
        CloseHandle(Fi);
        end;
        //пример вызова:
         
        var
          OverRead:TOverlapped;
         res:cardinal;
        begin
          ZeroMemory(@OverRead,sizeof(OverRead));
          OverRead.hEvent := CreateEvent(nil,true,false,nil);
           res:= AsyncReadFile('C:\LargeFile.txt',OverRead);
          CloseHandle(OverRead.hEvent);
        end;
        end;


      Этот код можно переделать и для записи, только нужно немного изменить логику и вызовы ReadFile на WriteFile.
      В общем жду критики ).
      Сообщение отредактировано: XshStasX -
      Что бы делало твое добро, если бы не существовало зла?
      Воланд в "Мастере и Маргарите"
      1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
      0 пользователей:


      Рейтинг@Mail.ru
      [ Script Execution time: 0,0680 ]   [ 15 queries used ]   [ Generated: 24.11.17, 20:22 GMT ]