Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Поиск в файле с использованием MMF / 25 сообщений из 28, страница 1 из 2
26.11.2019, 12:51
    #39894417
Fktrc
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
Написал в целях повышения квалификации код поиска в файле с использованием 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
26.11.2019, 13:27
    #39894456
makhaon
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
...
Рейтинг: 0 / 0
27.11.2019, 06:34
    #39894813
Fktrc
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
makhaon,

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

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

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

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

Судя по декларации из доков, SearchBuf обломается даже на 2+ Гб (Integer)
...
Рейтинг: 0 / 0
28.11.2019, 12:43
    #39895514
Fktrc
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
alekcvp, эта версия нет
...
Рейтинг: 0 / 0
28.11.2019, 13:16
    #39895549
Fktrc
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
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
28.11.2019, 13:21
    #39895559
Barmaley57
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
Fktrc
В процессе поиска память не росла. Искомое намеренно располагалось в конце файла, чтобы весь файл был прочитан.
Если имеется ввиду рабочий набор, то он и не должен расти. Заполняться будет кэш ОС (блоками по 256kb вроде как).
...
Рейтинг: 0 / 0
28.11.2019, 13:29
    #39895567
Fktrc
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
Barmaley57
Если имеется ввиду рабочий набор, то он и не должен расти
Значит, все норм.
...
Рейтинг: 0 / 0
28.11.2019, 14:05
    #39895618
Barmaley57
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
Barmaley57
блоками по 256kb вроде как
Не, гоню. Это при обычном чтении через ReadFile c кэшированием. Там ОСь мапит блоками по 256kb. При mmf - просто подкачка страницами 4kb при ошибке доступа к страницам.
...
Рейтинг: 0 / 0
29.11.2019, 17:39
    #39896548
RWolf
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
Для поиска подстроки есть алгоритмы побыстрее простого посимвольного сопоставления.
https://ru.wikipedia.org/wiki/Поиск_подстроки
...
Рейтинг: 0 / 0
29.11.2019, 17:49
    #39896552
alekcvp
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
RWolf
Для поиска подстроки есть алгоритмы побыстрее простого посимвольного сопоставления.
https://ru.wikipedia.org/wiki/Поиск_подстроки

В догонку: https://habr.com/ru/post/307220/
...
Рейтинг: 0 / 0
30.11.2019, 13:23
    #39896691
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.
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
30.11.2019, 18:30
    #39896714
alekcvp
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
Fktrc,

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

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

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

патч, а то по запарке прошляпил:
Код: pascal
1.
2.
3.
4.
-  lenSearch, nPos: Integer;
-  szFile: Int64;
+  lenSearch: Integer;
+  szFile, nPos: Int64;
...
Рейтинг: 0 / 0
01.12.2019, 18:27
    #39896830
Sapersky
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
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
02.12.2019, 21:12
    #39897312
Bred eFeM
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
Sapersky
Я бы попробовал сделать асинхронное чтение очередной порции, то есть прочитали фрагмент, даём команду на чтение следующего и одновременно ищем в текущем. Не через MMF, а ReadFile с FILE_FLAG_OVERLAPPED. Хотя не знаю, получается ли там реальная асинхронность на практике, не пробовал.
Получается, и если еще FILE_FLAG_NO_BUFFERING, то время поиска = время чтения. А вот к MMF асинхронность не прикрутишь ))
...
Рейтинг: 0 / 0
09.12.2019, 13:57
    #39899971
Sapersky
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
А как FILE_FLAG_NO_BUFFERING помогает? Я, кстати, не замечал от него эффекта, когда-то пробовал для тестирования, чтобы Винда не кэшировала файл - но заметной разницы в скорости не было, то есть видимо всё равно кэшировала.

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

А Кнут - Моррис - Пратт втроём не смогли забороть одного Л.Толстого. Как я понял, смысл алгоритма в том, чтобы быстро обрабатывать частичные совпадения с подстрокой. Если их мало или нет, то и эффекта от алгоритма нет, получается даже медленнее стандартной Pos (наверное потому, что она на ассемблере).
...
Рейтинг: 0 / 0
09.12.2019, 15:08
    #39900036
defecator
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
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
09.12.2019, 15:11
    #39900038
s62
s62
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск в файле с использованием MMF
Sapersky,
когда-то обсуждали на другом форуме скорость "простого" поиска и упрощенного БМХ (Бойер-Мур-Хорспул). Пришли к выводу, что выигрыш есть для относительно длинных строк, а для искомой подстроки длиной до 6 символов разницы практически нет.
Оценки сложности алгоритмов есть вот тут https://ru.wikipedia.org/wiki/Поиск_подстроки
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Поиск в файле с использованием MMF / 25 сообщений из 28, страница 1 из 2
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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