powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Поиск в файле с использованием MMF
25 сообщений из 28, страница 1 из 2
Поиск в файле с использованием MMF
    #39894417
Fktrc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Написал в целях повышения квалификации код поиска в файле с использованием MMF

Код: pascal
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.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
program MMapFileSearch;

{$APPTYPE CONSOLE}

uses
  Windows, SysUtils, AnsiStrings;

var
  IsDebuggerPresent: function: Boolean;

function MapFileRead(const Filename: String; var hFile, hMap: THandle ): Pointer;
begin
  hFile := FileOpen(Filename, fmOpenRead + fmShareDenyNone);
  Win32Check(hMap <> INVALID_HANDLE_VALUE);

  hMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
  Win32Check(hMap <> 0);

  Result := MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  Win32Check(Result <> nil);
end;

procedure UnmapFile( BasePtr: Pointer; hFile, hMap: THandle );
begin
  if BasePtr <> nil then
    UnmapViewOfFile( BasePtr );

  if hMap <> 0 then
    CloseHandle( hMap );

  if hFile <> INVALID_HANDLE_VALUE then
    CloseHandle( hFile );
end;

function StrPos(const Str1, Str2: PAnsiChar; const LenStr1: UInt64): PAnsiChar;
var
  MatchStart, LStr1, LStr2: PAnsiChar;
  l1, l2: UInt64;
begin
  Result := nil;
  if (Str1^ = #0) or (Str2^ = #0) then
    Exit;

  MatchStart := Str1;
  l2 := 0;
  while l2 < LenStr1 do
  begin
    if MatchStart^ = Str2^ then
    begin
      l1 := l2+1;
      LStr1 := MatchStart+1;
      LStr2 := Str2+1;
      while True do
      begin
        if LStr2^ = #0 then
          Exit(MatchStart);
        if (LStr1^ <> LStr2^) or (l1 >= LenStr1) then
          Break;
        Inc(LStr1); Inc(l1);
        Inc(LStr2);
      end;
    end;
    Inc(MatchStart); Inc(l2);
  end;
end;

function FileSize(AFileName: String): Int64;
var
  sr : TSearchRec;
begin
  if FindFirst(AFileName, faAnyFile, sr) = 0 then
     Result := sr.Size
  else
     Result := -1;
  FindClose(sr) ;
end;

var
  pFile, iStr, p2: PAnsiChar;
  hFile, hMap: THandle;
  time, szFile: Cardinal;
begin
  @IsDebuggerPresent := GetProcAddress(GetModuleHandle('kernel32.dll'), 'IsDebuggerPresent');

  Writeln('ParamStr(1): ', ParamStr(1));
  Writeln('ParamStr(2): ', ParamStr(2));

  szFile := FileSize(ParamStr(1));
  Writeln('File size: ', szFile);

  GetMem(p2, Length(ParamStr(2))+1);
  AnsiStrings.StrPCopy(p2, AnsiString(ParamStr(2)));

  time := GetTickCount;
  pFile := MapFileRead(ParamStr(1), hFile, hMap);
  iStr := StrPos(pFile, p2, szFile);
  Writeln('Elapsed time: ',GetTickCount-time);

  if iStr <> nil then
    Writeln('Found: ', iStr-pFile+1)
  else
    Writeln('Not found');

  FreeMem(p2, Length(ParamStr(2))+1);
  UnmapFile(pFile, hFile, hMap);

  if Assigned(IsDebuggerPresent) and IsDebuggerPresent then
  begin
    Writeln('Press enter');
    Readln;
  end;
end.



В целом работает, но есть небольшая проблема - сходу не нашел функцию, аналогичную StrPos, но с поиском по строке заранее известной длины. Пришлось выдернуть из SysUtils и подправить. Есть ли готовая такая функция?
Ну и вообще, все ли тут в порядке, есть ли что расширить и углубить?
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39894456
Фотография makhaon
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39894813
Fktrc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
makhaon,

спасибо, с ней процентов на 10-15 побыстрее стало:
Код: pascal
1.
2.
-  iStr := StrPos(pFile, p2, szFile);
+  iStr := AnsiStrings.SearchBuf(pFile, szFile, 0, 0, p2, [soDown, soMatchCase{, soWholeWord}]);
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895043
Фотография Кроик Семён
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
странно, StrPos написана на inline-ассемблере (по крайней мере, в Delphi 6)
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895049
Фотография Кроик Семён
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кроик Семён
странно, StrPos написана на inline-ассемблере (по крайней мере, в Delphi 6)
и тупо ищет, без всяких дополнительных опций типа игнора разницы заглавных/строчных букв
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895325
Fktrc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кроик Семён
странно, StrPos написана на inline-ассемблере (по крайней мере, в Delphi 6)

Fktrc
Пришлось выдернуть из SysUtils и подправить.
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895394
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Fktrc,

В файле, большем чем размер RAM искать будет?..
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895428
Василий 2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp
Fktrc,

В файле, большем чем размер RAM искать будет?..

Судя по декларации из доков, SearchBuf обломается даже на 2+ Гб (Integer)
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895514
Fktrc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp, эта версия нет
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895549
Fktrc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp,

А эта да.
Файл мапится поблочно, проверял на файле, превышающем размер озу в 2 раза. В процессе поиска память не росла. Искомое намеренно располагалось в конце файла, чтобы весь файл был прочитан.

Код: pascal
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.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
program MMapFileSearch;

{$APPTYPE CONSOLE}

uses
  Windows, SysUtils, StrUtils, AnsiStrings;

type
  TCUInt64 = packed record
      L, H: Cardinal;
    end;

var
  lenParam2, SizeBlock: Cardinal;
  IsDebuggerPresent: function: Boolean;

procedure OpenFileMapRead(const Filename: String; var hFile, hMap: THandle );
begin
  hFile := FileOpen(Filename, fmOpenRead + fmShareDenyNone);
  Win32Check(hFile <> INVALID_HANDLE_VALUE);

  hMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
  Win32Check(hMap <> 0);
end;

function MapFile(const hMap: THandle; nOffset: UInt64; szBlock: Cardinal ): Pointer;
var
  CUInt64: TCUInt64 absolute nOffset;
begin
  repeat
    Result := MapViewOfFile(hMap, FILE_MAP_READ, CUInt64.H, CUInt64.L, szBlock);

    if Result = nil then
      if szBlock > lenParam2 then
        szBlock := szBlock div 2
      else
        Break;
  until Result <> nil;
  Win32Check(Result <> nil);
end;

procedure UnmapFile( BasePtr: Pointer );
begin
  if BasePtr <> nil then
    UnmapViewOfFile( BasePtr );
end;

procedure CloseFile( const hFile, hMap: THandle );
begin
  if hMap <> 0 then
    CloseHandle( hMap );

  if hFile <> INVALID_HANDLE_VALUE then
    CloseHandle( hFile );
end;

function FileSize(AFileName: String): Int64;
var
  sr : TSearchRec;
begin
  if FindFirst(AFileName, faAnyFile, sr) = 0 then
     Result := sr.Size
  else
     Result := -1;
  FindClose(sr) ;
end;

var
  pFile, iStr, param2: PAnsiChar;
  hFile, hMap: THandle;
  time, szBlock, dwAllocationGranularity : Cardinal;
  szFile, fOffset: Int64;
  l: _SYSTEM_INFO;
begin
  @IsDebuggerPresent := GetProcAddress(GetModuleHandle('kernel32.dll'), 'IsDebuggerPresent');

  Writeln('ParamStr(1): ', ParamStr(1));
  Writeln('ParamStr(2): ', ParamStr(2));

  szFile := FileSize(ParamStr(1));
  Writeln('File size: ', szFile);

  lenParam2 := Length(ParamStr(2));

  // MapViewOfFile может мапить только по смещению, равному (N * _SYSTEM_INFO.dwAllocationGranularity), поэтому запомним эту цифру
  GetSystemInfo(l);
  dwAllocationGranularity := l.dwAllocationGranularity;

  // размер блока поиска
  //SizeBlock := 1024 * 1024 * 1024;
  //SizeBlock := 512 * 1024 * 1024;
  SizeBlock := 256 * 1024 * 1024;

  Assert((SizeBlock > lenParam2) and (SizeBlock < MaxInt));

  GetMem(param2, lenParam2+1);
  AnsiStrings.StrPCopy(param2, AnsiString(ParamStr(2)));

  iStr := nil;
  pFile := nil;
  fOffset := 0;
  szBlock := SizeBlock;

  time := GetTickCount;

  OpenFileMapRead(ParamStr(1), hFile, hMap);

  // мапим файл по блокам размером szBlock и выполняем поиск
  while True do
  begin
    if fOffset+szBlock > szFile then
      szBlock := szFile - fOffset
    else if fOffset > szFile then
      Break;

    pFile := MapFile(hMap, fOffset, szBlock);
    iStr := AnsiStrings.SearchBuf(pFile, szBlock, 0, 0, param2, [soDown, soMatchCase{, soWholeWord}]);
    UnmapFile(pFile);

    if (iStr <> nil) or (fOffset+szBlock = szFile) then
      Break;

    // на случай, если искомое находится на границе между блоками, отступаем назад на длину искомого
    // с учетом гранулярности смещения
    fOffset := dwAllocationGranularity * ((fOffset + szBlock - lenParam2) div dwAllocationGranularity);
  end;

  CloseFile(hFile, hMap);

  Writeln('Elapsed time: ',GetTickCount-time);

  if iStr <> nil then
    Writeln('Found offset: ', fOffset+(iStr-pFile+1), ' (', IntToHex(fOffset+(iStr-pFile+1), 10), ')')
  else
    Writeln('Not found');

  FreeMem(param2, lenParam2+1);

  if Assigned(IsDebuggerPresent) and IsDebuggerPresent then
  begin
    Writeln('Press enter');
    Readln;
  end;
end.

...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895559
Barmaley57
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Fktrc
В процессе поиска память не росла. Искомое намеренно располагалось в конце файла, чтобы весь файл был прочитан.
Если имеется ввиду рабочий набор, то он и не должен расти. Заполняться будет кэш ОС (блоками по 256kb вроде как).
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895567
Fktrc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Barmaley57
Если имеется ввиду рабочий набор, то он и не должен расти
Значит, все норм.
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39895618
Barmaley57
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Barmaley57
блоками по 256kb вроде как
Не, гоню. Это при обычном чтении через ReadFile c кэшированием. Там ОСь мапит блоками по 256kb. При mmf - просто подкачка страницами 4kb при ошибке доступа к страницам.
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39896548
RWolf
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Для поиска подстроки есть алгоритмы побыстрее простого посимвольного сопоставления.
https://ru.wikipedia.org/wiki/Поиск_подстроки
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39896552
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
RWolf
Для поиска подстроки есть алгоритмы побыстрее простого посимвольного сопоставления.
https://ru.wikipedia.org/wiki/Поиск_подстроки

В догонку: https://habr.com/ru/post/307220/
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39896691
Fktrc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Использован поиск подстроки по алгоритму Кнута - Морриса - Пратта. Программа может принимать третьим параметром число прогонов поиска по файлу, чтобы вычислить среднее время.
Код: pascal
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.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
program MMapFileSearch;

{$APPTYPE CONSOLE}

uses
  Windows, SysUtils, StrUtils, AnsiStrings;

type
  ACardinal = array of Cardinal;
  TCUInt64 = packed record
      L, H: Cardinal;
    end;

const
  // размер блока поиска
  //SizeBlock := 1024 * 1024 * 1024;
  //SizeBlock := 512 * 1024 * 1024;
  SizeBlock = 256 * 1024 * 1024;

var
  dwAllocationGranularity: Cardinal;
  IsDebuggerPresent: function: Boolean;

procedure OpenFileMapRead(const Filename: String; var hFile, hMap: THandle );
begin
  hFile := FileOpen(Filename, fmOpenRead + fmShareDenyNone);
  Win32Check(hFile <> INVALID_HANDLE_VALUE);

  hMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
  Win32Check(hMap <> 0);
end;

function MapFile(const hMap: THandle; nOffset: UInt64; szBlock: Cardinal ): Pointer;
var
  CUInt64: TCUInt64 absolute nOffset;
begin
  Result := MapViewOfFile(hMap, FILE_MAP_READ, CUInt64.H, CUInt64.L, szBlock);
  Win32Check(Result <> nil);
end;

procedure UnmapFile( BasePtr: Pointer );
begin
  if BasePtr <> nil then
    UnmapViewOfFile( BasePtr );
end;

procedure CloseFile( const hFile, hMap: THandle );
begin
  if hMap <> 0 then
    CloseHandle( hMap );

  if hFile <> INVALID_HANDLE_VALUE then
    CloseHandle( hFile );
end;

function FileSize(AFileName: String): Int64;
var
  sr : TSearchRec;
begin
  if FindFirst(AFileName, faAnyFile, sr) = 0 then
     Result := sr.Size
  else
     Result := -1;
  FindClose(sr) ;
end;

function StrPNew(const AStr: AnsiString; var ALen: Integer): PAnsiChar;
begin
  ALen := Length(AStr);
  GetMem(Result, ALen+1);
  AnsiStrings.StrPCopy(Result, AStr);
end;

// поиск подстроки по алгоритму Кнута - Морриса - Пратта
function SearchKMP(const s: PAnsiChar; const N: Integer; const p: PAnsiChar; const M: Integer): Integer;
var
  i, j: Integer;
  d: array of Integer;
begin
  SetLength(d, M);

  // Вычисление префикс-функции
  d[0] := 0;
  j := 0;
  for i := 1 to M-1 do
  begin
    while (j > 0) and (p[j] <> p[i]) do
      j := d[j-1];

    if p[j] = p[i] then
      Inc(j);

    d[i] := j;
  end;

    // Поиск
  j := 0;
  for i := 0 to N-1 do
  begin
    while (j > 0) and (p[j] <> s[i]) do
      j := d[j-1];

    if p[j] = s[i] then
      Inc(j);

    if j = M then
    begin
      SetLength(d, 0);
      Exit(i - j + 1);
    end;
  end;

  SetLength(d, 0);
  Exit(-1);
end;

function MMFSearch(const AFileName: String; AFileSize: Int64; const pSearch: PAnsiChar; lenSearch: Cardinal;
  var ATime: Cardinal): Int64;
var
  //iStr,
  pFile: PAnsiChar;
  hFile, hMap: THandle;
  szBlock: Cardinal;
  nPos: Integer;
  fOffset: Int64;
begin
  ATime := GetTickCount;

  nPos := -1;
  fOffset := 0;
  szBlock := SizeBlock;

  OpenFileMapRead(AFileName, hFile, hMap);

  // мапим файл по блокам размером szBlock и выполняем поиск
  while True do
  begin
    if fOffset+szBlock > AFileSize then
      szBlock := AFileSize - fOffset
    else if fOffset > AFileSize then
      Break;

    pFile := MapFile(hMap, fOffset, szBlock);
    //iStr := AnsiStrings.SearchBuf(pFile, szBlock, 0, 0, pSearch, [soDown, soMatchCase{, soWholeWord}]);
    //nPos := iStr-pFile;
    nPos := SearchKMP(pFile, szBlock, pSearch, lenSearch);
    UnmapFile(pFile);

    if (nPos >= 0) or (fOffset+szBlock = AFileSize) then
      Break;

    // на случай, если искомое находится на границе между блоками, отступаем назад на длину искомого
    // с учетом гранулярности смещения
    fOffset := dwAllocationGranularity * ((fOffset + szBlock - lenSearch) div dwAllocationGranularity);
  end;

  CloseFile(hFile, hMap);

  if nPos >= 0 then
    Result := fOffset + nPos
  else
    Result := -1;

  ATime := GetTickCount-ATime;
end;

var
  param2: PAnsiChar;
  AllTime, time, i, nCnt: Cardinal;
  lenSearch, nPos: Integer;
  szFile: Int64;
  l: _SYSTEM_INFO;
begin
  @IsDebuggerPresent := GetProcAddress(GetModuleHandle('kernel32.dll'), 'IsDebuggerPresent');

  Writeln('ParamStr(1): ', ParamStr(1));
  Writeln('ParamStr(2): ', ParamStr(2));

  szFile := FileSize(ParamStr(1));
  Writeln('File size: ', szFile);

  // MapViewOfFile может мапить только по смещению, равному (N * _SYSTEM_INFO.dwAllocationGranularity), поэтому запомним эту цифру
  GetSystemInfo(l);
  dwAllocationGranularity := l.dwAllocationGranularity;

  param2 := StrPNew(AnsiString(ParamStr(2)), lenSearch);

  Assert((SizeBlock > lenSearch) and (SizeBlock < Cardinal(MaxInt)));

  // 3-й параметр - число прогонов поиска, чтобы вычислить среднее
  nCnt := StrToIntDef(ParamStr(3), 1);
  if nCnt > 1 then
  begin
    Writeln(0,'/',nCnt,': время не учитывается');
    MMFSearch(ParamStr(1), szFile, param2, lenSearch, time);
  end;

  nPos := -1;
  AllTime := 0;
  if nCnt > 0 then
  begin
    for i := 1 to nCnt do
    begin
      if nCnt > 1 then
        Write(i,'/',nCnt);

      nPos := MMFSearch(ParamStr(1), szFile, param2, lenSearch, time);

      if nCnt > 1 then
        Writeln(': ', time, ' ms');
      AllTime := AllTime + time;
    end;

    Writeln('Elapsed ', IfThen(nCnt > 1, 'mean '), 'time: ', AllTime div nCnt, ' ms');

    if nPos >= 0 then
      Writeln('Found offset: ', nPos, ' (', IntToHex(nPos, 10), ')')
    else
      Writeln('Not found');
  end;

  FreeMem(param2, lenSearch+1);

  if Assigned(IsDebuggerPresent) and IsDebuggerPresent then
  begin
    Writeln('Press enter');
    Readln;
  end;
end.



Итого результаты
Код: plaintext
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.
>MMapFileSearch_SearchKMP.exe "File750MB.dat" "search" 10
ParamStr(1): File750MB.dat
ParamStr(2): search
File size: 785068650
0/10: время не учитывается
1/10: 7098 ms
2/10: 6739 ms
3/10: 7161 ms
4/10: 6677 ms
5/10: 6989 ms
6/10: 6692 ms
7/10: 6817 ms
8/10: 6771 ms
9/10: 6957 ms
10/10: 6911 ms
Elapsed mean time: 6881 ms
Found offset: 773626828 (002E1C9BCC)

>MMapFileSearch_SearchBuf.exe "File750MB.dat" "search" 10
ParamStr(1): File750MB.dat
ParamStr(2): search
File size: 785068650
0/10: время не учитывается
1/10: 7862 ms
2/10: 7676 ms
3/10: 7987 ms
4/10: 7675 ms
5/10: 7660 ms
6/10: 7956 ms
7/10: 7706 ms
8/10: 8065 ms
9/10: 7707 ms
10/10: 7816 ms
Elapsed mean time: 7811 ms
Found offset: 773626828 (002E1C9BCC)


6881/7811 = 0,88 - ускорение еще в ~1,13 раза...
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39896714
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Fktrc,

1. Массив префикс-функции я бы сделал глобальным, чтобы не дёргать всё время память туда-сюда.

2. "на случай, если искомое находится на границе между блоками, отступаем назад на длину искомого с учетом гранулярности смещения", - если у нас есть глобальная префикс-функция, то мы можем по её значению в этой точке точно узнать: может ли искомое находиться на границе между блоками и сколько символов из старого буфера необходимо сохранить (= её значению).

Но это уже красивости, вряд ли они дадут какой-либо заметный выигрыш в скорости.
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39896727
Sapersky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ну и до кучи здесь:
https://www.agner.org/optimize/asmlib.zip
есть ф-я A_strstr, которая быстрее стандартной Pos раз в 10, за счёт использования специальных инструкций из SSE4.
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39896758
Fktrc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Sapersky
Ну и до кучи здесь:
https://www.agner.org/optimize/asmlib.zip
есть ф-я A_strstr, которая быстрее стандартной Pos раз в 10, за счёт использования специальных инструкций из SSE4.
strstr32.asmThe strings must be zero-terminated.
Из-за этого поиск по бинарному файлу невозможен. Нужна версия, которая ищет по буферу заранее известного размера. А сам я переделывать ее, конечно же не буду)) да и не смогу.
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39896760
Fktrc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Fktrc
Использован поиск подстроки по алгоритму Кнута - Морриса - Пратта. Программа может принимать третьим параметром число прогонов поиска по файлу, чтобы вычислить среднее время.

патч, а то по запарке прошляпил:
Код: pascal
1.
2.
3.
4.
-  lenSearch, nPos: Integer;
-  szFile: Int64;
+  lenSearch: Integer;
+  szFile, nPos: Int64;
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39896830
Sapersky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Fktrc
Нужна версия, которая ищет по буферу заранее известного размера. А сам я переделывать ее, конечно же не буду)) да и не смогу.
Ну может я как-нибудь поковыряю, когда будет время. Так-то есть вариант SSE4-команды, которой можно задать длину (PcmpEstrM), но здесь
https://www.strchr.com/strcmp_and_strlen_using_sse_4.2
в комментариях пишут, что самый быстрый вариант - на SSE2 с использованием _mm_cmpeq_epi8 (команда pcmpeqb).

Вообще если искать в больших файлах, которые не в кэше, то упираться будет скорее в диск, даже если это SSD.
Я бы попробовал сделать асинхронное чтение очередной порции, то есть прочитали фрагмент, даём команду на чтение следующего и одновременно ищем в текущем. Не через MMF, а ReadFile с FILE_FLAG_OVERLAPPED. Хотя не знаю, получается ли там реальная асинхронность на практике, не пробовал.
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39897312
Bred eFeM
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Sapersky
Я бы попробовал сделать асинхронное чтение очередной порции, то есть прочитали фрагмент, даём команду на чтение следующего и одновременно ищем в текущем. Не через MMF, а ReadFile с FILE_FLAG_OVERLAPPED. Хотя не знаю, получается ли там реальная асинхронность на практике, не пробовал.
Получается, и если еще FILE_FLAG_NO_BUFFERING, то время поиска = время чтения. А вот к MMF асинхронность не прикрутишь ))
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39899971
Sapersky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
А как FILE_FLAG_NO_BUFFERING помогает? Я, кстати, не замечал от него эффекта, когда-то пробовал для тестирования, чтобы Винда не кэшировала файл - но заметной разницы в скорости не было, то есть видимо всё равно кэшировала.

Тем временем доделал поиск подстроки на SSE2. Быстрее для длинных строк/буферов, для коротких медленнее.
https://drive.google.com/open?id=1zVWYmnSq0Lki4_8Ju9E9jnYHHkhKqZvD

А Кнут - Моррис - Пратт втроём не смогли забороть одного Л.Толстого. Как я понял, смысл алгоритма в том, чтобы быстро обрабатывать частичные совпадения с подстрокой. Если их мало или нет, то и эффекта от алгоритма нет, получается даже медленнее стандартной Pos (наверное потому, что она на ассемблере).
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39900036
Фотография defecator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
Sapersky
А как FILE_FLAG_NO_BUFFERING помогает? Я, кстати, не замечал от него эффекта, когда-то пробовал для тестирования, чтобы Винда не кэшировала файл - но заметной разницы в скорости не было, то есть видимо всё равно кэшировала.

Тем временем доделал поиск подстроки на SSE2. Быстрее для длинных строк/буферов, для коротких медленнее.
https://drive.google.com/open?id=1zVWYmnSq0Lki4_8Ju9E9jnYHHkhKqZvD

А Кнут - Моррис - Пратт втроём не смогли забороть одного Л.Толстого. Как я понял, смысл алгоритма в том, чтобы быстро обрабатывать частичные совпадения с подстрокой. Если их мало или нет, то и эффекта от алгоритма нет, получается даже медленнее стандартной Pos (наверное потому, что она на ассемблере).


Если в начале FormShow поставить
SetPriority(True, True) ;

то результаты для Pos, Agner/SSE4, SSE2 - не изменятся вообще,
а вот для KMP улучшатся ровно в два раза
...
Рейтинг: 0 / 0
Поиск в файле с использованием MMF
    #39900038
s62
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Sapersky,
когда-то обсуждали на другом форуме скорость "простого" поиска и упрощенного БМХ (Бойер-Мур-Хорспул). Пришли к выводу, что выигрыш есть для относительно длинных строк, а для искомой подстроки длиной до 6 символов разницы практически нет.
Оценки сложности алгоритмов есть вот тут https://ru.wikipedia.org/wiki/Поиск_подстроки
...
Рейтинг: 0 / 0
25 сообщений из 28, страница 1 из 2
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Поиск в файле с использованием MMF
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]