Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2004.10.24;
Скачать: [xml.tar.bz2];

Вниз

Поиск в бинарном файле   Найти похожие ветки 

 
Dimaxx   (2004-09-24 23:32) [0]

Сабж. Нужен алгоритм действительно быстрого поиска последовательности байт любой длины в файлах любого размера (до гигабайта). Читать кусками по-деревенски не катит. Раньше у меня был алгоритм, но с падением Фуджика все пропало.


 
Defunct ©   (2004-09-25 01:33) [1]

S - строка поиска
I - текущий номер символа в строку поиска.

1. I := 1;
1. Создать буфер 1Mb или больше как вам нравится
2. Считать очередную порцию данных из файла.
3. Пока не вышли за границы буфера : Бежать по буферу в поисках S[1]
  иначе - GOTO 7
4. Если нашли символ S[1] в буфере: тогда
  For I:=2 to Length(S) do <сравниваем отсальные символы>
5. Совпадение - выход с результатом True
6. GOTO 3
7. Файл не закончился - GOTO 2
8. Выход с результатом False


 
Defunct ©   (2004-09-25 01:36) [2]

[1] Алгоритм конечно же нуждается в поправках, но общий смысл такой.


 
GuAV ©   (2004-09-25 01:37) [3]


> 1. Создать буфер 1Mb или больше как вам нравится

Сказали же

> Читать кусками по-деревенски не катит.

Хотя как без этого... ну низнаю :(


 
Defunct ©   (2004-09-25 01:47) [4]

А где же там по-деревенски?

По-деревенски это будет примерно так:

S - строка поиска
S2 - буфер
Pos - позиция в файле

1. Pos := 0;
2. Seek(F, Pos)
3. считать в S2 Length(S) символов
4. Pos := Pos + 1;
5. S2=S - выход с результатом True
6. Pos + Length(S) < FileSize(F) - да GOTO 2
7. выход с результатом False


 
Германн ©   (2004-09-25 02:08) [5]

А кто такой "Фуджик" и куда он упал?


 
Defunct ©   (2004-09-25 02:39) [6]

Германн ©   (25.09.04 02:08) [5]

винт такой, они сейчас почти все сыпятся.


 
Anatoly Podgoretsky ©   (2004-09-25 11:18) [7]

GuAV ©   (25.09.04 01:37) [3]
А раз не катит, то путей немного - все в память, можно через MMF


 
TUser ©   (2004-09-25 15:06) [8]

См. например, алгоритм Кнута-Мориса-Пратта. Серьезные люди его придумали ...


 
Dimaxx   (2004-09-25 21:52) [9]

2 Defunct: Подобное делал. Туфта, ибо кусок в 1 метр файла просматривается около 2-5 секунд, а это очень много. А если мне нужно искать в одном файле несколько разных образцов? Тогда ваще тормоз получается. TotalCommander просматривает за это время 30-40 метров.

А хде посмотреть про алгоритм Кнута-Мориса-Пратта?


 
Defunct ©   (2004-09-25 22:18) [10]

Dimaxx   (25.09.04 21:52) [9]
Значит криво сделал.

Сейчас потрачу полчаса, и докажу обратное. Не может там тратиться 2-5 секунд.


 
GuAV ©   (2004-09-25 22:34) [11]


> Dimaxx   (25.09.04 21:52) [9]

Надо через SCASB/SCASW/SCASD искать первые 1/2/4
а потом CompareMem.


 
default ©   (2004-09-25 22:41) [12]

конечно надо искать более серьёзные алгоритмы чем тупой перебор в данном случае...


 
GuAV ©   (2004-09-25 22:44) [13]


> конечно надо искать более серьёзные алгоритмы чем тупой
> перебор в данном случае...

Может я и ошибаюсь, но имхо тупой перебор - самое быстрое.
При этом буфер большой, и за один такт перебора обрабатывать данные размером в машинное слово, т.е. 4 байта.


 
default ©   (2004-09-25 22:45) [14]

http://www.3ka.mipt.ru/vlib/books/Programming/ComputerScience/StryngAnalysis/
вот тут недавно эту ссылку кидали


 
Defunct ©   (2004-09-26 00:04) [15]

Держите, в 100 мегабайтном файле находит строку менее чем за 1 сек., возможно нужны поправки. Будут вопросы - задавайте.

Type TBuffer = Array[0..1000000-1] of byte;
    PBuffer = ^TBuffer;

Function CharPos(AChar:Char; Buffer:Pointer; Size:Integer):Integer;Assembler;
Asm
 Cld
 Push Es
 Push EDi
 Push Ds
 Pop  Es
 Mov  Al , AChar
 Mov  EDi, Buffer
 Mov  ECx, Size
 Push ECx
 Repne ScasB
 Pop  EAx
 Jnz  @@NotMatch
 Sub  EAx, ECx
 Mov  Result, EAx  // This is odd when optimization is on
 Jmp  @@Exit
@@NotMatch:
 Mov  Result, -1
@@Exit:
 Pop  EDi
 Pop  Es
End;

Function CompareStrings(Constant:String; Buffer:PBuffer):Boolean;
Var I:Integer;
Begin
 Result := True;
 I := 1;
 While Result and (I<Length(Constant)) Do
 Begin
   Inc(I);
   Result := Result and (Constant[i] = Chr(Buffer^[i-2]));
 End;
End;

Function LoadBuffer(var Buffer; var F:File; var SeekPos:Int64):Integer;
Begin
 Seek(F, SeekPos);
 BlockRead(F, Buffer, 1000000, Result);
 SeekPos := FilePos(F);
End;

Function ConstantExists(FileName, Constant: String):Boolean;
Var Buffer  : PBuffer;
   SeekPos : Int64;
   F       : File;
   BufSize : Integer;
   BufPos  : Integer;
   SearchResult : Integer;
Begin
 Result := False;

 If FileExists(FileName) Then
 Begin
   SeekPos := 0;
   New(Buffer);
   Assign(F, FileName);
   Reset(F,1);

   Repeat
     BufSize := LoadBuffer(Buffer^, F, SeekPos);
     BufPos  := 0;
     While (Not Result) and (BufPos>=0) Do
     Begin
       SearchResult := CharPos( Constant[1], @Buffer^[BufPos], BufSize-BufPos );
       If SearchResult>0 Then BufPos := BufPos + SearchResult
                         Else BufPos := -1;
       If SearchResult<>-1 Then
       Begin
        Result := CompareStrings(Constant, @Buffer^[BufPos]);
        BufPos := BufPos + 1;
       End;

       If BufPos>=BufSize Then BufPos := -1;
     End;
   Until (BufSize=0) Or Result;

   Close(F);
 End;
End;

procedure TForm1.Button3Click(Sender: TObject);
begin
 If ConstantExists(Edit1.Text, Edit2.Text) Then
    ShowMessage("Found")
 Else
    ShowMessage("Not Found");
end;


 
Defunct ©   (2004-09-26 00:12) [16]

[15]

Оптимизпцией не занимался и не учитывал выход строки за границу буфера. Оставил эти задачи для вас.


 
Defunct ©   (2004-09-26 00:26) [17]

> конечно надо искать более серьёзные алгоритмы чем тупой перебор в данном случае...

Это какие такие более серьезные алгоритмы позволят все просмотреть без пересмотра всего файла?

default ©   (25.09.04 22:45) [14]

Куча теории, мало практики. Ссылка ерундовая.
сходите лучше сюда:

www.intel.com
строка поиска:
Pentium Processor Family
Developers manual


 
default ©   (2004-09-26 00:51) [18]

Defunct ©   (26.09.04 00:26) [17]
не понял "наезда"?
почему ерундовая ссылка?
вообще за что купил за то продал(я её тут взял)
"Это какие такие более серьезные алгоритмы позволят все просмотреть без пересмотра всего файла?"
не понял, вы хотите сказать что тот же к примеру алгоритм Бойера-Мура медленней обычного перебора Вами описанного в [1]?


 
Defunct ©   (2004-09-26 01:01) [19]

> не понял "наезда"?

"наезд" был на ссылку, а не на вас.

> почему ерундовая ссылка?
потому что незная возможностей аппаратуры тобиш без практики, любая теория превращается в "пук".

> вообще за что купил за то продал(я её тут взял)

не спорю.

> не понял, вы хотите сказать что тот же к примеру алгоритм Бойера-Мура медленней обычного перебора Вами описанного в

Естественно, алгоритм [1] будет быстрее работать чем алгоритм Бойра-Мура, потому что алгоритм [1] опирается на особенности архитектуры Intel x86 процессора, а господа Бойер и Мур сей особенности не знали и предложили сдвигать всю строку и искать с конца. таким образом в алгоритме Бойера-Мура нелья воспользоваться линейной строковой функцией Repne Scas.


 
default ©   (2004-09-26 01:17) [20]

[19]
и всё-таки я сомневаюсь что Ваш вариант самый быстрый
скорее уверен что не самый


 
GuAV ©   (2004-09-26 01:26) [21]


>  Push Ds
>  Pop  Es

что то мне не нравится что сег регистры задействованы.

и ваще код не нравится... хотя алгоритм может быть и быстрый.


 
Defunct ©   (2004-09-26 02:09) [22]

GuAV ©   (26.09.04 01:26) [21]
> что то мне не нравится что сег регистры задействованы.

см. документацию по команде STOS, работает с регистровой парой ES:[EDI]. на входе в функцию ES может отличаться от DS для этого приняты меры предосторожности.

> и ваще код не нравится...
хозяин-барин - напишите лучше.

default ©   (26.09.04 01:17) [20]
> и всё-таки я сомневаюсь что Ваш вариант самый быстрый
реализацию можно сделать быстрее почти на 30-40% если оперировать DWORD. Алгоритма более быстрого вы не найдете.

> скорее уверен что не самый
странно, что у вас доверие к алгоритмам 60-х годов выше чем к алгоритмам 2004 года.


 
GuAV ©   (2004-09-26 02:16) [23]


> > что то мне не нравится что сег регистры задействованы.
> см. документацию по команде STOS, работает с регистровой
> парой ES:[EDI]. на входе в функцию ES может отличаться от
> DS для этого приняты меры предосторожности.

да я знаю, а Вы смотрите доку по BASM
Вы не должны никогда изменять содержимое сегментных регистров  (ds, es и ss указывают на один и тот же сегмент; cs имеет свое собственное значение; fs используется Windows и gs резервирован).

> > и ваще код не нравится...
> хозяин-барин - напишите лучше.


>  While Result and (I<Length(Constant)) Do
>  Begin
>    Inc(I);
>    Result := Result and (Constant[i] = Chr(Buffer^[i-2]));
>  End;

например здесь "Result and" - лишнее а Constant[i] содержит лишний UniqueString.
и потом я про

> быстрее почти на 30-40% если оперировать DWORD


 
Defunct ©   (2004-09-26 02:52) [24]

GuAV ©   (26.09.04 02:16) [23]
> да я знаю

сомнительно

> а Вы смотрите доку по BASM

Я сам такие доки пишу, опираясь на документацию к процессорам.

> например здесь "Result and" - лишнее а Constant[i] содержит лишний UniqueString.

лишний что?

Да.. на фоне всего кода это очень серъезное замечание. см. [16]. Не нравится? напишете лучше, за то же время?
Жаль только у вас будет пример перед глазами, какой-никакой, а все же рабочий.

> и потом я про
>> быстрее почти на 30-40% если оперировать DWORD

Скажите вы меня считаете полным васей или наполовину? С какой радости я должен забесплатно писать или делиться оптимальным кодом поиска?


 
GuAV ©   (2004-09-26 03:00) [25]


> сомнительно

зря.
в BP я так и писал.

> Я сам такие доки пишу, опираясь на документацию к процессорам.

однако нужно ещё и учитывать особенности ОС и компилятора.
> Да.. на фоне всего кода это очень серъезное замечание.

первое подвернувшееся.
> С какой радости я должен забесплатно писать или делиться
> оптимальным кодом поиска?

правильно, не стоило ваще этот код выкладывать.
свой я уже писал, но он сейчас не доступен а то я бы выложил.

> Жаль только у вас будет пример перед глазами, какой-никакой,
> а все же рабочий.

в случае реализации этого алгоритма я предпочёл бы писать сам а не видеть этот пример, потому что алгоритм достаточно простой, а код достаточно плохой.


 
GuAV ©   (2004-09-26 03:03) [26]


> Mov  Al , AChar


>  Mov  ECx, Size

это уже говорит о недостатке опыта в BASM


 
GuAV ©   (2004-09-26 03:07) [27]


>  Mov  Result, EAx  // This is odd when optimization is on

и это тоже.
этот код не был изначально для 32bit delphi и был тупо скопирован.

>  BlockRead(F, Buffer, 1000000, Result);

лучше подобрать кратное степени 2
> Function CompareStrings(Constant:String; Buffer:PBuffer):Boolean;
> Var I:Integer;
> Begin
>  Result := True;
>  I := 1;
>  While Result and (I<Length(Constant)) Do
>  Begin
>    Inc(I);
>    Result := Result and (Constant[i] = Chr(Buffer^[i-2]));
>  End;
> End;

это вообще нужно было на BASM.


 
GuAV ©   (2004-09-26 03:10) [28]


>  Cld

и это лишнее. эот флаг сохраняется неустановленным.
посмотри SysUtils для примеров.


 
Defunct ©   (2004-09-26 03:18) [29]

> однако нужно ещё и учитывать особенности ОС и компилятора.

учтены особенности как ОС так и компилятора, причем для обоих опций компилятора с оптимизацией и без. Найдете?

> свой я уже писал, но он сейчас не доступен а то я бы выложил.

голословное утверждение.

> в случае реализации этого алгоритма я предпочёл бы писать сам а не видеть этот пример, потому что алгоритм достаточно простой, а код достаточно плохой.

Учитывая что и алгоритм[1], и код[15] были предложены мной, записано на личный счет в качестве необоснованного оскорбления.


 
Defunct ©   (2004-09-26 03:34) [30]

>  Mov  Result, EAx  // This is odd when optimization is on

>и это тоже.
>этот код не был изначально для 32bit delphi и был тупо скопирован.


i ignore this allegation due to your age dear friend. i"ve wrote the comment in english because the simple fact is well known, copy-paste doesnt work between Delphi editor and the editor here.

>>  BlockRead(F, Buffer, 1000000, Result);
> лучше подобрать кратное степени 2

абсолютно без разницы, ОС при следующем обращении к файлу вытянет данные из кеша. На скорость никак не влияет.

> GuAV ©   (26.09.04 03:10) [28]

Это сугубо ваше мнение, флаг может измениться, а может и не измениться, зависит от ветра в поле. Обычно, никто не следит за содержимым флага D поэтому нелишней предосторожностью будет его сброс.

>> Function CompareStrings(Constant:String; Buffer:PBuffer):Boolean;
> это вообще нужно было на BASM.
Это на чем захотел на том и сделал, на скорость эта процедура практически не влияет. А автору вопроса думаю будет понятней на Delphi.

> это уже говорит о недостатке опыта в BASM
фраза вызывает улыбку


 
Defunct ©   (2004-09-26 03:49) [31]

2 GuAV, когда разберетесь в коде приведенном ниже, и скажете где он может использоваться, тогда будем с вами говорить об опыте. А так кроме, голословных утверждений и оскорблений я от вас пока ничего не услышал.

Start:

Db 0B8H
SubEnable:
Db 2dH
Db 3eH

Db 0EBH
Db 0FCH
Ret


 
Defunct ©   (2004-09-26 06:09) [32]

Я проанализировал варинт работы с DWORD, ничего он не ускорит, наоборот замедлит поиск.

Спросите почему?

ответ прост, нам придется сдвигаться на 1 байт постоянно, так как строка может начинаться не обязательно точно на границе DWORD. строка может быть смещена на 1,2 или 3 байта от начала границы DWORD. следовательно придется отказаться от быстрой функции REPNE SCAS и код будет примерно таким:

@@Instead_RepneStos:
SHL   EAx, 8
LODSB
CMP   EAx, EDx ; EDx - DWORD constant
Jnz   @@Instead_RepneScas


Что мы выиграем?
1. Отпадает надобность проверки первых четырех символов в функции CompareString, для маленьких строк возможно ускорение на 2-3%.

Что мы проигрываем?
1. Нельзя найти строки состоящие из 3-х и менее символов.
2. для больших строк - замедление на 15-25% т.к. одна циклическая команда заменена 3-мя! (в основном цикле поиска), причем одна из команд - команда перехода (медленная по определению, т.к. вызывает сброс конвеера).
3. Функция CharPos увеличится, т.к. придется рассмотреть частный случай начальной загрузки первого DWORD"а, что приведет к заметному замедлению если в файле встречается много одинаковых подстрок, равных первому DWORD искомой строки.

А теперь взвесим все "ЗА" и "ПРОТИВ" и придем к логическому выводу: [1] - один из самых быстрых алгоритмов поиска для Intel x86 (если не самый быстрый). Код [15] требует оптимизации, но думаю выбрасывать байты из работающей программы, проще чем ковыряться с неработающей.

Так что - наздоровье Dimaxx, пользуйтесь


 
GuAV ©   (2004-09-26 13:26) [33]


> // This is odd when optimization is on

Это нафиг не нужно ни при оптимизации ни без неё

> флаг может измениться, а может и не измениться, зависит
> от ветра в поле.

подпрограммы на Дельфи должны следить за тем что флаг не установовлен. т.е. ставишь - будб добр збрасывай. см. SysUtils - там видно моного процедур которые расссчитывают на то что он не установлен.

> 1. Нельзя найти строки состоящие из 3-х и менее символов.

для них оставить SCASB



> строка может быть смещена на 1,2 или 3 байта от начала границы
> DWORD. следовательно придется отказаться от быстрой функции
> REPNE SCAS

перебор вариантов можно оставить в CompareStrings

> Это на чем захотел на том и сделал, на скорость эта процедура
> практически не влияет. А автору вопроса думаю будет понятней
> на Delphi.

в случае достаточно длинной строки это может выбросить все прелести asm кода.


> для обоих опций компилятора с оптимизацией и без. Найдете?

вы про
Mov  Al , AChar

для asm подпрограмм оптимизация не влияет, параметры всё равно через регистры.


 
Defunct ©   (2004-09-26 14:43) [34]

GuAV ©   (26.09.04 13:26) [33]

вначале [31]


 
GuAV ©   (2004-09-26 14:49) [35]


> вначале [31]

сначала это
MOV ax, 03E2Dh
JMP @SubEnable,

потом
sub ax, 0EB3Eh
cld

я не сомневаюсь что Вы знаете asm, но с BASM у Вас похоже трабл


 
Defunct ©   (2004-09-26 15:19) [36]

PS: Хотите оптимальную процедуру CharPos - пожалуйста:
но только c оговорками
{этот код работает только при компиляции на D7 с включенной оптимизацией}

Function CharPos(AChar:Char; Buffer:Pointer; Size:Integer):Integer;Assembler;
Asm
Xchg EDx, EDi
Push ECx
Repne ScasB
Pop  EAx
Jnz  @@NotMatch
Sub  EAx, ECx
Xchg EDx, Edi
Ret
@@NotMatch:
Xor  EAx, EAx
Dec  EAx
Xchg EDx, Edi
End;


 
Defunct ©   (2004-09-26 15:23) [37]

GuAV ©   (26.09.04 14:49) [35]

1. Ответ неверный.
2. Не раскрыта область применения кода.


 
GuAV ©   (2004-09-26 16:59) [38]

>{этот код работает только при компиляции на D7 с включенной оптимизацией}
Для тех кто на бронепоезде, повторяю
для asm подпрограмм оптимизация не влияет, параметры всё равно используется модель register и параметры передаются через регистры.

однако это уже выглядит лучше, хотя не проверяю.

Defunct ©   (26.09.04 15:23) [37] Вероятно Вы имеете ввиду, что вход может быть и сразу в SubEnable?
И код применим как элемент "защиты", где смысл в том что одни и те же бинарные даммые соответствуют разным инструкциям, что может вызвать затруднения при дизасеблировании?

я не разбираюсь в этом, может это и зачем-то надо.

Но по части обычного использованияассемблера в Дельфи
> с BASM у Вас похоже трабл


 
GuAV ©   (2004-09-26 17:06) [39]


> всё равно используется модель register и параметры передаются
> через регистры.

по умолчанию, разумеется, можно явно написать pascal


 
Defunct ©   (2004-09-26 17:55) [40]

Const MaxBufferSize = 1000000;

Type TBuffer = Array[0..MaxBufferSize-1] of byte;
    PBuffer = ^TBuffer;

Function CharPos(AChar:Char; Buffer:Pointer; Size:Integer):Integer;Assembler;
Asm
Xchg  EDx, EDi
Push  ECx
Repne ScasB
Pop   EAx
Jnz   @@NotMatch
Sub   EAx, ECx
Xchg  EDx, Edi
Ret
@@NotMatch:
Xor   EAx, EAx
Dec   EAx
Xchg  EDx, Edi
End;

Function CompareStrings(Constant, Buffer:Pointer; BufSize:Integer):Boolean;Assembler;
Asm
 Push EDi
 Push ESi
 Xchg EAx, ESi
 Mov  EDi, EDx
 Xchg EDx, EBx
 Mov  EBx, [ESi-4]
 Dec  EBx

@@Scan:
 Mov  Al, [ESi]
 Xchg EDx, EDi
 Push ECx
 Call CharPos
 Pop  ECx
 Xchg EDx, EDi
 Sub  ECx,EAx
 Cmp  ECx,EBx
 Jb   @@Exit_False

 Cmp  EAx, 0FFFFFFFFh
 Jnz  @@CompareNextChars
@@Exit_False:
 Pop  ESi
 Pop  EDi
 Xchg EDx, Ebx
 Xor  EAx,EAx
 Ret

@@CompareNextChars:
 Push ECx
 Mov  ECx,EBx
 Push ESi
 Inc  ESi
 Push EDi    //Здесь можно предусмотреть пропуск просмотренных символов
 Repe CmpsB
 Pop  EDi    //будет работать немножко быстрее
 Pop  ESi
 Pop  ECx
 Jnz  @@Scan

 Pop  ESi
 Pop  EDi
 Xchg EDx, Ebx
 Xor  EAx,EAx
 Dec  EAx
End;

Function LoadBuffer(var Buffer; var F:File; var SeekPos:Int64):Integer;
Begin
 Seek(F, SeekPos);
 BlockRead(F, Buffer, MaxBufferSize, Result);
 SeekPos := FilePos(F);
End;

Function ConstantExists(FileName, Constant: String):Boolean;
Var Buffer  : PBuffer;
   SeekPos : Int64;
   F       : File;
   BufSize : Integer;
Begin
 Result := False;

 If FileExists(FileName) Then
 Begin
   SeekPos := 0;
   New(Buffer);
   Assign(F, FileName);
   Reset(F,1);

   Repeat
     BufSize := LoadBuffer(Buffer^, F, SeekPos);
     Result := CompareStrings(@Constant[1], Buffer, BufSize);
     SeekPos := SeekPos - Length(Constant);
   Until Result Or (BufSize<MaxBufferSize);

   Close(F);
   Dispose(Buffer);
 End;
End;


Вот до чего мне удалось оптимизировать код[15], здесь учтен выход строки за границу буфера.

GuAV ©   (26.09.04 16:59) [38]
Вместо "наездов" на код, за то же время могли бы его оптимизировать сами, или написать свой.

>И код применим как элемент "защиты", где смысл в том что одни и те же бинарные даммые соответствуют разным инструкциям, что может вызвать затруднения при дизасеблировании?

почти правильно, за исключением того, что у этого кода есть строго определенное назначение (попросту его больше не для чего применять кроме как ....). Я хотел от вас услышать именно это назначение.


 
GuAV ©   (2004-09-26 18:54) [41]

лучше.

я ещё могу немного оптимизировать, самую малость...

CompareStrings(const Constant: string; Buffer ...и при вызове соотв
CompareStrings(Constant, ...
это уберет немного лишнего кода в месте вызова.

Const MaxBufferSize = 1048576 // привязывайсь к физ размеру кластера и страницы памяти...

да и не надо FileExists(FileName), всё равно он может существовать и не открыться.

потом так ещё не мешало бы
  New(Buffer);
  try
    Assign(F, FileName);
    Reset(F,1);
    try
     
    finally
      Close(F);
  finally
      Dispose(Buffer);
  end;

так оно корректнее а на произв-ть не влияет, т.к. только один раз делается.
> Вместо "наездов" на код, за то же время могли бы его оптимизировать
> сами, или написать свой.

понимаете счас у меня нет дельфи...


> почти правильно, за исключением того, что у этого кода есть
> строго определенное назначение (попросту его больше не для
> чего применять кроме как ....). Я хотел от вас услышать
> именно это назначение.

ну не знаю. сказал же, что в Вашей области не разбираюсь...
этот код в прочем не относится к сабжу.
в яндексе искать в падлу, обзаву его "ДизАсмНаёмка" :)


 
GuAV ©   (2004-09-26 18:58) [42]

 finally
     Close(F);
  end;
 finally


 
Defunct ©   (2004-09-26 19:18) [43]

GuAV ©   (26.09.04 18:54) [41, 42]

Поправки принимаются все кроме:

> Const MaxBufferSize = 1048576 // привязывайсь к физ размеру кластера и страницы памяти...

Лучше уже привязываться к объему L2 кеша процессора, а если нет тогда все равно кратно двум или нет. К кластеру привязываться смысла нет - в ОС есть механизм упреждающего чтения.

> да и не надо FileExists(FileName), всё равно он может существовать и не открыться.

Файл может и не существовать, проверка будет нелишней.


 
GuAV ©   (2004-09-26 19:52) [44]


> Лучше уже привязываться к объему L2 кеша процессора, а если
> нет тогда все равно кратно двум или нет. К кластеру привязываться
> смысла нет - в ОС есть механизм упреждающего чтения.

ок принято.

> Файл может и не существовать, проверка будет нелишней.

Если процедура написана по правильному, т.е. чтение в блоке try то в ситуации когда файл не существует, исключение подумется при первом чтении.

Возможна ситуация когда файл существует но почему-то не читается.

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

по части оскорблений - мне не понравился код из за использование сег. регистров, также бросилось в глаза Mov  Result, EAx. N.к. я знаю что это всё ни нужно, я усомнился в качесте кода, Вы сослались на доку [22], я тоже [23], после чего в [24] - первое оскорбление в мой адрес (необоснованный "наезд"). в [26] же я усомнившись в Вашем опыте привел свой аргумент который Вы не опровергли.
ну то есть что я хочу сказать: требуй вежливости - будь вежлив сам.


 
GuAV ©   (2004-09-26 19:59) [45]


> Если процедура написана по правильному, т.е. чтение в блоке
> try то в ситуации когда файл не существует, исключение подумется
> при первом чтении.

разумеется, оно подымется в любом случае.
просто оно будет правильно обработано,
> Если процедура написана по правильному
.

И потом, подпрограмма не должна оставлять факт остсутсвия файла без внимания, она должна возбудить исключение по этому делу.


 
Defunct ©   (2004-09-26 20:12) [46]

> Если процедура написана по правильному, т.е. чтение в блоке try то в ситуации когда файл не существует, исключение подумется при первом чтении.

Кто-то, конечно же это были не вы, а ваш двойник, в смежной ветке с заголовком New() сказал золотую фразу:

GuAV ©   (26.09.04 02:01) [50]
> т.е. способ try..except - пример неправильного написания программы.

Вы сами себе противоречите.

PS: пора заканчивать, уже над нами наверно весь форум смеется "hot china guys", ICQ у меня указан в анкете.


 
Sergey_Masloff   (2004-09-26 20:18) [47]

>уже над нами наверно весь форум смеется
Если вы когда меряетесь еще и код будете продолжать приводить то лучше здесь продолжайте. Неожиданно интересная ветка оказалась...
В любом случае спасибо. Интерес конечно чисто академический но читать (не ругань а код и комментарии) мне например было очень интересно. Так что в любом случае спасибо ;-)


 
GuAV ©   (2004-09-26 21:38) [48]


> Неожиданно интересная ветка оказалась...


поэтому продолжаю здесь

Defunct ©   (26.09.04 20:12) [46]

Я имел ввиду там способ проверки валидности указателя через try... except, который неправильный. тут я предлагаю использовать конструкцию try..finally и хранить исключения для вызавашего кода. Это вещи разные, имхо даже противоположные. Вызывающая подпрограмма не должна также использовать try..except в том виде в каком Вы предложили её в той ветке.


>  (не ругань а код и комментарии) мне например было очень
> интересно.

:) мне тоже особенно
Mov  Result, EAx  // This is odd when optimization is on
т.к. любая функция Delphi, возвращающая Integer возвращает его в EAX. (кроме D1, где в AX)
<off>имхо неверный коментарий более грубая ошибка чем неверный код</off>

по поводу развития мысли кода:

DWORD вариант я пока не отмёл, т.к. можно не сдвигать строку, а пройти буфер 4 раза с разными младшими битами ESi и EDi.

Даже не было опровержение метода предолженного default (c)

Сейчас поставил Delphi, попробую этот код в работе...


> уже над нами наверно весь форум смеется

я кажется не говорю ничего смешного.
моё упорство может показаться кому-то смешным, ну вот такой я есть.


 
GuAV ©   (2004-09-26 21:41) [49]


> ESi и EDi.

только EDi точнее.


 
Dimaxx   (2004-09-26 22:13) [50]

Только отлучился, а тут уже темку раздули... :-)


 
Defunct ©   (2004-09-26 22:22) [51]

> Я имел ввиду там способ проверки валидности указателя через try... except, который неправильный.

Так то были вы? :)
Извините, но мне так понравилась эта фраза, что я ее еще раз повторю.

GuAV ©   (26.09.04 02:01) [50]
> т.е. способ try..except - пример неправильного написания программы.


> DWORD вариант я пока не отмёл, т.к. можно не сдвигать строку, а пройти буфер 4 раза с разными младшими битами ESi и EDi.

Удачи. потестируем ваше решение, смотрится хорошо но существенного выиграша в скрости не даст, см [32] пункты "что мы проиграем? - 1,3" плюс добавляется еще чаcтный случай сброса поиска.

Mov  Result, EAx  // This is odd when optimization is on
т.к. любая функция Delphi, возвращающая Integer возвращает его в EAX. (кроме D1, где в AX)
<off>имхо неверный коментарий более грубая ошибка чем неверный код</off>


Вы кстатити как поняли коментарий?
Там написано "при оптимизации - это лишнее"
далее смотрите пост [16], где я отмечаю, что код требует оптимизации, жаль автору такая интересная задачка недосталась.

Function ...;assembler;safecall;
Function ...;assembler;pascal;
Мало ли что, может захочется в DLLку вставить этот код и использоваться safecall. Кстати, вполне актуальное место для приведенного [15] кода.

> Даже не было опровержение метода предолженного default (c)
Какого метода? Ссылку давайте, сыт погорло голословными утверждениями.

> Сейчас поставил Delphi, попробую этот код в работе...
аж дух затаил, жду с нетерпением что скажет Guru AV.


 
GuAV ©   (2004-09-26 23:06) [52]

Defunct ©   (26.09.04 22:22) [51]

Про safecall может и так, но это было бы не правильное её применение.

т.к. любая функция Delphi, возвращающая Integer возвращает его в EAX

{$O-}
{$OPTIMIZATION OFF}
function Return555: Integer; stdcall; assembler; // или pascal анаолгично
asm
 MOV  EAX, 555;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 Caption:=IntToStr(Return555);
end;



>  Guru AV

:) Это идея.


 
Anatoly Podgoretsky ©   (2004-09-26 23:21) [53]

GuAV ©   (26.09.04 23:06) [52]
Guru AV

Вопрос в толковании


 
GuAV ©   (2004-09-26 23:25) [54]

<off>

> Вопрос в толковании

да, я про 216 :)
</off>


 
GuAV ©   (2004-09-26 23:26) [55]

любителям стрёмного асм-кода посвящается...
Function CharPos:Integer;Assembler;
 { in: AL: Char
       EDI: Pointer to buffer
       ECX: Size of buffer
   out: EAX: Result
       ECX: Size of buffer
   }
Asm
//  Xchg  EDx, EDi
 Push  ECx
 Repne ScasB
 MOV EAX, [ESP] // Pop   EAx
 Jnz   @@NotMatch
 Sub   EAx, ECx
//  Xchg  EDx, Edi
 POP   ECX
 Ret
 @@NotMatch:
 Xor   EAx, EAx
 Dec   EAx
//  Xchg  EDx, Edi
 POP   ECX
End;

Function CompareStrings(Constant: string; Buffer:Pointer; BufSize:Integer):Boolean;Assembler;
Asm
Push EDi
Push ESi
Xchg EAx, ESi
Mov  EDi, EDx
Xchg EDx, EBx
Mov  EBx, [ESi-4]
Dec  EBx

@@Scan:
Mov  Al, [ESi]
// Xchg EDx, EDi
// Push ECx
Call CharPos
// Pop  ECx
// Xchg EDx, EDi


 
Defunct ©   (2004-09-26 23:33) [56]

> И потом, подпрограмма не должна оставлять факт остсутсвия файла без внимания, она должна возбудить исключение по этому делу.

С моей точки зрения, фукция должна игнорировать сей факт, ее назначение поиск строки в файле. А раз нет файла, то нет и строки, все четко. Не надо никаких исключений, от них только программа захламляется.

PS: Как код?
сравните оба по скорости, [15] и [40] разницы практически нет.

PPS: to GuruAV, прочитайте [10] сверьте время [10] и [15] сколько ушло на реализацию алгоритма (писал с нуля), посмотрите цель написания кода[10] и прочитайте [16]. теперь скажите, как бы на моем месте вы среагировали на [21].

Согласитесь, задача, хоть и сравнительно простая, но имеет ой как много неприятных моментов.


 
Defunct ©   (2004-09-26 23:41) [57]

GuAV ©   (26.09.04 23:26) [55]

Вот, давно пора было.
Это из серии анекдотов про Илью Муромца, "Как трезвый так золотой человек, как выпьет ну просто хавайся"

Любителям стремного кода надо еще добавить флаг C вместо проверки EAX на -1

Asm
//  Xchg  EDx, EDi
Push  ECx
Repne ScasB
MOV EAX, [ESP] // Pop   EAx
Jnz   @@NotMatch
Sub   EAx, ECx
//  Xchg  EDx, Edi
POP   ECX
CLC
Ret
@@NotMatch:
// Xor   EAx, EAx
// Dec   EAx
STC
//  Xchg  EDx, Edi
POP   ECX
End;

Call CharPos
// Pop  ECx
// Xchg EDx, EDi
Jnc  @@CompareNextChars
Sub  ECx,EAx
Cmp  ECx,EBx
Jb   @@Exit_False

// Cmp  EAx, 0FFFFFFFFh
// Jnz  @@CompareNextChars


Естессно оптимизировать мона и дальше


 
GuAV ©   (2004-09-26 23:47) [58]


> С моей точки зрения, фукция должна игнорировать сей факт,
> ее назначение поиск строки в файле. А раз нет файла, то
> нет и строки, все четко. Не надо никаких исключений, от
> них только программа захламляется.

Не согласен. Исключения полезны, особенно если умеешь с ними обращася.

> Естессно оптимизировать мона и дальше

Да.

Вопросы по коду (что я не понял):

1. Как обрабатывается буфер нулевой длины ?
2. (самое интересное) как обрабатывается ситуация когда часть строки в одном буфере, часть в другом ?
3. Зачем танцы с SeekPos учитывая что строка одна ?


 
GuAV ©   (2004-09-26 23:48) [59]


> Согласитесь, задача, хоть и сравнительно простая, но имеет
> ой как много неприятных моментов.

Вот тут безоговорочно согласен!


 
GuAV ©   (2004-09-26 23:59) [60]


> PPS: to GuruAV, прочитайте [10] сверьте время [10] и [15]
> сколько ушло на реализацию алгоритма (писал с нуля), посмотрите
> цель написания кода[10] и прочитайте [16]. теперь скажите,
> как бы на моем месте вы среагировали на [21].

Ок. Я Вас понял. Поймите и меня: мне код в [15] показался стрёмным именно из-за [21] [26] и особенно [27] в начале. Из-за этго показалось, что писал чайник. Я высказал свои опасения в [21].

зы: перерегистрироваться не хочу, лучше воспользуюсь полем реальное имя.


 
Defunct ©   (2004-09-27 00:02) [61]

GuAV ©   (26.09.04 23:47) [58]

Ха, наконец-то интересный разговор.

2. (самое интересное) как обрабатывается ситуация когда часть строки в одном буфере, часть в другом ?

вот ответ:
..
Sub  ECx,EAx
Cmp  ECx,EBx
Jb   @@Exit_False
..

..
SeekPos := SeekPos - Length(Constant);
..


3. Зачем танцы с SeekPos учитывая что строка одна ?
Думаю это отпадает после ответа на 2.

1. Как обрабатывается буфер нулевой длины ?
Не учтено. проверка должна быть в CharPos.
В измененном варинте добавляю проверку:

Asm
//  Xchg  EDx, EDi

 Push  ECx
 Test  ECx,ECx
 Jz    @@NotMatch

 Repne ScasB
 MOV EAX, [ESP] // Pop   EAx
 Jnz   @@NotMatch
 Sub   EAx, ECx
//  Xchg  EDx, Edi
 POP   ECX
 CLC
 Ret
@@NotMatch:
// Xor   EAx, EAx
// Dec   EAx
 STC
//  Xchg  EDx, Edi
 POP   ECX
End;


Это было действительно дельное замечание. спасибо.


 
Defunct ©   (2004-09-27 00:07) [62]

>Не учтено. проверка должна быть в CharPos.
>В измененном варинте добавляю проверку:


Test  ECx,ECx
Jz    @@NotMatch


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

If BufSize> Length(Constant) Then
Result := CompareStrings(@Constant[1], Buffer, BufSize);


 
Defunct ©   (2004-09-27 00:23) [63]

Вот и закончились выходные...

2 Dimaxx спасибо за вопрос


 
GuAV ©   (2004-09-27 00:30) [64]

Defunct ©   (27.09.04 00:02) [61]

понял.
а попробуйте не выносить LoadBuffer, окажется всё проще:
  Repeat
    BlockRead(F, Buffer^, MaxBufferSize, BufSize);

    Result := CompareStrings(Constant, Buffer, BufSize);

    Seek(F, FilePos(F) - Length(Constant));
  Until Result Or (BufSize<MaxBufferSize);


в своём подобном коде на pascal я просто запоминал позицию в искомой строке.
дело в том что он был не на асм. а в этом я искал того же.


> Вот и закончились выходные...

а у нас ещё полчаса выходных.


 
GuAV ©   (2004-09-27 00:40) [65]

:)
4. как обрабатывается строка длинее MaxBufferSize ?


 
Defunct ©   (2004-09-27 01:51) [66]

GuAV ©   (27.09.04 00:30) [64]

Дык, все неточности, что вы видите - исправьте. Полную версию вашего оптимизированного кода выложете.

В чужем глазу соринку видим, в своем не заметим и бревна(С) народная мудрость. Так что я нисколько не сомневаюсь, что вы в состоянии значительно улучшить код [40]

> а попробуйте не выносить LoadBuffer, окажется всё проще
Это тому яркое подтверждение, я туда и не посмотрел бы.

> 4. как обрабатывается строка длинее MaxBufferSize ?
с помощью Norton FileCompare или как оно там называется ;)

Зы, а код этот думаю для многих пригодится, так как тема быстрого поиска весьма актуальна. может стоит в кладовку выложить?


 
Defunct ©   (2004-09-27 02:53) [67]

Тут после всего залез в модуль System функция _Pos (для ShortString) работает по алгоритму [1] (если отбросить часть которая относится к файлам).


 
GuAV ©   (2004-09-27 13:01) [68]

изучил код.
счас выложу оптимизированный.
сначала кометарий об ошибке
>  Xor  EAx,EAx
>  Dec  EAx
> End;

Xor  EAx,EAx
INC  EAX
end;

дело в том, что ord(True) равно 1. Даже есть код, например в Dialogs.pas который использует конструкцию
const C: array[Boolean] of DWORD = (A, B);
естественно он не будет готов обработать -1.


 
GuAV ©   (2004-09-27 13:03) [69]

Const MaxBufferSize = 1024*1024;

Type TBuffer = Array[0..MaxBufferSize-1] of byte;
   PBuffer = ^TBuffer;

Function CompareStrings(const Constant: string;
  Buffer: Pointer; BufSize: Integer): Boolean; Assembler;
Asm
 Push EDi
 Push ESi
 {Xchg EAx, ESi}  MOV  ESI, EAX  // Eax is not preserved anyway
 Mov  EDi, EDx
 {  EDx is not used to pass buffer pointer anymore
    Hehce we can use EDx here and keep EBx unused
 }

 {Xchg EDx, EBx}
 {Mov  EBx, [ESi-4]} MOV EDX, [ESI-4]
 {Dec  EBx} DEC EDX

@@Scan:
 Mov  Al, [ESi]
 {Xchg EDx, EDi}
 {Push ECx}

 {Xchg  EDx, EDi}
 Push  ECx
 Repne ScasB
 JNE   @@NotMatch
 {
   as we"ve moved CharPos here we can now
   jump almost right to Exit_False
 }

 {Pop   EAx}
 {Jnz   @@NotMatch}
 MOV   EAX, [ESP]
 Sub   EAx, ECx
 {Xchg  EDx, Edi}
 {Ret} {JMP @@ScanDone

 @@NotMatch:
 Xor   EAx, EAx
 Dec   EAx       }
 {Xchg  EDx, Edi}
@@ScanDone:

 Pop  ECx
 {Xchg EDx, EDi}

 Sub  ECx,EAx
 Cmp  ECx, EDX
 Jb   @@Exit_False

{ Cmp  EAx, 0FFFFFFFFh
 Jnz  @@CompareNextChars }
@@NotMatch:
 POP   ECX
@@Exit_False:
 Pop  ESi
 Pop  EDi
 {Xchg EDx, Ebx}
 Xor  EAx,EAx
 Ret

@@CompareNextChars:
 Push ECx
 Mov  ECx, EDX
 Push ESi
 Inc  ESi
 Push EDi    //Здесь можно предусмотреть пропуск просмотренных символов
 Repe CmpsB
 Pop  EDi    //будет работать немножко быстрее
 Pop  ESi
 Pop  ECx
 Jnz  @@Scan

 Pop  ESi
 Pop  EDi
 {Xchg EDx, Ebx}
 Xor  EAx,EAx
 INC  EAx
End;

Function ConstantExists(FileName, Constant: String):Boolean;
Var
 Buffer  : PBuffer;
 F       : File;
 BufSize : Integer;
Begin
 Result := False;
 New(Buffer);
 try
   Assign(F, FileName);
   Reset(F,1);
   try
     Repeat
       BlockRead(F, Buffer^, MaxBufferSize, BufSize);
       Result := CompareStrings(Constant, Buffer, BufSize);
       Seek(F, FilePos(F) - Length(Constant));
     Until Result Or (BufSize<MaxBufferSize);
   finally
     Close(F);
   end;
 finally
   Dispose(Buffer);
 end;
End;


 
GuAV ©   (2004-09-27 13:13) [70]


> { Cmp  EAx, 0FFFFFFFFh
>  Jnz  @@CompareNextChars }

случайно закоментировал, не надо кометировать это
 Cmp  EAx, 0FFFFFFFFh
 Jnz  @@CompareNextChars

и ещё

     Repeat
       BlockRead(F, Buffer^, MaxBufferSize, BufSize);
       if Bufsize=0 then Exit;
       Result := CompareStrings(Constant, Buffer, BufSize);
       Seek(F, FilePos(F) - Length(Constant));
     Until Result Or (BufSize<MaxBufferSize);


 
Defunct ©   (2004-09-27 16:39) [71]

Код [69] не рабочий.

Project .... failed with message "privileged instruction at address ..." process stopped use RUN to continue

Вашу идею с объединением функций подхватил, сейчас попробую сделать рабочим.


 
Defunct ©   (2004-09-27 17:19) [72]

Function CompareStrings(const Constant: string;
 Buffer: Pointer; BufSize: Integer): Boolean; Assembler;
Asm
 Push EDi
 Push ESi
 Push EBx
 MOV  ESI, EAX       // ESi pointer to constant
 Mov  EDi, EDx       // EDi pointer to buffer
 Mov  EBX, [ESI-4]
 Dec  EBX
 Dec  EBx            // EBx - length of the constant w/o 1st and last chars
 Mov  Al, [ESi]      // Store first char of the constant in AL
 Mov  Ah, [ESi+EBx+1]// Store last char of the constant in AH
 Mov  EDx,ECx        // EDx - Stored residual buffer size

@@Scan:
 Repne ScasB
 JNE   @@Exit_False  // no 1st char of the constant found = exit_false
 Mov   EDx, ECx      // store new residual buffer size
 Cmp   EDx, EBx      // constant still can be settled in buffer
 Ja    @@CheckLastChar  // no - exit with false result

@@Exit_False:
 Pop  EBx
 Pop  ESi
 Pop  EDi
{Xchg EDx, Ebx}
 Xor  EAx,EAx        // False = (0), check delphi help for more info
 Ret

@@CheckLastChar:      // as proposed in Boyer-Mur alhorithm

 Cmp   Ah, [EDi+EBx] // check found substring with last char of the constant
 Jnz   @@Scan        // not match - continue searching

@@CompareWholeString:
  Mov  ECx, Ebx  // ECx - constant chars amount
  Push ESi       // store pointer to constant
  Inc  ESi
  Push EDi       //&#199;&#228;&#229;&#241;&#252; &#236;&#238;&#230;&#237;&#238; &#239;&#240;&#229;&#228;&#243;&#241;&#236;&#238;&#242;&#240;&#229;&#242;&#252; &#239;&#240;&#238;&#239;&#243;&#241;&#234; &#239;&#240;&#238;&#241;&#236;&#238;&#242;&#240;&#229;&#237;&#237;&#251;&#245; &#241;&#232;&#236;&#226;&#238;&#235;&#238;&#226;
  Repe CmpsB
  Pop  EDi       //&#225;&#243;&#228;&#229;&#242; &#240;&#224;&#225;&#238;&#242;&#224;&#242;&#252; &#237;&#229;&#236;&#237;&#238;&#230;&#234;&#238; &#225;&#251;&#241;&#242;&#240;&#229;&#229;
  Pop  ESi
  Mov  ECx, EDx  // restore residual buffer size to ECx
  Jnz  @@Scan

  Pop  EBx
  Pop  ESi
  Pop  EDi

  Xor  EAx,EAx
  Dec  EAx       // True = (-1) check delphi help for more info
End;

Function ConstantExists(FileName, Constant: String):Boolean;
Var
Buffer  : PBuffer;
F       : File;
BufSize : Integer;
Begin
Result := False;
New(Buffer);
try
  Assign(F, FileName);
  Reset(F,1);
  try
    Repeat
      BlockRead(F, Buffer^, MaxBufferSize, BufSize);
      If BufSize< Length(Constant) Then Exit;
      Result := CompareStrings(Constant, Buffer, BufSize);
      Seek(F, FilePos(F) - Length(Constant));
    Until Result Or (BufSize<MaxBufferSize);
  finally
    Close(F);
  end;
finally
  Dispose(Buffer);
end;
End;


Немножко модифицировал алгоритм поиска взял проверку последнего символа из алгоритма Бойера-Мура, работает быстрее кода [40] если в буфере встречается много одинаковых "обрезков" искомой строки.


 
Defunct ©   (2004-09-27 18:03) [73]

Function CompareStrings(const Constant: string;
Buffer: Pointer; BufSize: Integer): Boolean; Assembler;
Asm
Push EDi
Push ESi
Push EBx
MOV  ESI, EAX       // ESi pointer to constant
Mov  EDi, EDx       // EDi pointer to buffer
Mov  EBX, [ESI-4]
Dec  EBX
Mov  Ah, [EBx+ESi]  // Store last char of the constant in AH
Dec  EBx            // EBx - length of the constant w/o 1st and last chars
Mov  Al, [ESi]      // Store first char of the constant in AL
Mov  EDx,ECx        // EDx - Stored residual buffer size

@@Scan:
Repne ScasB
JNE   @@Exit_False  // no 1st char of the constant found = exit_false
Mov   EDx, ECx      // store new residual buffer size
Cmp   EDx, EBx      // constant still can be settled in buffer
Ja    @@CheckLastChar  // no - exit with false result

@@Exit_False:
Pop  EBx
Pop  ESi
Pop  EDi
Xor  EAx,EAx        // False = (0), check delphi help for more info
Ret

@@CheckLastChar:      // as proposed in Boyer-Mur alhorithm

Cmp   Ah, [EDi+EBx] // check found substring with last char of the constant
Jnz   @@Scan        // not match - continue searching

@@CompareWholeString:
 Mov  ECx, Ebx  // ECx - constant chars amount
 Push ESi       // store pointer to constant
 Inc  ESi
 Push EDi      //
 Repe CmpsB
 Pop  EDi       //
 Pop  ESi
 Mov  ECx, EDx  // restore residual buffer size to ECx
 Jnz  @@Scan

 Pop  EBx
 Pop  ESi
 Pop  EDi

 Xor  EAx,EAx
 Dec  EAx       // True = (-1) check delphi help for more info
End;


Вот, теперь вроде бы ничего лишнего нет, кроме пары Push/Pop, которая четно говоря мне там не нравится..


 
Defunct ©   (2004-09-27 19:25) [74]

Эта функция работает почти в два раза быстрее чем стандартная функция Pos из модуля SYSTEM Delphi. За счет того, что в основном цикле поиска нет ни одного обращения к стеку, и минимизированы обращения к памяти.

Function ConstantPos(const Constant: string;
 Buffer: Pointer; BufSize: Integer): Integer; Assembler;
Asm
  Push EDi
  Push ESi
  Push EBx
  Push EBp
  Push ECx            // store total buffer size for later calculation pos
  MOV  ESI, EAX       // ESi pointer to constant
  Mov  EDi, EDx       // EDi pointer to buffer
  Mov  EBX, [ESI-4]
  Dec  EBX
  Mov  Ah, [ESi+EBx]  // Store last char of the constant in AH
  Dec  EBx            // EBx - length of the constant w/o 1st and last chars
  Mov  Al, [ESi]      // Store first char of the constant in AL
  Mov  EDx,ECx        // EDx - Stored residual buffer size
  Inc  ESi
  Mov  EBp, ESi       // EBp - stored pointer to 2nd char of constant

@@Scan:
  Repne ScasB
  JNE   @@Exit_False  // no 1st char of the constant found = exit_false
  Mov   EDx, ECx      // store new residual buffer size
  Cmp   EDx, EBx      // constant still can be settled in buffer
  Ja    @@CheckLastChar  // no - exit with false result

@@Exit_False:
  Pop  EAx
  Xor  EAx,EAx        // False = (0), check delphi help for more info
  Dec  EAx
  Jmp  @@Leave_Proc

@@CheckLastChar:      // as proposed in Boyer-Mur alhorithm

  Cmp   Ah, [EDi+EBx] // check found substring with last char of the constant
  Jnz   @@Scan        // not match - continue searching

@@CompareWholeString:
  Mov  ECx, Ebx       // ECx - constant chars amount
  Repe CmpsB
  Jz   @@Constant_Exists
  Mov  ESi, EBp       // restore pointer to 2nd char of constant
  Add  EDx, ECx
  Sub  EDx, EBx       // correct residual buffer size
  Mov  ECx, EDx       // restore residual buffer size to ECx

  Jnz  @@Scan

@@Constant_Exists:
  Pop  EAx
  Sub  EAx, EDx

@@Leave_Proc:

  Pop  EBp
  Pop  EBx
  Pop  ESi
  Pop  EDi
End;


Использовать приминительно к сабжу можно так:

Function ConstantExists(FileName, Constant: String):Integer;
Var
Buffer  : PBuffer;
F       : File;
BufSize : Integer;
Begin
Result := -1;
New(Buffer);
try
  POs
  Assign(F, FileName);
  Reset(F,1);
  try
    Repeat
      BlockRead(F, Buffer^, MaxBufferSize, BufSize);
      If BufSize< Length(Constant) Then Exit;
//       Result := CompareStrings(Constant, Buffer, BufSize);
      Result := ConstantPos(Constant, Buffer, BufSize);
      Seek(F, FilePos(F) - Length(Constant));
    Until (Result>0) Or (BufSize<MaxBufferSize);
  finally
    Close(F);
  end;
finally
  Dispose(Buffer);
end;
End;


 
Defunct ©   (2004-09-27 19:41) [75]

Забыл добавить описание:

Function ConstantPos(const Constant: string;
Buffer: Pointer; BufSize: Integer): Integer; Assembler;
{  -> EAx - указатель на строку поиска (константа)
     EDx - указатель на буфер, в котором требуется найти строку поиска
     ECx - длина буфера

  <- EAx - Позиция строки поиска в буфере
           (-1) если строки в буфере нет
}


 
Defunct ©   (2004-09-27 20:52) [76]

результат поиска строки "01wbcb" в файле gladiator.avi (726Mb) с учетом что такой строки в файле нет, а есть множество строк "01wb" составил - 22.34 сек, при повторном запуске - 13.71 сек. проверял на компе с 512Mb памяти (т.е. файл не мог полностью разместиться в кеше).


 
GuAV ©   (2004-09-27 21:38) [77]


>  Xor  EAx,EAx
>  Dec  EAx       // True = (-1) check delphi help for more
> info

заблуждаетесь.
F1 - Boolean types


>   Repe CmpsB

Repe CmpsD, а CmpsD - остаток


 
GuAV ©   (2004-09-27 21:38) [78]

из справки:
Boolean ByteBool, WordBool, LongBool
False < True False <> True
Ord(False) = 0 Ord(False) = 0
Ord(True) = 1 Ord(True) <> 0
Succ(False) = True Succ(False) = True
Pred(True) = False Pred(False) = True


 
GuAV ©   (2004-09-27 22:08) [79]


> Код [69] не рабочий.
>
И после [70] ?


 
Defunct ©   (2004-09-27 22:19) [80]

GuAV ©   (27.09.04 21:38) [77]
> Repe CmpsD, а CmpsB - остаток

Уже рассматривал, этот вариант. Мне не хватает одного РОНа, чтобы сделать с CmpsD. Приходится юзать стек, значительно проиграем на коротких строках. либо придется использовать регисты MM0-MMX, тада возможен, как тут любят выражаться, "гимор" если в программе была арифметика с плавающей точкой.

> заблуждаетесь.
Возможно, а возможно и нет, читал совсем недавно 1-2 месяца назад, в справке по Delphi тока не помню где именно (удивлялся). Там еще были примеры с приведением типов Integer -> Boolean, Boolean -> Integer, Boolean -> Byte, Byte -> Boolean, и вот осело в памяти $FFFFFFFF = True. Так или иначе, это не имеет значения, т.к. проверка на True все равно делается командой Test(будь там -1 или +1 результат не изменится, а по скорости Dec и Inc идентичны)

Test Reg,Reg
Jnz  @@True

PS: могу попробовать сделать поиск с использованием MMX если найду время.


 
Defunct ©   (2004-09-27 22:23) [81]

GuAV ©   (27.09.04 22:08) [79]
не столь важно, поймите я не хочу вас оскорбить или сказать, что вы плохо разбираетесь в асме, просто заметил ошибку, исправил привел вариант [74] так что не зацикливайтесь на одном месте. Хотите продолжить оптимизацию, продолжайте с [74]


 
GuAV ©   (2004-09-27 22:26) [82]


> результат поиска строки "01wbcb" в файле gladiator.avi (726Mb)
> с учетом что такой строки в файле нет, а есть множество
> строк "01wb" составил - 22.34 сек, при повторном запуске
> - 13.71 сек. проверял на компе с 512Mb памяти (т.е. файл
> не мог полностью разместиться в кеше).

Это мысль ! Буфер надо не 1ГБ а где-то 16..32 кБ


 
GuAV ©   (2004-09-27 22:32) [83]


> Так или иначе, это не имеет значения, т.к. проверка на True
> все равно делается командой Test(будь там -1 или +1 результат
> не изменится, а по скорости Dec и Inc идентичны)

Не всегда, на пример я сослался в
GuAV ©   (27.09.04 13:01) [68]
т.е. с Boolean вольности не допускаются

> Возможно, а возможно и нет, читал совсем недавно 1-2 месяца
> назад, в справке по Delphi тока не помню где именно (удивлялся).
> Там еще были примеры с приведением типов Integer -> Boolean,
> Boolean -> Integer, Boolean -> Byte, Byte -> Boolean, и
> вот осело в памяти $FFFFFFFF = True.

Ну, в книгах бывают опечатки. Я думал тоже в EAX Integer-результат всегда, а оказывается к safecall это неприменимо.

$FFFFFFFF = True
Насколько я поню, это так в VisualBasic

в Delphi SizeOf(Boolean) = 1 байт. ord(True)=1

есть типы ByteBool, LongBool, WordBool в которых допускаются вольности по части ord(True). в Window.pas кажется или ещё где то объявлен тип BOOL=LongBool


 
GuAV ©   (2004-09-27 22:50) [84]

Раз возвращаете Integer, возвращайте уже позицию в файле.

Function ConstantExists(FileName, Constant: String): Int64;
Var
 Buffer  : PBuffer;
 F       : File;
 BufSize : Integer;
 BufPos  : Integer;
Begin
 Result:=0;
 New(Buffer);
 try
   AssignFile(F, FileName);
   Reset(F,1);
   try
     Repeat
       BlockRead(F, Buffer^, MaxBufferSize, BufSize);
       If BufSize < Length(Constant) Then Break;
       BufPos := ConstantPos(Constant, Buffer, BufSize);
       if BufPos > 0 then
       begin
         Inc(Result, BufPos);
         Exit;
       end;
       Inc(Result, BufSize);
       Seek(F, FilePos(F) - Length(Constant));
     Until (BufSize<MaxBufferSize);
     Result := -1;
   finally
     CloseFile(F);
   end;
 finally
   Dispose(Buffer);
 end;
End;


> Буфер надо не 1ГБ а где-то 16..32 кБ

вечер плохого дня...


 
GuAV ©   (2004-09-27 22:59) [85]


>  if BufPos > 0 then

BufPos => 0 или BufPos <> -1.


 
GuAV ©   (2004-09-27 23:03) [86]

такой вариант принимается ?

@@CompareWholeString:
 Mov  ECx, Ebx       // ECx - constant chars amount
 shr  ECx, 2       // ECx - constant chars amount div 4
 Repe CmpsD
 Jz   @@Constant_Exists
 Mov  ECx, Ebx
 and  ECx, 3
 Repe CmpsB
 Jz   @@Constant_Exists

или наоборот, сначала B а потом D
 Mov  ECx, Ebx       // ECx - constant chars amount
 shr  EBx, 2       // EBx - constant chars amount div 4
 and  ECx, 3
 Repe CmpsB
 Jz   @@Constant_Exists
 Mov  ECx, Ebx
 Repe CmpsD
 Jz   @@Constant_Exists


 
Defunct ©   (2004-09-27 23:13) [87]

[84]
Поправлю, вы забыли учесть откат на Length(Constant):

Function ConstantExists(FileName, Constant: String): Int64;
Var
Buffer  : PBuffer;
F       : File;
BufSize : Integer;
BufPos  : Integer;
Begin
BufPos := -1;
New(Buffer);
try
  AssignFile(F, FileName);
  Reset(F,1);
  try
    Repeat
      Result := FilePos(F);
      BlockRead(F, Buffer^, MaxBufferSize, BufSize);
      If BufSize >= Length(Constant) Then
      BufPos := ConstantPos(Constant, Buffer, BufSize);
      Result := Result + BufPos;
      Seek(F, FilePos(F) - Length(Constant));
    Until (BufPos>0) or (BufSize<MaxBufferSize);
  finally
    CloseFile(F);
  end;
finally
  If BufPos < 0 Then Result := -1;
  Dispose(Buffer);
end;
End;


 
GuAV ©   (2004-09-27 23:26) [88]


>       If BufSize >= Length(Constant) Then
>       BufPos := ConstantPos(Constant, Buffer, BufSize);
>       Result := Result + BufPos;

Result будет накапливать -1.


 
GuAV ©   (2004-09-27 23:29) [89]


> Поправлю, вы забыли учесть откат на Length(Constant):

Тогда
Function ConstantExists(FileName, Constant: String): Int64;
Var
Buffer  : PBuffer;
F       : File;
BufSize : Integer;
BufPos  : Integer;
Begin
New(Buffer);
try
  AssignFile(F, FileName);
  Reset(F,1);
  try
    Repeat
      Result := FilePos(F);
      BlockRead(F, Buffer^, MaxBufferSize, BufSize);
      If BufSize < Length(Constant) Then Break;
      BufPos := ConstantPos(Constant, Buffer, BufSize);
      if BufPos > 0 then
      begin
        Inc(Result, BufPos);
        Exit;
      end;
      {Inc(Result, BufSize);}
      Seek(F, FilePos(F) - Length(Constant));
    Until (BufSize<MaxBufferSize);
    Result := -1;
  finally
    CloseFile(F);
  end;
finally
  Dispose(Buffer);
end;
End;


 
Defunct ©   (2004-09-27 23:38) [90]

GuAV ©   (27.09.04 23:03) [86]
принимается с поправками, а то получим нехилую ошибку при Length(Constant)<=6 и при (Length(Constant)) mod 4 = 0.


@@CompareWholeString:
  Mov  ECx, Ebx       // ECx - constant chars amount
  shr  ECx, 2         // ECx - constant chars amount div 4
  jz   @@SkipDTest
  Repe CmpsD
  Jnz  @@SkipBTest
@@SkipDTest:  
  Mov  ECx, Ebx
  and  ECx, 3
  jz   @@SkipBTest
  Repe CmpsB
  Jz   @@Constant_Exists
@@SkipBTest:
  Mov  ESi, EBp       // restore pointer to 2nd char of constant


В общем, получилось просто отлично. на том же файле (gladiator.avi) строка поиска ("01wbcb") время просмотра всего файла - 13.43 сек (было 13.71). Для больших строк конечно же ускорение будет выше.

Попробовал поиграться с размером буфера:
- 10MB скорость падает
- делаю 512Kb - скорость падает.
(Кеш L2 у меня 1Mb) потому 1Mb - оптимально как для машины на которой проводил тесты.


 
Defunct ©   (2004-09-27 23:45) [91]

GuAV ©   (27.09.04 23:26) [88]

Дык, в секции finally посмотрите If bufpos<0....
и сброс перед поиском Result := FilePos


 
GuAV ©   (2004-09-27 23:57) [92]

 Mov  ECx, Ebx       // ECx - constant chars amount
 shr  ECx, 2         // ECx - constant chars amount div 4
 jz   @@SkipDTest
 Repe CmpsD
 Jnz  @@SkipBTest
@@SkipDTest:  
 Mov  ECx, Ebx
 and  ECx, 3
 jz   @@Constant_Exists // two least significant bits
                        // of amount is zero so the Constant matches

 Repe CmpsB
 Jz   @@Constant_Exists
@@SkipBTest:

?


 
GuAV ©   (2004-09-27 23:58) [93]

сорри, Ж нажал вместо КОД
Mov  ECx, Ebx       // ECx - constant chars amount
shr  ECx, 2         // ECx - constant chars amount div 4
jz   @@SkipDTest
Repe CmpsD
Jnz  @@SkipBTest
@@SkipDTest:  
Mov  ECx, Ebx
and  ECx, 3
jz   @@Constant_Exists // two least significant bits
                       // of amount is zero so the Constant
matches
Repe CmpsB
Jz   @@Constant_Exists
@@SkipBTest:

принято ?


 
GuAV ©   (2004-09-28 00:09) [94]


> GuAV ©   (27.09.04 23:26) [88]
>
> Дык, в секции finally посмотрите If bufpos<0....
> и сброс перед поиском Result := FilePos

ок.
Всё же вариант [89] ничем не хуже, только проще.

 If BufPos < 0 Then Result := -1;
кстати подобный код следует размещать после until перед finally, но не в finally. дело в том, что в случае отсутствия исключения он и так выполнится. в случае исключения результат функции не нужен, более того я даже не вижу возможности им воспользоваться.


 
Defunct ©   (2004-09-28 00:32) [95]

GuAV ©   (27.09.04 23:58) [93]
конечно принято!

GuAV ©   (28.09.04 00:09) [94]
> в случае исключения результат функции не нужен
почему бы не перестраховаться. пусть возвращает правильный результат даже в исключительной ситуации. (нам тока спасибо скажут)

> более того я даже не вижу возможности им воспользоваться.
А я вижу, когда "наплевать" на исключения, их просто проигнорируют, а результат функции будет верным.
Тока не надо говорить, что так делать не правильно, сам знаю, но все равно делаю ;)


 
GuAV ©   (2004-09-28 00:38) [96]


> > более того я даже не вижу возможности им воспользоваться.
> А я вижу, когда "наплевать" на исключения, их просто проигнорируют,
> а результат функции будет верным.
> Тока не надо говорить, что так делать не правильно, сам
> знаю, но все равно делаю ;)


например если
I:=MyFunc
и в MyFunc - исключение, то в I остаётся старое значение

я не пробовал, но даже немного сомневаюсь что возможно получить результат в этом случае. если можете - покажите как Вы бы это делали.


 
Defunct ©   (2004-09-28 00:56) [97]

> я не пробовал, но даже немного сомневаюсь что возможно получить результат в этом случае. если можете - покажите как Вы бы это делали.

Беру функцию [87]

Использую ее так:

procedure TForm1.Button1Click(Sender: TObject);
Var Position : Int64;
begin
Try
  Position := ConstantExists(Edit1.Text, Edit2.Text);
Except
End;

If Position>0 Then
   ShowMessage("Found")
Else
   ShowMessage("Not Found");
End;
end;

В Edit1 ввожу какую-то лабуду типа "sdfsdf.asdasd" в Edit2 - "Test String" возникает исключение на экране вижу "Not Found". Хотя понятно, тест синтетический проще было бы в обработчике исключения написать, что файла с таким именем нет. ;)


 
GuAV ©   (2004-09-28 00:59) [98]

Defunct ©   (28.09.04 00:56) [97]
Это похоже на уровне "повезло", попробуйте
Var Position : Int64; - глобальная переменная.


 
Defunct ©   (2004-09-28 01:00) [99]

PS: код [97] серьезно не воспринимать, правильно должно быть так:

procedure TForm1.Button1Click(Sender: TObject);
begin
 Try
   If ConstantExists(Edit1.Text, Edit2.Text)>0 Then
      ShowMessage("Found")
   Else
      ShowMessage("Not Found");
 Except
   ShowMessage("file "+Edit1.Text+" not found");
 End;
end;


 
Defunct ©   (2004-09-28 01:03) [100]

GuAV ©   (28.09.04 00:59) [98]
Ок, убедили, признаю - страдать фигней незачем.


 
GuAV ©   (2004-09-28 01:21) [101]

знаете, как можно узнать про флаг D, отсутсвие результата при exception и т.д. ?
посмотреть исходники VCL, найти места где Вы не согласны с Borland и разобраться почему они всё же правы.

см. мою анкету -
F1 - интересно, Ctrl+Click - ещё интереснее,

Defunct ©   (28.09.04 01:00) [99]
на самом деле, лучше вообще без except. тогда будет выведен текст исключения в сообщении, почему именно не получилось. может не файл не найден а ещё что-то. хотя бы EOutOfMemory.

или уже например что-то типа
Try
  If ConstantExists(Edit1.Text, Edit2.Text)>0 Then
     ShowMessage("Found")
  Else
     ShowMessage("Not Found");
Except
  on EInOutError do ShowMessage("file "+Edit1.Text+" not found");
End;

except без on следует использовать только если внутри raise; - т.е. исключение сохранится для дальнейшего использования.


 
Defunct ©   (2004-09-28 02:29) [102]

> найти места где Вы не согласны с Borland и разобраться почему они всё же правы.

Знаете, не надо так свято верить в правоту Borland, функция [74] - быстрее Pos, а та к которой мы с вами в итоге пришли после усовершенствования [74] работает более чем в два раза быстрее чем встроенная функция поиска подстроки в строке: Pos. И это не первая функция на моем счету, которая доказывает, что не все решения от Borland оптимальны.

> F1 - интересно, Ctrl+Click - ещё интереснее,
Хелп сделан хорошо, здесь упреков нет.

> на самом деле, лучше вообще без except. тогда будет выведен текст исключения в сообщении
Это уж кому как, я предпочту чтобы программа умолчала об исключении вместо того чтобы показала, что исключение в ней не обрабатывается.

> почему именно не получилось. может не файл не найден а ещё что-то. хотя бы EOutOfMemory.

Ну что ж тогда увы и ах..:
<begin offtop>
.. в одном отдаленном селе Василий Певченко скачал шареварную версию нового "PuperCommander". С возгласом:
- ну наконец-то, догожданная новая версия,
Василий принялся изучать Readme файл со списоком новых возможностей и улучшений...
- О! быстрый поиск строки, сейчас посмотрю, что они там намутили, - улыбнулся Василий, который несколько лет ждал именно эту фичу.
Он задавал имя файла и ввел "смешные анеГДОТЫ" в раскрашенном поле строки поиска, к его глубочайшему удивлению на экране возникало сообщение "Да ну нет такого файла!". Василий повторял ввод имени раз за разом, раз за разом, но на экране в также весело красовалась пестрая надпись с заголовком PuperCommander, уведомляющая, что такого файла нет. Василий вспомнил, что вчера выпил не мало, об этом напиминали трясущиеся руки, и решил скопировать имя файла прямо из проводника самой модной операционки "ГунДоус" от корпорации "МодныйСофт". А на экране все также пестрилась веселая надпись "Да не найден, надо меньше пить". В гневе Василий нажал кнопку "ПЫтание" на корпусе своего модного компутера "ИВМ" от фирмы "Красночух Электроникс", замигали лампочки, в динамиках раздался приятный женский голос "подождите пожалуйста, ГунДоус приступил к первой фазе подготовки к выполнению завершения вашей работы". Василий ждал..
.....
...
..
Экран засветился, потом погас, потом опять засветился на этот раз с надписью "Красночух Электроникс БИОС, ждите", замигали лампочки. Василий потихоньку приходил в себя, он взял очередную бутылочку пива из холодильника и удобно развалился на кресле с мягкой спинкой. Наблюдая за меняющимися картинками, которыми обычно сопровождается загрузка модной системы "ГунДоус", он размышлял о том, что он самый счастливый человек на свете. Теперь у него есть "PuperCommander" и он сможет с гордостью похвастаться об этом своим друзьям. "ГунДоус" пригласил Василия ввести пароль, после чего Василий сразу же запустил "PuperCommander" ввели имя файла и.... О чудо! ни каких надписей, пестрая табличка с вопросом "смешные анеГДОТЫ найдены, показать?".
- Глючный ГунДоус, - подумал Василий, - когда уже эти олухи из корпорации "МодныйСофт" сделают что-то нормальное..
<end offtop>

И в чем-то Василий будет прав.
В данном конкретном случае меня не интересуют другие ошибки кроме "файл не найден" так как логически там их быть не может, кроме случая - ошибка ОС.


 
jack128 ©   (2004-09-28 02:42) [103]

Defunct ©   (28.09.04 2:29) [102]
работает более чем в два раза быстрее чем встроенная функция поиска подстроки в строке:


Ага, сели два чела, 4 дня провозились и еще и удивляются, как же у них получилась функция, которая быстрее стандартной реализации ;-)

зы Pos - не ориентирован на поиск подстроки в 1Гб файле. Насколько она быстрее при работе с обычными строками (длиной 30-100 символов)
ззы вашу функцию нужно в базу знаний отправить.


 
Defunct ©   (2004-09-28 03:06) [104]

> Насколько она быстрее при работе с обычными строками (длиной 30-100 символов)

Pos будет медленнее, так как в стадартной функции Pos две пары Push/Pop в основном цикле поиска и не рассмотривается вариант отбраковки подстроки по первому и последнему символам.

На сколько медленнее сказать сейчас не могу. На маленьких строках тяжело точно измерить скорость, куча побочных эффектов, например, просто факт вызова функции (команда Call) отнимает примерно столько времени сколько потребуется на просмотр 2-5 символов, согласитесь немало при длинне строки в 30 символов.


 
Defunct ©   (2004-09-28 10:46) [105]

2 GuruAV.

Подумайте как можно применить следущее:
скажем есть возможность получать такие последовательности (команда PCMPEQB mmreg, mmreg/rel32}
на входе:
->
  54h 34h 34h 45h 35h 43h 64h 70h 65h 70h
  34h 34h 34h 34h 34h 34h 34h 34h 34h 34h


на выходе:
<-
  00h FFh FFh 00h 00h 00h 00h 00h 00h 00h


за один такт.

  Как бы это применить к нашему сабжу.. Хочется избавиться от ScasB


 
Defunct ©   (2004-09-28 11:49) [106]

вот наброски, черт его знает стоит ли оно того, что-то уж сильно много кода получается


  ...
{$ifdef MMX}
  MovQ  MM0, [Edi]    // load prefics
  PCMPEQB MM0, MM6    // make a mask (mm6 filled by 1st char of da constant)
  MovD  EAx, MM0
  Test  EAx,EAx
  Jnz   @@Char_somewhere??
  PSRLQ MM0,32        // mm0 := mm0 div 2^32
  MovD  EAx, MM0
  Test  EAx, EAx
  Jnz   @@Char_Somewhere??
  Add   EDi,8
  Sub   ECx,8
  Jns   @@Scan
{$else}
  Repne ScasB
{$endif}
  ...
  ...

@@Leave_Proc:
{$ifdef MMX}
  EMMS
{$endif}


 
Игорь Шевченко ©   (2004-09-28 12:30) [107]

MMF оно проще...


 
Sha ©   (2004-09-28 12:34) [108]

Ага, никакого буфера.
И простым циклом на Паскале.


 
GuAV ©   (2004-09-28 22:12) [109]


> И это не первая функция на моем счету, которая доказывает,
> что не все решения от Borland оптимальны.

Однако они почти все верны. Т.к. умные люди писали и их очень многие используют.
Ну могут быть и не оптимальны, т.к. они и не особо стремятся - они вероятно такой фигнёй не страдают.


> Это уж кому как, я предпочту чтобы программа умолчала об
> исключении вместо того чтобы показала, что исключение в
> ней не обрабатывается.

Ну вот допустим памяти не хватило. Тогда файл не найден.
А он не найден потому что его нет или памяти нехватило, или ошибка чтения файла - угадай-ка ? :)
Если же речь идет об ошибке ОС, то показ текста выданого самой ОС - ИМХО лучшая обработка.


> меня не интересуют другие ошибки кроме "файл не найден"
> так как логически там их быть не может, кроме случая - ошибка
> ОС

Может и не интересуют, но они могут в принципе быть.
И потом, ошибка файл не найден должна обрабатываться не этой функцией, а вызывающей её функцией и если хотите всё же её обрабатывать, то GuAV ©   (28.09.04 01:21) [101] .

Ошибки могут быть даже там где их логически быть не может.

jack128 ©

> Ага, сели два чела, 4 дня провозились и еще и удивляются,
> как же у них получилась функция, которая быстрее стандартной
> реализации ;-)

я вообще-то не особо возился :) заметь, несмотря на то что я поставил Дельфи, я ни разу не выполнял этот код. Так чит
Поэтому больше благодари Defunct ©


> ззы вашу функцию нужно в базу знаний отправить.

Почему-то мне кажется что там такое уже есть.
Если конечно Defunct © напишет версию через продвинутый набор комманд, то этой может там ещё не быть.

Кстати, согласно моей информации (изрядно впрочем устаревшей), MOV и PUSH работают одинаково быстро (если в качестве операнда push РОН). XCHG же работает медленнее. Может на современных процессорах это не так, тогда был бы рад информации.

PUSH  reg16   ¦ push  dx                ¦ 88/86 11 (88=15)
PUSH  reg32*  ¦                         ¦   286 3
PUSHW reg16   ¦                         ¦   386 2
PUSHD reg16*  ¦                         ¦   486 1
PUSHD reg32*  ¦                         ¦

MOV  reg,reg        ¦ mov   dh,bh             ¦ 88/86 2
                   ¦ mov   dx,cx             ¦   286 2
                   ¦ mov   bp,sp             ¦   386 2
                   ¦                         ¦   486 1

XCHG  reg,reg       ¦ xchg   cx,dx        ¦ 88/86 4
                    ¦ xchg   dl,dh        ¦   286 3
                    ¦ xchg   al,ah        ¦   386 3
                    ¦                     ¦   486 3



> MMF оно проще...

- Cолдаты! берите ломы и копайте яму 2 на 2!
- Товарищь прапорщик, а не лучше ли взять лопаты?
- А мне не нужно лучше, мне нужно чтоб вы за... (замучились) !


 
Defunct ©   (2004-09-29 02:06) [110]

XCHG  reg32,reg32       ¦ xchg   eax,edx        ¦ P-Pro and higher - 1/2


 
Fedia   (2004-09-29 02:56) [111]

To Defunct and GuAV.
Даже несмотря на то, что большинство кода на ASM для меня оставалось загадкой, все равно с удовольствием почитал ветку.
А итоговую процедуру поиска с удовольствием буду использовать.
Просто хотел поблагодарить GuAV за настойчивость, а Defunct за терпение :)


 
Defunct ©   (2004-09-29 03:23) [112]

Кому интересно, поиск строки с использованием MMX. Ускорен поиск на 10-20% (скорость зависимосит от частоты появлнеия первого символа искомой строки в буфере поиска). Результат пересмотра буфера объемом- 2.5Gb (25Mb 100 раз подряд) составляет 9.123 сек, в то время как без MMX - 10.238 сек.

{$define MMX}

Function ConstantPos(const Constant: string;
 Buffer: Pointer; BufSize: Integer): Integer; Assembler;
Asm
  Push EDi
  Push ESi
  Push EBx
  Push EBp
  Push ECx            // store total buffer size for later calculation pos
  MOV  ESI, EAX       // ESi pointer to constant
  Mov  EDi, EDx       // EDi pointer to buffer
  Mov  Al, [ESi]      // Store first char of the constant in AL
  Mov  EBX, [ESI-4]
  Dec  EBX
{$ifdef MMX}
  Mov  Ah, Al         // Spreading char to 32bit register
  Push Ax
  Push Ax
  Pop  EAx
  MovD MM6, EAx
  PSLLQ MM6,MM6       // Spreading char to mmx register
  PXOR  MM5,MM5       // Z mask for later calculations
  MovD MM6, EAx
{$endif}
  Mov  Ah, [ESi+EBx]  // Store last char of the constant in AH
  Dec  EBx            // EBx - length of the constant w/o 1st and last chars
  Mov  EDx,ECx        // EDx - Stored residual buffer size
  Inc  ESi
  Mov  EBp, ESi       // EBp - stored pointer to 2nd char of constant

{$ifdef MMX}
  MovD MM4, EAx       // store EAx in MM buffer
{$endif}

@@Scan:
{$ifdef MMX}
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PCMPGTD   MM7, MM5    // make a dword mask
  PUNPCKHBW MM7, MM7    // mix 2 dwords into a qword
  MovD      EAx, MM7    // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi,8           // correct index
  Sub   ECx,8           // and buffer size
  Jns   @@Scan          // continue scan if buffer not empty
  Jmp   @@Exit_False    // otherwise - leave with no result
@@CharFound:
  MovD  EAx, MM4        // restore EAx (1st and last chars of the constant)
{$endif}
  Repne ScasB
  JNE   @@Exit_False  // no 1st char of the constant found = exit_false
  Mov   EDx, ECx      // store new residual buffer size
  Cmp   EDx, EBx      // constant still can be settled in buffer
  Ja    @@CheckLastChar  // no - exit with false result

@@Exit_False:
  Pop  EAx
  Xor  EAx,EAx        // False = (0), check delphi help for more info
  Dec  EAx
  Jmp  @@Leave_Proc

@@CheckLastChar:      // as proposed in Boyer-Mur alhorithm

  Cmp   Ah, [EDi+EBx] // check found substring with last char of the constant
  Jnz   @@Scan        // not match - continue searching

@@CompareWholeString:

  Mov  ECx, Ebx       // ECx - constant chars amount
  shr  ECx, 2         // ECx - constant chars amount div 4
  jz   @@SkipDTest
  Repe CmpsD
  Jnz  @@SkipBTest
@@SkipDTest:
  Mov  ECx, Ebx
  and  ECx, 3
  jz   @@Constant_Exists // two least significant bits
                      // of amount are zero so the Constant matches
  Repe CmpsB
  Jz   @@Constant_Exists
@@SkipBTest:

  Mov  ESi, EBp       // restore pointer to 2nd char of constant
  Add  EDx, ECx
  Sub  EDx, EBx       // correct residual buffer size
  Mov  ECx, EDx       // restore residual buffer size to ECx

  Jnz  @@Scan

@@Constant_Exists:
  Pop  EAx
  Sub  EAx, EDx

@@Leave_Proc:
{$ifdef MMX}
  EMMS                // finish mmx operations
{$endif}

  Pop  EBp
  Pop  EBx
  Pop  ESi
  Pop  EDi
End;


 
Defunct ©   (2004-09-29 03:36) [113]

> - А мне не нужно лучше, мне нужно чтоб вы за... (замучились) !

Отож простые задачи - неитересны, и даже скучны.


 
Defunct ©   (2004-09-29 04:30) [114]

Так работает немножко быстрее, на 0.5-1%

  ...
{$ifdef MMX}
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PCMPGTD   MM7, MM5    // make a dword mask
  PUNPCKHWD MM7, MM7    // mix 2 dwords into a qword
  ...
  ...
   
{$endif}

  Repne ScasB
{$ifndef MMX}
  JNE   @@Exit_False  // no 1st char of the constant found = exit_false
{$endif}
  ...


>> меня не интересуют другие ошибки кроме "файл не найден"
>> так как логически там их быть не может, кроме случая - ошибка
>> ОС

> Может и не интересуют, но они могут в принципе быть.

> Ну вот допустим памяти не хватило. Тогда файл не найден.
> А он не найден потому что его нет или памяти нехватило, или
> ошибка чтения файла - угадай-ка ? :)


PS: GuruAV как вам рассказик [102]? там все сказано об этом.


 
Defunct ©   (2004-09-29 04:50) [115]

> Ну могут быть и не оптимальны, т.к. они и не особо стремятся - они вероятно такой фигнёй не страдают.

Вот потому все серьезные проекты, которые требуют скорости и пишутся на C++, а компилируются или MS Visual Studio .Net или интеловский компилятором.


 
Игорь Шевченко ©   (2004-09-29 10:22) [116]


> Вот потому все серьезные проекты, которые требуют скорости
> и пишутся на C++, а компилируются или MS Visual Studio .Net
> или интеловский компилятором.


Лажу гнать не надо, а ?


 
Defunct ©   (2004-09-29 18:14) [117]

Игорь Шевченко ©   (29.09.04 10:22) [116]

в чем лажа?


 
GuAV ©   (2004-09-29 22:10) [118]


> PS: GuruAV как вам рассказик [102]? там все сказано об этом.

Не понял что Вы хотели им сказать. Однако
[101] и
> Может и не интересуют, но они могут в принципе быть.

> Ну вот допустим памяти не хватило. Тогда файл не найден.
> А он не найден потому что его нет или памяти нехватило, или
> ошибка чтения файла - угадай-ка ? :)

в силе.

Defunct ©   (29.09.04 03:23) [112]
c00l !
Напишите ещё и {$else} и выделите не MMX код туда, а то совсем прочитать не могу


 
Defunct ©   (2004-09-29 23:16) [119]

> Напишите ещё и {$else} и выделите не MMX код туда, а то совсем прочитать не могу

Не, там все должно быть как есть. с помощью MMX делается только определение наличия символа в QWORD, дальше repne scas просматривает только цепочку из 8-ми байт.

Из старого кода выбрасывается только одна команда, смотреть поправку [114] секция {$ifndef}

Уберете {define MMX} будет работать старая функция, оставите - будет сканироваться по 8 байт.


 
Sha ©   (2004-09-30 09:24) [120]

> Defunct

Попробуй написать аналог Борландовской Pos на базе своей функции и померяйся с вот этим:

//(c) John O"Harrow
function PosJOH_MMX(const SubStr : AnsiString; const Str : AnsiString) : Integer;
asm
 test      eax, eax
 jz        @NotFoundExit    {Exit if SurStr = ""}
 test      edx, edx
 jz        @NotFound        {Exit if Str = ""}
 mov       ecx, [edx-4]     {Length(Str)}
 cmp       [eax-4], 1       {Length SubStr = 1?}
 je        @SingleChar      {Yes - Exit via CharPos}
 jl        @NotFound        {Exit if Length(SubStr) < 1}
 sub       ecx, [eax-4]     {Subtract Length(SubStr), -ve handled by
CharPos}
 add       ecx, 1           {Number of Chars to Check for 1st Char}
 push      esi              {Save Registers}
 push      edi
 push      ebx
 push      ebp
 mov       esi, eax         {Start Address of SubStr}
 mov       edi, ecx         {Initial Remainder Count}
 mov       eax, [eax]       {AL = 1st Char of SubStr}
 mov       ebp, edx         {Start Address of Str}
 mov       ebx, eax         {Maintain 1st Search Char in BL}
@StrLoop:
 mov       eax, ebx         {AL  = 1st char of SubStr}
 mov       ecx, edi         {Remaining Length}
 push      edx              {Save Start Position}
 call      @CharPos         {Search for 1st Character}
 pop       edx              {Restore Start Position}
 test      eax, eax         {Result = 0?}
 jz        @StrExit         {Exit if 1st Character Not Found}
 mov       ecx, [esi-4]     {Length SubStr}
 add       edx, eax         {Update Start Position for Next Loop}
 sub       edi, eax         {Update Remaining Length for Next Loop}
 sub       ecx, 1           {Remaining Characters to Compare}
@StrCheck:
 mov       al, [edx+ecx-1]  {Compare Next Char of SubStr and Str}
 cmp       al, [esi+ecx]
 jne       @StrLoop         {Different - Return to First Character Search}
 sub       ecx, 1
 jnz       @StrCheck        {Check each Remaining Character}
 mov       eax, edx         {All Characters Matched - Calculate Result}
 sub       eax, ebp
@StrExit:
 pop       ebp              {Restore Registers}
 pop       ebx
 pop       edi
 pop       esi
 ret
@NotFound:
 xor       eax, eax         {Return 0}
@NotFoundExit:
 ret
@SingleChar:
 mov       al, [eax]        {Search Character}
@CharPos:
 CMP       ECX, 8
 JG        @@NotSmall
@@Small:
 or        ecx, ecx
 jle       @@NotFound       {Exit if Length <= 0}
 CMP       AL, [EDX]
 JZ        @Found1
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+1]
 JZ        @Found2
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+2]
 JZ        @Found3
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+3]
 JZ        @Found4
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+4]
 JZ        @Found5
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+5]
 JZ        @Found6
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+6]
 JZ        @Found7
 DEC       ECX
 JZ        @@NotFound
 CMP       AL, [EDX+7]
 JZ        @Found8
@@NotFound:
 XOR       EAX, EAX
 RET
@Found1:
 MOV       EAX, 1
 RET
@Found2:
 MOV       EAX, 2
 RET
@Found3:
 MOV       EAX, 3
 RET
@Found4:
 MOV       EAX, 4
 RET
@Found5:
 MOV       EAX, 5
 RET
@Found6:
 MOV       EAX, 6
 RET
@Found7:
 MOV       EAX, 7
 RET
@Found8:
 MOV       EAX, 8
 RET

@@NotSmall:                  {Length(Str) > 8}
 MOV       AH, AL
 ADD       EDX, ECX
 MOVD      MM0, EAX
 PUNPCKLWD MM0, MM0
 PUNPCKLDQ MM0, MM0
 PUSH      ECX              {Save Length}
 NEG       ECX
@@First8:
 MOVQ      MM1, [EDX+ECX]
 ADD       ECX, 8
 PCMPEQB   MM1, MM0         {Compare All 8 Bytes}
 PACKSSWB  MM1, MM1         {Pack Result into 4 Bytes}
 MOVD      EAX, MM1
 TEST      EAX, EAX
 JNZ       @@Matched        {Exit on Match at any Position}
 CMP       ECX, -8          {Check if Next Loop would pass String End}
 JGE       @@Last8
@@Align:                     {Align to Previous 8 Byte Boundary}
 LEA       EAX, [EDX+ECX]
 AND       EAX, 7           {EAX -> 0 or 4}
 SUB       ECX, EAX
@@Loop:
 MOVQ      MM1, [EDX+ECX]
 ADD       ECX, 8
 PCMPEQB   MM1, MM0         {Compare All 8 Bytes}
 PACKSSWB  MM1, MM1         {Pack Result into 4 Bytes}
 MOVD      EAX, MM1
 TEST      EAX, EAX
 JNZ       @@Matched        {Exit on Match at any Position}
 CMP       ECX, -8          {Check if Next Loop would pass String End}
{$IFNDEF NoUnroll}
 JGE       @@Last8
 MOVQ      MM1, [EDX+ECX]
 ADD       ECX, 8
 PCMPEQB   MM1, MM0         {Compare All 8 Bytes}
 PACKSSWB  MM1, MM1         {Pack Result into 4 Bytes}
 MOVD      EAX, MM1
 TEST      EAX, EAX
 JNZ       @@Matched        {Exit on Match at any Position}
 CMP       ECX, -8          {Check if Next Loop would pass String End}
{$ENDIF}
 JL        @@Loop
@@Last8:
 MOVQ      MM1, [EDX-8]     {Position for Last 8 Used Characters}
 POP       EDX              {Original Length}
 PCMPEQB   MM1, MM0         {Compare All 8 Bytes}
 PACKSSWB  MM1, MM1         {Pack Result into 4 Bytes}
 MOVD      EAX, MM1
 TEST      EAX, EAX
 JNZ       @@Matched2       {Exit on Match at any Position}
 EMMS
 RET                        {Finished - Not Found}
@@Matched:                   {Set Result from 1st Match in EDX}
 POP       EDX              {Original Length}
 ADD       EDX, ECX
@@Matched2:
 EMMS
 SUB       EDX, 8           {Adjust for Extra ADD ECX,8 in Loop}
 TEST      AL, AL
 JNZ       @@MatchDone      {Match at Position 1 or 2}
 TEST      AH, AH
 JNZ       @@Match1         {Match at Position 3 or 4}
 SHR       EAX, 16
 TEST      AL, AL
 JNZ       @@Match2         {Match at Position 5 or 6}
 SHR       EAX, 8
 ADD       EDX, 6
 JMP       @@MatchDone
@@Match2:
 ADD       EDX, 4
 JMP       @@MatchDone
@@Match1:
 SHR       EAX, 8           {AL <- AH}
 ADD       EDX, 2
@@MatchDone:
 XOR       EAX, 2
 AND       EAX, 3           {EAX <- 1 or 2}
 ADD       EAX, EDX

end;


 
Игорь Шевченко ©   (2004-09-30 10:18) [121]

Defunct ©   (29.09.04 18:14) [117]

В процитированном


 
Romkin ©   (2004-09-30 10:31) [122]

Sha ©  (30.09.04 09:24) [120] Бррр...
Может, так достаточно? http://delphibase.endimus.ru/?action=viewfunc&topic=strsearch&id=10271
Интересно, и насколько отстает?


 
Sha ©   (2004-09-30 16:44) [123]

Romkin ©   (30.09.04 10:31) [122]

Вот и мне интересно, насколько Defunct отстанет.
Думаю, в среднем по разным длинам, отстанет прилично.

А в 2 слишним раза перекрыть RTL можно и на Паскале:

function PosShaPas(const SubStr: AnsiString; const Str: AnsiString): Integer;
var
 len, lenSub: integer;
 ch: char;
 p, pSub, pStart, pEnd: pchar;
label
 Ret, Ret0, Ret1, Next0, Next1;
begin;
 p:=pointer(Str);
 pSub:=pointer(SubStr);

 //if you need pure Pascal uncomment this paragraph
 //and comment out the next 3 paragraphs
{
 len:=length(Str);
 lenSub:=length(SubStr);
 pEnd:=p+len;
 pStart:=p;
 pEnd:=pEnd-lenSub;
 if (len<=0) or (lenSub<=0) or (p>pEnd) then begin;
   Result:=0;
   exit;
   end;
}

 if (p=nil) or (pSub=nil) then begin;
   Result:=0;
   exit;
   end;

 len:=pinteger(p-4)^;
 lenSub:=pinteger(pSub-4)^;
 if (len<lenSub) or (lenSub<=0) then begin;
   Result:=0;
   exit;
   end;

 pEnd:=p+len;
 pStart:=p;
 pEnd:=pEnd-lenSub;

 ch:=pSub[0];

 if lenSub=1 then begin;
   repeat;
     if ch=p[0] then goto Ret0;
     if ch=p[1] then goto Ret1;
     p:=p+2;
     until p>pEnd;
   Result:=0;
   exit;
   end;

 repeat;
   if ch=p[0] then begin;
     len:=lensub;
     repeat;
       if psub[len-1]<>p[len-1] then goto Next0;
       if psub[len-2]<>p[len-2] then goto Next0;
       len:=len-2;
       until len<2;
     goto Ret0;
Next0:end;

   if ch=p[1] then begin;
     len:=lensub;
     repeat;
       if psub[len-1]<>p[len] then goto Next1;
       if psub[len-2]<>p[len-1] then goto Next1;
       len:=len-2;
       until len<2;
     goto Ret1;
Next1:end;

   p:=p+2;
   until p>pEnd;
 Result:=0;
 exit;

Ret1:
 inc(pEnd);
 p:=p+2;
 if p<=pEnd then goto Ret;
 Result:=0;
 exit;
Ret0:
 inc(p);
Ret:
 Result:=p-pStart;
 end;


 
Defunct ©   (2004-09-30 21:05) [124]

Sha ©   (30.09.04 09:24) [120]

Пока не не адаптировал фукнкцию к Ansi String, но к приведенной вами функции - Respect.

строка SubString = "Test Stringx" - 12 симоволов.
5.978/ 4.153 / 3.900 ([120]/[123]/[112]).

строка SubString = "Test String" - 11 симоволов.
6.278/ 4.753 / 4.085 ([120]/[123]/[112]).

Но это и понятно, там рассмотрены почти все частные случаи.
Попробую и своего зверька [112] привести к нормальному виду.

To Sha: За счет чего достигается такое быстродействие в [123]?


 
GuAV ©   (2004-09-30 21:20) [125]

У меня быстрее [120], [123] немного медленнее.
на коротком файле (autoecex.bat) быстрее всего [112].

Celeron 2000, win9x.

не ММХ - ф-ция ваще в пролёте.


 
Defunct ©   (2004-09-30 21:37) [126]

Defunct ©   (30.09.04 21:05) [124]
опечатка:

строка SubString = "Test Stringx" - 12 симоволов.
5.978/ 4.153 / 3.900 ([112]/[120]/[123]).

строка SubString = "Test String" - 11 симоволов.
6.278/ 4.753 / 4.085 ([112]/[120]/[123]).


 
Sha ©   (2004-09-30 22:22) [127]

> Defunct ©   (30.09.04 21:05) [124]
> За счет чего достигается такое быстродействие в [123]?

За счет алгоритма, основанного на использовании внутреннего представления AnsiString, двухкратного развертывания циклов поиска первого совпадения и сравнения последующих символов, а также маленькой хитрости - изменения направления сравнения.

Так что дело тут, как видишь, совсем не в выборе языка :)


 
Defunct ©   (2004-09-30 22:24) [128]

С вот таким вот изменением, скорость [112] сравнялась с [120]

@@Scan:
{$ifdef MMX}
  MovD      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
//   PCMPGTD   MM7, MM5    // make a dword mask
//   PUNPCKHWD MM7, MM7    // mix 2 dwords into a qword
  MovD      EAx, MM7    // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi,4           // correct index
  Sub   ECx,4           // and buffer size
  Jns   @@Scan          // continue scan if buffer not empty
  Jmp   @@Exit_False    // otherwise - leave with no result
@@CharFound:
  MovD  EAx, MM4        // restore EAx (1st and last chars of the constant)
{$endif}
  Repne ScasB
{$ifndef MMX}
  JNE   @@Exit_False  // no 1st char of the constant found = exit_false
{$endif}


Я даже не думал, что MovQ работает настолько медленней чем 2 MovD
[123] действительно получается в пролете.

строка SubString = "Test String" - 11 симоволов.
4.078/4.085/4.753  ([112 с указанным изменением]/[120]/[123])

Просмотренный буфер - 2.5Gb (читался порциями по 1Mb, файл 25Mb, 100 раз подряд).


 
Defunct ©   (2004-09-30 22:26) [129]

> а также маленькой хитрости - изменения направления сравнения.

О, стоит применить к [112] ;)


 
Sha ©   (2004-09-30 22:27) [130]

> Defunct ©   (30.09.04 22:24) [128]

Дай полный код своего аналога Pos. Я его завтра потестирую.


 
GuAV ©   (2004-09-30 22:40) [131]


> Пока не не адаптировал фукнкцию к Ansi String

для своих тестов я так сделал:

Function DefunctMMX(const Constant: string;
const Buffer: string): Integer; Assembler;
Asm
 Push EDi
 Push ESi
 Push EBx
 Push EBp
// a fix to make this fcn Pos compatible
 MOV  ECX, [EDX-4] // should not affect perfomance much
 Push ECx            // store total buffer size for later calculation pos


 
Defunct ©   (2004-09-30 23:22) [132]

> Дай полный код своего аналога Pos. Я его завтра потестирую.

unit Search;

interface
{$define MMX}

Function ConstantPos(const Constant, Buffer: string): Integer;

implementation

Function ConstantPos(const Constant, Buffer: string): Integer; Assembler;
Asm
  Push EDi
  Push ESi
  Push EBx
  Push EBp
  MOV  ESI, EAX       // ESi pointer to constant
  Mov  EDi, EDx       // EDi pointer to buffer
  Mov  ECx, [EDi-4]
  Push ECx            // store total buffer size for later calculation pos
  Mov  EBX, [ESI-4]
  Dec  EBX
  Mov  Al, [ESi]      // Store first char of the constant in AL
{$ifdef MMX}
  Mov  Ah, Al         // Spreading char to 32bit register
  Push Ax
  Push Ax
  Pop  EAx
  MovD MM6, EAx
  PSLLQ MM6,MM6       // Spreading char to mmx register
  MovD MM6, EAx

  PXOR  MM5,MM5       // Z mask for later calculations
{$endif}
  Mov  Ah, [ESi+EBx]  // Store last char of the constant in AH
  Dec  EBx            // EBx - length of the constant w/o 1st and last chars
  Mov  EDx,ECx        // EDx - Stored residual buffer size
  Inc  ESi
  Mov  EBp, ESi       // EBp - stored pointer to 2nd char of constant

{$ifdef MMX}
  MovD MM4, EAx       // store EAx in MM buffer
{$endif}

@@Scan:
{$ifdef MMX}
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PCMPEQD   MM7, MM5    // make a dword mask
  PCMPEQD   MM7, MM5    // invert dword mask
  PSLLQ     MM7,16      // mix 2 dwords into a qword
  MovD      EAx, MM7    // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi,8           // correct index
  Sub   ECx,8           // and buffer size
  Jns   @@Scan          // continue scan if buffer not empty
  Jmp   @@Exit_False    // otherwise - leave with no result
@@CharFound:
  MovD  EAx, MM4        // restore EAx (1st and last chars of the constant)
{$endif}
  Repne ScasB
{$ifndef MMX}
  JNE   @@Exit_False  // no 1st char of the constant found = exit_false
{$endif}
  Mov   EDx, ECx      // store new residual buffer size
  Cmp   EDx, EBx      // constant still can be settled in buffer
  Ja    @@CheckLastChar  // no - exit with false result

@@Exit_False:
  Pop  EAx
  Xor  EAx,EAx        // False = (0), check delphi help for more info
  Dec  EAx
  Jmp  @@Leave_Proc

@@CheckLastChar:      // as proposed in Boyer-Mur alhorithm

  Cmp   Ah, [EDi+EBx] // check found substring with last char of the constant
  Jnz   @@Scan        // not match - continue searching

@@CompareWholeString:

  Mov  ECx, Ebx       // ECx - constant chars amount
  shr  ECx, 2         // ECx - constant chars amount div 4
  jz   @@SkipDTest
  Repe CmpsD
  Jnz  @@SkipBTest
@@SkipDTest:
  Mov  ECx, Ebx
  and  ECx, 3
  jz   @@Constant_Exists // two least significant bits
                      // of amount are zero so the Constant matches
  Repe CmpsB
  Jz   @@Constant_Exists
@@SkipBTest:

  Mov  ESi, EBp       // restore pointer to 2nd char of constant
  Add  EDx, ECx
  Sub  EDx, EBx       // correct residual buffer size
  Mov  ECx, EDx       // restore residual buffer size to ECx

  Jnz  @@Scan

@@Constant_Exists:
  Pop  EAx
  Sub  EAx, EDx

@@Leave_Proc:
{$ifdef MMX}
  EMMS                // finish mmx operations
{$endif}

  Pop  EBp
  Pop  EBx
  Pop  ESi
  Pop  EDi
End;

end.


 
Defunct ©   (2004-09-30 23:46) [133]

Вот более достоверные результаты (просмотр 25Gb):

[132] - 42.230 сек
[120] - 38.800 сек
[123] - 46.831 сек

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

2 Sha, если вашу функцию сделать в MMX исполнении, она поидее побъет всех ;) т.к. [132] без MMX - 65.453


 
Sha ©   (2004-09-30 23:53) [134]

> Defunct

1. Функция не является полным аналогом Pos.
Не будет работать для пустых строк, т.к. за длиной строки полезет по адресу (0-4)=FFFFFFFC.

2. Не корректно замерять скорость Pos на файлах или на буфере длиной 2Gb. Погрешности за счет дисковых очень велики и не предсказуемы.


 
GuAV ©   (2004-09-30 23:58) [135]

Sha ©   (30.09.04 23:53) [134]
Какие размеры для замера предлагаете?
имхо 0, 128 байт, 1 кБ и 1 МБ.


 
Sha ©   (2004-10-01 00:12) [136]

> Defunct

3. Оригинальная Pos из RTL в случае неуспеха возвращает 0, твоя -1:
@@Exit_False:
 Pop  EAx
 Xor  EAx,EAx        // False = (0), check delphi help for more info
 Dec  EAx
 Jmp  @@Leave_Proc


4. Оригинальная Pos никогда не тестирует символы, идущие за терминатором. Твоя тестирует, что теоретически может вызвать AV.


 
Sha ©   (2004-10-01 00:16) [137]

> GuAV ©   (30.09.04 23:58) [135]
> Какие размеры для замера предлагаете?

Предлагаю использовать неплохой (хотя и не без нереканий) вариант
http://dennishomepage.gugs-cats.dk/PosBV45.zip


 
Sha ©   (2004-10-01 00:38) [138]

> Defunct

5. Похоже, твоя функция иногда будет выдавать неверный результат (следствие пункта 4):
ConstantPos(#0,"Мы находим даже терминатор")>0


 
Defunct ©   (2004-10-01 02:25) [139]

Ок, со всем здесь услышанным, подправил [132], так, что возвращается "0" если строка не найдена, подправлена работа со строками нулевой длинны, и плюс сделал выравнивание адреса как в [120] (спасибо за пример [120]).
Ну теперь получается по скорости, на некоторых строках выигрывает новая функция, на некоторых - [120]. По времени, приблизительно получилось следующее:

на разных строках функция [120] (1.25Gb пересмотр).
результат от 19.069 сек до 19.984.

новая функция тестировалась в тех же условиях (1.25Gb пересмотр)
результат от 18.768 сек до 20.364.

Буфер поиска - 1Mb.

Следует отметить неоспоримое преимущество приведенной здесь функции, она в отличие от [120] способна работать на процессорах у которых нет MMX.

unit Search;

interface
{$define MMX}

Function ConstantPos(const Constant, Buffer: string): Integer;

implementation

Function ConstantPos(const Constant, Buffer: string): Integer; Assembler;
Asm
  Push EDi
  Push ESi
  Push EBx
  Push EBp
  MOV  ESI, EAX       // ESi pointer to constant
  Mov  EDi, EDx       // EDi pointer to buffer
  Mov  ECx, [EDi-4]
  Push ECx            // store total buffer size for later calculation pos
  Test ECx,ECx
  Jz   @@Exit_False
  Mov  EBX, [ESI-4]
  Test EBx,EBx
  Jz   @@Exit_False
  Dec  EBX
  Mov  Al, [ESi]      // Store first char of the constant in AL
{$ifdef MMX}
  Mov  Ah, Al         // Spreading char to 32bit register
  MovD MM6, EAx
  PUNPCKLWD MM6, MM6
  PUNPCKLDQ MM6, MM6
  PXOR  MM5,MM5       // Z mask for later calculations
  Dec  EBx            // EBx - length of the constant w/o 1st and last chars
  Mov  Ax,[ESi+EBx]
  Dec  EBx
{$endif}

{$ifndef MMX}
  Mov  Ah, [ESi+EBx]  // Store last char of the constant in AH
{$endif}
  Mov  EDx,ECx        // EDx - Stored residual buffer size
  Inc  ESi
  Mov  EBp, ESi       // EBp - stored pointer to 2nd char of constant

{$ifdef MMX}
  MovD MM4, EAx       // store EAx in MM buffer
{$endif}

@@Scan:
{$ifdef MMX}

  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PACKSSWB  MM7, MM7
  MovD  EAx, MM7        // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi, 8
  Mov   EAx, Edi
  And   EAx, 7
  Sub   EDi,EAx         // Aligning index
  Sub   ECx,8
  Add   ECx,EAx         // and correcting buffer size
  Js    @@Exit_False

@@Scan2:
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PACKSSWB  MM7,MM7
  MovD  EAx, MM7        // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi,8           // correct index
  Sub   ECx,8           // and buffer size

  Jns   @@Scan2         // continue scan if buffer not empty
  Jmp   @@Exit_False    // otherwise - leave with no result
@@CharFound:
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  MovD      EAx, MM7
  Test      EAx, EAx
  Jnz       @@Loop1
  PSRLQ     MM7,32
  MovD      EAx, MM7
@@Loop1:
  inc   EDi
  dec   Ecx
  Shr   EAx,8
  Jz    @@ContinueHere
  jmp   @@Loop1

{$endif}
  Repne ScasB
{$ifndef MMX}
  JNE   @@Exit_False  // no 1st char of the constant found = exit_false
{$else}
@@ContinueHere:
  MovD  EAx, MM4        // restore EAx (1st and last chars of the constant)
{$endif}

@@CheckLastChar:      // as proposed in Boyer-Mur alhorithm

{$ifndef MMX}
  Cmp   Ah, [EBx+Edi] // check found substring with last char of the constant
{$else}
  Cmp   Ax, [EBx+EDi]
{$endif}
  Jz    @@CompareWholeString  // not match - continue searching
  Jmp   @@Scan

@@Exit_False:
  Pop  EAx
  Xor  EAx,EAx        // 0 if constant was not found
  Jmp  @@Leave_Proc

@@CompareWholeString:
  Mov   EDx, ECx      // store new residual buffer size
  Cmp   EDx, EBx      // constant still can be settled in buffer
  Jna   @@Exit_False   // no - exit with false result

  Mov  ECx, Ebx       // ECx - constant chars amount
  shr  ECx, 2         // ECx - constant chars amount div 4
  jz   @@SkipDTest
  Repe CmpsD
  Jnz  @@SkipBTest
@@SkipDTest:
  Mov  ECx, Ebx
  and  ECx, 3
  jz   @@Constant_Exists // two least significant bits
                      // of amount are zero so the Constant matches
  Repe CmpsB
  Jz   @@Constant_Exists
@@SkipBTest:

  Mov  ESi, EBp       // restore pointer to 2nd char of constant
  Add  EDx, ECx
  Sub  EDx, EBx       // correct residual buffer size
  Mov  ECx, EDx       // restore residual buffer size to ECx

  Jnz  @@Scan

@@Constant_Exists:
  Pop  EAx
  Sub  EAx, EDx

@@Leave_Proc:
{$ifdef MMX}
  EMMS                // finish mmx operations
{$endif}

  Pop  EBp
  Pop  EBx
  Pop  ESi
  Pop  EDi
End;

end.


На будущее думаю попробовать сделать поиск через SSE/SSE2

> 4. Оригинальная Pos никогда не тестирует символы, идущие за терминатором. Твоя тестирует, что теоретически может вызвать AV.

ни старая, ни исправленная функции не выходили за границу буфера.


 
Defunct ©   (2004-10-01 02:28) [140]

> [139] (1.25Gb пересмотр).

не там точку поставил.

12.5Gb пересмотр.


 
Defunct ©   (2004-10-01 03:14) [141]

посмотрел тест [137] недостаток теста в том, что он был нацелен именно, на поиск подстроки в строке. Функция же [139] больше нацелена на поиск подстроки в файле, т.е. предполагается просмотр не одного буфера а немкольких. Поэтому по одному тесту скорости у нее высокая оценка (SubBench2), а по второму (SubBench1) - низкая, суммарная оценка получалась лучше чем RTL/RTLa/b но значительно хуже чем у PosJoh и PosSha, сейчас внесу поправки с учетом изменения задачи.


 
Defunct ©   (2004-10-01 06:17) [142]

> Sha

Провозился всю ночь с этой функцией, выкладывать пока не буду так как еще не доделал. Есть вопрос, что в вашем тесте означает SubBench1 и SubBench2?

В изначальном варианте [139] было:
SubBench1 = 261
SubBench2 = 159

Сейчас почемуто получается:
SubBench1 = 295
SubBench2 = 121

Суммарный Bench индекс практически не изменился все на том же уровне 410-420.

В функциях PosJoh_MMX и PosSha наблюдаю примерно такую картину:
SubBench1 = 177, 180
SubBench2 = 147, 152

SubBench1 относится к маленьким строкам я правильно понимаю?


 
Sha ©   (2004-10-01 09:44) [143]

> Defunct

> Буфер поиска - 1Mb.
Многовато будет :) Наиболее частое применение Pos - поиск первого вхождения в строке до 100 символов. В случае  успеха в среднем результат поиска не превышает 40. Именно для этих условий и должна проводиться оптимизация. Для других условий пишутся другие функции, которые сравнивать с Pos будет неверно.

> Следует отметить неоспоримое преимущество приведенной здесь
> функции, она в отличие от [120] способна работать на
> процессорах у которых нет MMX.
По приведенной мной ссылке ты можешь найти реализации Pos на любой вкус.

> ни старая, ни исправленная функции не выходили за границу буфера.
Извини, значит, я был недостаточно внимателен. Тогда пункты 4 и 5 снимаются.

> посмотрел тест [137] недостаток теста в том, что он был
> нацелен именно, на поиск подстроки в строке. Функция же [139]
> больше нацелена на поиск подстроки в файле, т.е.
> предполагается просмотр не одного буфера а немкольких
Вот это я совсем не понял.

> SubBench1 относится к маленьким строкам я правильно понимаю?
Поиск малых строк среди малых строк (первые 35 тестов).


 
Defunct ©   (2004-10-01 10:27) [144]

> посмотрел тест [137] недостаток теста в том, что он был
> нацелен именно, на поиск подстроки в строке. Функция же [139]
> больше нацелена на поиск подстроки в файле, т.е.
> предполагается просмотр не одного буфера а немкольких

Имел в виду, что тест не полностью отражает возможности функций. Не учитывается скорость поиска в больших см. сабж до 1Gb строках.

> Поиск малых строк среди малых строк (первые 35 тестов).
Я так и думал. Крутил и так и сяк, на малых строках существенно проигрываю, PosJoh и PosSha. (в PosJoh заметил ведется отдельная обработка маленьких строк)

Зато на больших строках получилось обогнать все функции:
просматриваемый буфер делится на 2 кадра, в каждом кадре просмотр ведется в двух направлениях.

> Для других условий пишутся другие функции, которые сравнивать с Pos будет неверно.

согласен, но так или иначе, даже с оптимизацией совсем под другую задачу (сабж) результат сравнения с Pos тоже не плох, индекс 416 (как я понял в 3 раза быстрее стандартной Pos) по вашему тесту дают не многие функции.


 
Sha ©   (2004-10-01 10:41) [145]

> Defunct ©   (01.10.04 10:27) [144]
> Зато на больших строках получилось обогнать все функции

Наверное, точнее так: на больших строках с образом в конце.
Не забывай, что просто для таких строк Джон еще не написал свою функцию :)

PS. Твой результат действительно очень хорош.
Не желаешь принять участие в FastCode?


 
Defunct ©   (2004-10-01 11:44) [146]

> Не желаешь принять участие в FastCode?

на тему?


 
Sha ©   (2004-10-01 11:52) [147]

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


 
Defunct ©   (2004-10-01 12:01) [148]

> Тем там много...
это хорошо

> Кстати, было бы неплохо выложить здесь окончательный вариант.
Не вопрос, как только закончу, и тест покажет что функция прошла Validate тест сразу выложу.


 
GuAV ©   (2004-10-01 20:32) [149]


> функция прошла Validate

вот, например
const S1="AV"; S2="Guru AV";

procedure TForm1.Button1Click(Sender: TObject);
begin
 Caption := Format("Sha = %d, Pos = %d, defunct = %d",
     [PosShaPas(S1, S2), Pos(S1, S2), ConstantPos(S1, S2)]);
// Sha = 6, Pos = 6, defunct = 0
end;


 
Alekc   (2004-10-01 20:38) [150]

Кто-то спрашивал почему у борланда такие неоптимальные функции: если тут столько народу одну Pos так долго оптимизирует, то сколько б времени у Borland"ы ушло на всё RTL/VCL ? 10 лет ? :о)


 
Defunct ©   (2004-10-02 01:50) [151]

> Sha

Я не понимаю, что я делаю не так, для маленьких строк никак не могу улучшить показания теста. Привожу функцию, которая прошла все 11 Validate тестов. Гляньте, может заметите, что там не так при маленьких строках. На больших строках эта функция побила все, в т.ч. и PosJohMMX_a.

Function ConstantPos(const Constant, Buffer: string): Integer; Assembler;
Asm
  Test eax, eax
  Jz   @@NotFound_Exit    {Exit if SubStr = ""}
  Test edx, edx
  Jz   @@NotFound_Exit    {Exit if Str = ""}
  Mov  ECx, [EDx-4]
  Test ECx, ECx
  Jz   @@NotFound_Exit
  Cmp  [EAx-4],1
  Jnz   @@WideSearch

  Cmp   ECx,8
  Ja    @@WideScan

// CharPos
@@Scan4:
  Mov  Al,[EAx]
  Push EDi
  Add  EDx, ECx
  Xchg EDi, EDx
  Mov  EDx, ECx
  Not  ECx
  Inc  ECx

@@SLoop4:
  Cmp  AL, [EDi+ECx]
  Jz   @@Found4
  Inc  ECx
  Jnz  @@SLoop4
  Xor  EAx, EAx
  Pop  EDi
  Ret

@@Found4:
  Mov  EAx, EDx
  Add  EAx, ECx
  Inc  EAx
  Pop  EDi
  Ret

@@WideScan:
  Mov  Al,[EAx]
  Mov  Ah, Al         // Spreading char to 32bit register
  MovD MM6, EAx
  PUNPCKLWD MM6, MM6
  PUNPCKLDQ MM6, MM6

  Push ECx
@@Scan5:
  MovQ      MM7, [EDx]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PACKSSWB  MM7, MM7
  MovD  EAx, MM7        // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound2    // yes - 1st char of the constant was found
  Add   EDx,8           // correct index
  Sub   ECx,8           // and buffer size

  Jns   @@Scan5         // continue scan if buffer not empty

@@Exit_False2:
  Emms
  Pop   EAx
  Xor   EAx, EAx
  Ret

@@CharFound2:
  MovQ      MM7, [EDx]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  MovD  EAx, MM7
  Test  EAx, EAx
  Jnz   @@Loop2
  Sub   ECx,4
  PSRLQ MM7,32
  MovD  EAx, MM7
@@Loop2:
  Dec   Ecx
  Test  Al,Al
  Js    @@Check2
  Shr   EAx,8
  Jmp   @@Loop2

@@Check2:
  Test  ECx,ECx
  Js    @@Exit_False2
  Emms
  Pop   EAx
  Sub   EAx, ECx
  Ret

@@WideSearch:

  Push EDi
  Push ESi
  Push EBx
  Push EBp

  MOV  ESI, EAX       // ESi pointer to constant
  Mov  EDi, EDx       // EDi pointer to buffer
  Push ECx            // store total buffer size for later calculation pos
  Mov  EBX, [ESI-4]
  Test EBx, EBx
  Jz   @@Exit_False
  Mov  Al, [ESi]      // Store first char of the constant in AL
  Cmp  EBx, ECx
  Ja   @@Exit_False
  Jnz  @@Normal_Work
  Cmp  AL, [Edi]
  Jnz  @@Exit_False

@@Normal_Work:
  Dec  EBX

  Mov  Ah, [ESi+EBx]  // Store last char of the constant in AH
  Dec  EBx
  Mov  EDx,ECx        // EDx - Stored residual buffer size
  Inc  ESi
  Mov  EBp, ESi       // EBp - stored pointer to 2nd char of constant
  Cmp  ECx, 8
  Ja   @@Wide_String

@@Scan3:
  Xor  ECx, ECx
@@SLoop:
  Cmp  AL, [Edi+ECx]
  Jz   @@Found
  Inc  ECx
  Cmp  ECx, EDx
  Jb   @@SLoop
  Jmp  @@Exit_False

@@Found:
  Inc  ECx
  Add  EDi,ECx
  Sub  EDx, ECx
  Mov  ECx, EDx
  Cmp  EBx, EDx
  Jge  @@Exit_False
  Jmp  @@CheckLastChar

{}

@@Wide_String:
  MovD MM4, EAx       // store EAx in MM buffer
  Mov  Ah, Al         // Spreading char to 32bit register
  MovD MM6, EAx
  PUNPCKLWD MM6, MM6
  PUNPCKLDQ MM6, MM6
  PXOR  MM5,MM5       // Z mask for later calculations

@@Scan:
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  PACKSSWB  MM7,MM7
  MovD  EAx, MM7        // get mixed dword
  Test  EAx, EAx        // was there at least 1 bit set?
  Jnz   @@CharFound     // yes - 1st char of the constant was found
  Add   EDi,8           // correct index
  Sub   ECx,8           // and buffer size

  Jns   @@Scan          // continue scan if buffer not empty

  Jmp   @@Exit_False    // otherwise - leave with no result
@@CharFound:
  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  MovD  EAx, MM7
  Test  EAx, EAx
  Jnz   @@Loop1
  Add   EDi,4
  Sub   ECx,4
  PSRLQ MM7,32
  MovD  EAx, MM7
@@Loop1:
  inc   EDi
  dec   Ecx
  Test  Al,Al
  Js    @@Check
  Shr   EAx,8
  Jmp   @@Loop1

@@Check:
  MovD  EAx, MM4        // restore EAx (1st and last chars of the constant)

@@CheckLastChar:       // as proposed in Boyer-Mur alhorithm
  Mov   EDx, ECx      // store new residual buffer size
  Test  EDx, EDx
  Js    @@Exit_False
  Test  EBx, EBx
  Js    @@Constant_Exists

  Cmp   Ah, [EBx+Edi] // check found substring with last char of the constant
  Jz    @@CompareWholeString  // not match - continue searching
  Cmp   EDx,8
  Jnb   @@Scan
  Jmp   @@Scan3

@@Exit_False:
  Pop  EAx
  Xor  EAx,EAx        // False = (0), check delphi help for more info
  Jmp  @@Leave_Proc

@@NotFound_Exit:
  Xor  EAx, EAx
  Ret

@@CompareWholeString:
  Test  EBx, EBx
  Jz    @@Constant_Exists
  Cmp   EDx, EBx      // constant still can be settled in buffer
  Jna   @@Exit_False   // no - exit with false result

  Push EDi
  Mov  ECx, Ebx       // ECx - constant chars amount
  shr  ECx, 2         // ECx - constant chars amount div 4
  jz   @@SkipDTest
  Repe CmpsD
  Jnz  @@SkipBTest
@@SkipDTest:
  Mov  ECx, Ebx
  and  ECx, 3
  jz   @@Constant_Exists2 // two least significant bits
                      // of amount are zero so the Constant matches
  Repe CmpsB
  Jz   @@Constant_Exists2

@@SkipBTest:

  Pop  EDi
  Mov  ESi, EBp       // restore pointer to 2nd char of constant
//   Add  EDx, ECx
//   Sub  EDx, EBx       // correct residual buffer size
  Mov  ECx, EDx       // restore residual buffer size to ECx

  Cmp  EDx,8
  Jb   @@Scan3
  Jmp  @@Scan

@@Constant_Exists2:
  Add  ESp, 4

@@Constant_Exists:
  Pop  EAx
  Sub  EAx, EDx

@@Leave_Proc:
  EMMS                // finish mmx operations

  Pop  EBp
  Pop  EBx
  Pop  ESi
  Pop  EDi
End;


 
Sha ©   (2004-10-02 11:18) [152]

>Defunct ©   (02.10.04 01:50) [151]

1. Главное, что отличает функцию-победитель от других - очень быстрая выдача результата в случае нахождения образца в первых 8 позициях после начала сканирования первого символа. Анализ этих позиций вынесен из цикла и проводится очень быстро. Это позволяет отложить подготовительные действия для организации цикла (и сам цикл) до тех пор пока они действительно не потребуются, или иногда вообще их не выполнять.
В результате, даже, если даже цикл и будет выполняться, то будет сэкономлено несколько команд перехода и насыщение цикла наступит позднее.
Также имей ввиду, что использование MMX для строк короче 20 сиволов неоправданно из-за больших расходов на подготовку.

2. В следующем цикле
@@SLoop4:
 Cmp  AL, [EDi+ECx]
 Jz   @@Found4
 Inc  ECx
 Jnz  @@SLoop4

имеет смысл "inc ecx" заменить на "add ecx,1". После такой замены состояние регистра флагов для второго сравнения будет полностью определяться предыдущей командой, а не формироваться как результат выполнения cmp и inc.
Еще лучше развернуть цикл в 2 или 4 раза.

3. Этот цикл тоже лучше развернуть.
@@SLoop:
 Cmp  AL, [Edi+ECx]
 Jz   @@Found
 Inc  ECx
 Cmp  ECx, EDx
 Jb   @@SLoop


 
GuAV ©   (2004-10-02 15:33) [153]

так имхо немного быстрее будет CompareWholeString -
поиск в обратном порядке.
@@CompareWholeString:
 Test  EBx, EBx
 Jz    @@Constant_Exists
 Cmp   EDx, EBx      // constant still can be settled in buffer
 Jna   @@Exit_False   // no - exit with false result

//  Push  EDi

 MOV   ECX, EBX
 @@CW_Loop:
 SUB   ECX, 4
 JZ    @@CW_LastDWord
 JL    @@CW_Last123bytes
 MOV   EAX, [ESi+ECx]
 CMP   EAX, [EDi+ECx]
 JNE   @@SkipBTest
 JMP   @@CW_Loop

 @@CW_LastDWord:
 MOV   EAX, [ESi]
 CMP   EAX, [EDi]
 JNE   @@SkipBTest
 JMP   @@Constant_Exists

 @@CW_Last123bytes:

 ADD   ECX, 2
 JZ    @@Last2
 JNS   @@Last3
 JS    @@Last1

 @@Last3:
 MOV   AX, [ESi]
 CMP   AX, [EDi]
 JNE   @@SkipBTest
 MOV   AL, [ESi+2]
 CMP   AL, [EDi+2]
 JNE   @@SkipBTest
 JMP   @@Constant_Exists

 @@Last2:
 MOV   AX, [ESi]
 CMP   AX, [EDi]
 JNE   @@SkipBTest
 JMP   @@Constant_Exists

 @@Last1:
 MOV   AX, [ESi]
 CMP   AX, [EDi]
 JNE   @@SkipBTest
 JMP   @@Constant_Exists

@@SkipBTest:

// Pop  EDi
//  Mov  ESi, EBp       // restore pointer to 2nd char of constant
//   Add  EDx, ECx
//   Sub  EDx, EBx       // correct residual buffer size
 Mov  ECx, EDx       // restore residual buffer size to ECx

 Cmp  EDx,8
 Jb   @@Scan3
 Jmp  @@Scan

@@Constant_Exists:
 Pop  EAx
 Sub  EAx, EDx


 
GuAV ©   (2004-10-02 15:43) [154]


>  @@Last1:
>  MOV   AX, [ESi]
>  CMP   AX, [EDi]
>  JNE   @@SkipBTest
>  JMP   @@Constant_Exists

MOV   AL, [ESi] // Of course a byte-sized operand is meant
CMP   AL, [EDi] // That"s a kinda ctrl-C+ctrl-V eror :)


 
Sha ©   (2004-10-02 18:31) [155]

> GuAV

Сейчас я сижу за K6 и не могу проверить скорость на P4,
но два перехода подряд приводят к потерям тактов.


 
GuAV ©   (2004-10-02 18:34) [156]

Sha ©   (02.10.04 18:31) [155]

ок. не знал. у меня целерон. ща оптимизирую.


 
GuAV ©   (2004-10-02 18:50) [157]

Вот... тестить ща не могу... могут быть ошибки..
@@CompareWholeString:
 Test  EBx, EBx
 Jz    @@Constant_Exists
 Cmp   EDx, EBx      // constant still can be settled in buffer
 Jna   @@Exit_False   // no - exit with false result

//  Push  EDi

 MOV   ECX, EBX
 @@CW_Loop:
 SUB   ECX, 4
 JL    @@CW_Last123bytes
 MOV   EAX, [ESi+ECx]
 JZ    @@CW_LastDWord  // SUB ECX, 4 result was 0

 CMP   EAX, [EDi+ECx]
 JE   @@CW_Loop
 Mov   ECx, EDx
 JMP   @@SkipBTest1

 @@CW_LastDWord:
// MOV   EAX, [ESi]
 CMP   EAX, [EDi]
 JE    @@Constant_Exists
 Mov   ECx, EDx
 JMP   @@SkipBTest1

 @@CW_Last123bytes:

 ADD   ECX, 2
 JS    @@Last1
// JNS   @@Last3

 @@Last3:
 MOV   AX, [ESi]
 JZ    @@Last2
 CMP   AX, [EDi]
 JNE   @@SkipBTest
 MOV   AL, [ESi+2]
 CMP   AL, [EDi+2]
 JE   @@Constant_Exists
 Mov  ECx, EDx
 JMP   @@SkipBTest1

 @@Last2:
// MOV   AX, [ESi]
 CMP   AX, [EDi]
 JE   @@Constant_Exists
 Mov  ECx, EDx
 JMP   @@SkipBTest1

 @@Last1:
 MOV   AL, [ESi]
 CMP   AL, [EDi]
 //JNE   @@SkipBTest
 JE   @@Constant_Exists

@@SkipBTest:

// Pop  EDi
//  Mov  ESi, EBp       // restore pointer to 2nd char of constant
//   Add  EDx, ECx
//   Sub  EDx, EBx       // correct residual buffer size
 Mov  ECx, EDx       // restore residual buffer size to ECx

@@SkipBTest1:   //  Mov  ECx, EDx   moved to places to jump from

 Cmp  EDx,8
 Jb   @@Scan3
 Jmp  @@Scan


 
GuAV ©   (2004-10-03 00:34) [158]

@@WideScan:
 Mov  Al,[EAx]
 Mov  Ah, Al         // Spreading char to 32bit register
 MovD MM6, EAx
 PUNPCKLWD MM6, MM6
 PUNPCKLDQ MM6, MM6

 Push ECx
@@Scan5:
 MovQ      MM7, [EDx]  // load a part of buffer that needs to be scaned
 PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
 PACKSSWB  MM7, MM7
 MovD  EAx, MM7        // get mixed dword
 Test  EAx, EAx        // was there at least 1 bit set?
 Jnz   @@CharFound2    // yes - 1st char of the constant was found
 Add   EDx,8           // correct index
 Sub   ECx,8           // and buffer size

 Jns   @@Scan5         // continue scan if buffer not empty


тут, в слуаче ECx<8 выход за пределы буфера, причём аж до 7 байт.

при Validate test надо делать как нибудь чтобы нельзя было читать из S[Length(S)+1], где S это и Constant и Buffer
У меня сейчас две идеи: или Break Point на чтение данных или VirtualProtect...

возможно с WideSearch то же, этого кода я вообще не понял...


 
Sha ©   (2004-10-03 08:33) [159]

GuAV ©   (03.10.04 00:34) [158]

Я делал проверку через VirtualProtect в одном из Challenge, не помню в каком. Надо порыться... Позже...


 
Sha ©   (2004-10-03 11:35) [160]

> GuAV ©   (03.10.04 00:34) [158]
> тут, в слуаче ECx<8 выход за пределы буфера, причём аж до 7 байт.

Я тут по такому случаю написал еще одну процедуру проверки.
Поразительно, но данный тест ConstantPos проходит.
Разберусь в понедельник если время будет, сейчас еду на дачу.

//Protect/unprotect 4k-block at the end of string
procedure SetStringProtected(var s: string; var flags: dword);
const
 block= 4*1024;
var
 p, q: pchar;
begin
 if (flags and PAGE_NOACCESS)<>0
 then begin;
   SetLength(s,3*block);
   FillChar(s[1],Length(s),0);
   end;

 p:=pointer(s);
 integer(q):=(integer(p) and -block) + 2*block;

 if (flags and PAGE_NOACCESS)<>0
 then pinteger(p-4)^:=(q-p)-1  //Trunk the string
 else pinteger(p-4)^:=3*block; //Restore string length

 VirtualProtect(pchar(q),1,flags,@flags); //Set protection flags
 end;

function TMainForm.Validate0: boolean;
var
 s: string;
 p: pchar;
 q: pointer;
 flags: dword;
 len: integer;
begin;
 flags:=PAGE_NOACCESS;
 SetStringProtected(s, flags);
 try
   len:=Length(s); //Now Length(s)>4k.
   s[len-0]:="h";
   s[len-1]:="g";
   s[len-2]:="f";
   s[len-3]:="e";
   s[len-4]:="d";
   s[len-5]:="c";
   s[len-6]:="b";
   s[len-7]:="a";
   Result:=(PosFunction("abcdefgh", s)=len-7)
       and (PosFunction("bcdefgh", s)=len-6)
       and (PosFunction("cdefgh", s)=len-5)
       and (PosFunction("defgh", s)=len-4)
       and (PosFunction("efgh", s)=len-3)
       and (PosFunction("fgh", s)=len-2)
       and (PosFunction("gh", s)=len-1)
       and (PosFunction("h", s)=len-0)
       and (PosFunction("abcdefg9", s)=0)
       and (PosFunction("bcdefg9", s)=0)
       and (PosFunction("cdefg9", s)=0)
       and (PosFunction("defg9", s)=0)
       and (PosFunction("efg9", s)=0)
       and (PosFunction("fg9", s)=0)
       and (PosFunction("g9", s)=0)
       and (PosFunction("9", s)=0)
       and (PosFunction("9h", s)=0)
       and (PosFunction("9gh", s)=0)
       and (PosFunction("9fgh", s)=0)
       and (PosFunction("9efgh", s)=0)
       and (PosFunction("9defgh", s)=0)
       and (PosFunction("9cdefgh", s)=0)
       and (PosFunction("9bcdefgh", s)=0);
   //Construct the string inplace.
   p:=pchar(pointer(s))+(len+1-8); //Set address
   pInteger(p-4)^:=7; //Set length
   pInteger(p-8)^:=1; //Set reference count
   q:=p;
   Result:=Result
       and (PosFunction("bcdefgh", string(q))=1)
       and (PosFunction("cdefgh", string(q))=2)
       and (PosFunction("defgh", string(q))=3)
       and (PosFunction("efgh", string(q))=4)
       and (PosFunction("fgh", string(q))=5)
       and (PosFunction("gh", string(q))=6)
       and (PosFunction("h", string(q))=7)
       and (PosFunction("bcdefg9", string(q))=0)
       and (PosFunction("cdefg9", string(q))=0)
       and (PosFunction("defg9", string(q))=0)
       and (PosFunction("efg9", string(q))=0)
       and (PosFunction("fg9", string(q))=0)
       and (PosFunction("g9", string(q))=0)
       and (PosFunction("9", string(q))=0)
       and (PosFunction("9h", string(q))=0)
       and (PosFunction("9gh", string(q))=0)
       and (PosFunction("9fgh", string(q))=0)
       and (PosFunction("9efgh", string(q))=0)
       and (PosFunction("9defgh", string(q))=0)
       and (PosFunction("9cdefgh", string(q))=0);
 except
   Result:=false;
   end;
 SetStringProtected(s, flags);

 if Result then begin;
   ErrorEdit0.Text := "Passed Validate0";
   ErrorEdit0.Color := clGreen;
  end
 else begin;
   ErrorEdit0.Text := "Failed Validate0";
   ErrorEdit0.Color := clRed;
  end;
 Update;
 end;


 
GuAV ©   (2004-10-03 12:46) [161]

Ха. этот тест функция Defunct проходит.
Нужно запретить к части данных находящейся не по смещению кратному 8 от начала строки. Лучше даже не кратному 4.


 
Sha ©   (2004-10-04 00:39) [162]

GuAV ©   (03.10.04 12:46) [161]

По смещению 4 тоже проходит. Можешь убедиться, если добавишь такой код:

   p:=pchar(pointer(s))+(len+1-4);
   pInteger(p-4)^:=3;
   pInteger(p-8)^:=1;
   q:=p;
   Result:=Result
       and (PosFunction("fgh", string(q))=1)
       and (PosFunction("gh", string(q))=2)
       and (PosFunction("h", string(q))=3)
       and (PosFunction("fg9", string(q))=0)
       and (PosFunction("g9", string(q))=0)
       and (PosFunction("9", string(q))=0)
       and (PosFunction("9h", string(q))=0)
       and (PosFunction("9gh", string(q))=0)

А доступ по смещению, не кратному 4, запретить нельзя. В Wintel можно запретить доступ к одному или нескольким выровненным на границу 4k блокам размером 4k.
Кроме того, ANSI-строки всегда располагаются менеджером памяти Delphi выровненными на границу слова. Можно конечно вручную сконструировать невыровненную строку, но вряд ли это будет честно :)


 
GuAV ©   (2004-10-04 02:22) [163]


> Можно конечно вручную сконструировать невыровненную строку,
> но вряд ли это будет честно :)

А разве выходить за буфер честно ? :)
попробовал жетскую систему допинг котнтроля.
Оказывается, даже если нет 0-терминатора все функции её проходят кроме Defunct и ShaPas. Если есть 0-терминатор, то ShaPas проходит, но Defunct - нет. AV именно там где я ожидал. Вот код:
const
block= 4*1024;

type
 TBlocks = array[0..2] of array[0..block-1] of Byte;

function ProtectedStringCreate(const Source: string): string;
var
 L: Cardinal;
 Blocks, AlBlocks: ^TBlocks;
begin
 New(Blocks);
 AlBlocks:=pointer(integer(@Blocks[1]) and not $FFF);
 Result:="";
 L:=Length(Source);
 Pointer(Result):=@(AlBlocks^[1, 0]);
 Dec(integer(Result), L);
 PDWORD(integer(Result)-8)^:=1; // Fake Ref Count;
 PDWORD(integer(Result)-4)^:=L; // Length
 PPointer(integer(Result)-12)^:=Blocks; // Unaligned ptr 2 free
 VirtualProtect(@AlBlocks[1, 0], block, PAGE_NOACCESS, PDWORD(integer(Result)-16)^);
 Move(Source[1], PDWORD(integer(Result))^, L);
 // PBYTE(integer(Result)+L)^:=0; // attempt to write null-terminator should not work
end;

procedure ProtectedStringFree(var S: string);
var
 Blocks: ^TBlocks;
begin
 Blocks:=PPointer(integer(S)-12)^;
 VirtualProtect(@Blocks[1], block, PDWORD(integer(S)-16)^, nil);
 FreeMem(Blocks);
 Pointer(S):=nil;
end;

function TMainForm.Validate0: boolean;
var
s: string;
p: pchar;
q: pointer;
flags: dword;
len: integer;
begin;
s:=ProtectedStringCreate("12345678abcdefg");
try
  Result:=(PosFunction("abcdefg", s)=1+8)
      and (PosFunction("bcdefg", s)=2+8)
      and (PosFunction("cdefg", s)=3+8)
      and (PosFunction("defg", s)=4+8)
      and (PosFunction("efg", s)=5+8)
      and (PosFunction("fg", s)=6+8)
      and (PosFunction("g", s)=7+8)
      and (PosFunction("", s)=0)
      and (PosFunction("abcdef9", s)=0)
      and (PosFunction("bcdef9", s)=0)
      and (PosFunction("cdef9", s)=0)
      and (PosFunction("def9", s)=0)
      and (PosFunction("ef9", s)=0)
      and (PosFunction("f9", s)=0)
      and (PosFunction("9", s)=0)
      and (PosFunction("", s)=0)
      and (PosFunction("9", s)=0)
      and (PosFunction("9g", s)=0)
      and (PosFunction("9fg", s)=0)
      and (PosFunction("9efg", s)=0)
      and (PosFunction("9defg", s)=0)
      and (PosFunction("9cdefg", s)=0)
      and (PosFunction("9bcdefg", s)=0);
except
  Result:=false;
  end;
ProtectedStringFree(s);

if Result then begin;
  ErrorEdit0.Text := "Passed Validate0";
  ErrorEdit0.Color := clGreen;
 end
else begin;
  ErrorEdit0.Text := "Failed Validate0";
  ErrorEdit0.Color := clRed;
 end;
Update;
end;


 
Sha ©   (2004-10-04 11:04) [164]

> GuAV ©   (04.10.04 02:22) [163]
> Оказывается, даже если нет 0-терминатора все функции её проходят кроме Defunct и ShaPas.

Отсутствие терминатора означает нарушение формата ANSI-строки.
Сама Delphi считает, что он обязан присутствовать.
Попробуй, например, выполнить такой код:

procedure TMainForm.Button1Click(Sender: TObject);
var
 s: string;
begin
 s:="abc";
 s[4]:="d";
 ShowMessage(s);
end;


>> Можно конечно вручную сконструировать невыровненную строку,
>> но вряд ли это будет честно :)
> А разве выходить за буфер честно ? :)

В коде RTL присутствует неявное предположение о выравнивании ANSI-строк на границу.
Во первых менеджер памяти выделяет выровненную память, а префикс строки объявлен следующим образом:

type
 PStrRec = ^StrRec;
 StrRec = packed record
   refCnt: Longint;
   length: Longint;
 end;

Во-вторых, в коде _LStrCmp из System.pas есть совершенно замечательный кусок:

@@cmpRest:
       POP     EDX
       AND     EDX,3
       JE      @@equal

       MOV     ECX,[ESI]
       MOV     EBX,[EDI]
       CMP     CL,BL
       JNE     @@exit
       DEC     EDX
       JE      @@equal
       CMP     CH,BH
       JNE     @@exit
       DEC     EDX
       JE      @@equal
       AND     EBX,$00FF0000
       AND     ECX,$00FF0000
       CMP     ECX,EBX
       JNE     @@exit

Этот кусок используется для сравнения хвостов строк. Можно заметить, что если длина строки равна 4*N+1, то для сравнения одного последнего байта Delphi считывает из памяти двойное слово. Ясно, что этот код безопасен только в том случае, если строки выровнены на границу dword и что вполне допустимо читать двойное слово, содержащее последний значимый байт (не терминатор). Доступ к самому терминатору (и даже следующему за ним байту), естественно, тоже допустим иначе, как создать ANSI-строку).
Только не удалось пока уличить Delphi в доступе ко второму слову в двойном слове, начинающемся с терминатора. Но раз выделено, наверное тоже можно. Хотя ни одна из тестирумых программ этого не делает.

Так что не удастся тебе стать святее Папы Римского:
твой тест не прошла бы даже Delphi RTL :)


 
Суслик ©   (2004-10-04 11:07) [165]


> а префикс строки объявлен следующим образом:
>
> type
>  PStrRec = ^StrRec;
>  StrRec = packed record
>    refCnt: Longint;
>    length: Longint;
>  end;

в пятом дельфи в префиксе еще 4 байта есть.


 
Sha ©   (2004-10-04 11:09) [166]

читать
(и даже следующему за ним байту для wide-строк)


 
Sha ©   (2004-10-04 11:13) [167]

> Суслик ©   (04.10.04 11:07) [165]
> в пятом дельфи в префиксе еще 4 байта есть.

Это длина выделенного блока. Она в 6-ой просто не описана, т.к. при манипуляции со строками не используется. Но она там, естественно осталась, и хакеры могут ее использовать.


 
Суслик ©   (2004-10-04 11:15) [168]


>  [167] Sha ©   (04.10.04 11:13)


> Это длина выделенного блока.

Т.е. в этих 4х байтах хранится кол-во выделенных байт плюс к тем 4ем байтам, которые в заголовке выделенного блока хранит дельфовый менеджер памяти?


 
Суслик ©   (2004-10-04 11:19) [169]


>  Но она там, естественно осталась, и хакеры могут ее использовать.

Вот это мне не понятно. Что значит осталась? Судя по СPU под эти 4 байта память не выделяется.


 
Sha ©   (2004-10-04 11:41) [170]

> Суслик ©   (04.10.04 11:19) [169]

Да, действительно, не выделяется.
Похоже, был не прав.
Память подвела :)


 
Суслик ©   (2004-10-04 11:50) [171]


>  [170] Sha ©   (04.10.04 11:41)
> > Суслик ©   (04.10.04 11:19) [169]
>
> Да, действительно, не выделяется.
> Похоже, был не прав.
> Память подвела :)

Мы об этом недавно поспорили с АП - оба оказались правы - в Д5 есть 4 байта, в Д6 - нет. Потому и удивлен вашему ответу.


 
Defunct ©   (2004-10-04 12:21) [172]

Зы. померял свою функцию [151] на P2, без последних изменений внесенных GuraAV, поимела все функции по Overall Bechmark"у.

ps: насчет выхода за границу буфера ничего страшного в этом, хотя раз уж зашла речь об этом вечером посмотрю, что можно сделать. И выложу новый вариант.


 
kaZaNoVa ©   (2004-10-04 12:32) [173]

2алл - плиз, выложите окончательный вариант функции - замены POS  ;)
- а так у меня в памяти POS пашет ~120 мб/сек .. :))
при скорости винта 20~30 мб/сек, имхо даже у POS потрясающее быстродействие %)))


 
GuAV ©   (2004-10-04 14:25) [174]


> Так что не удастся тебе стать святее Папы Римского:
> твой тест не прошла бы даже Delphi RTL :)

Я же сказал, все проходят, кроме Вашей и Defunct, и даже ShowMessage работает! Попробуйте сами.
Defunct ©   (04.10.04 12:21) [172]
А Вы всё же попробуйте применить их. Ещё быстрее будет. И мой код не читает за буфером.


 
Sha ©   (2004-10-04 14:37) [175]

> GuAV ©   (04.10.04 14:25) [174]

Ты не понял, речь о том, что валидацию с твоим вариантом ProtectedStringCreate [163] не пройдет Delphi RTL, а потому он неверный.


 
Sha ©   (2004-10-04 14:51) [176]

Иными словами, если сама Delphi при работе с ANSI-строками предполагает, что они выровнены на границу двойного слова, читает и записывает терминатор, читает последнее двойное слово, содержащее символы строки, то почему это должно быть запрещено для сторонних строковых функций?


 
GuAV ©   (2004-10-04 19:45) [177]

Sha ©   (04.10.04 14:51) [176]

Ок. с нуль терминатором Ваша проходит, но Defunct нет.
Pos из RTL работает.
Что не будет работать искать не буду, поверю на слово.


> они выровнены на границу двойного слова

у Defunct предполагается, что на границу 8. Как Вам это ?

Будет ли тогда мой тест корректным если дать выравинвание на дв. слово но не на четверное и добавить нуль терминатор?


 
GuAV ©   (2004-10-04 21:19) [178]

Этио создаст DWORD-aligned строку с 0 теминатором, однако фция Defunct не пройдёт
const
block= 4*1024;

type
 TBlocks = array[0..2] of array[0..block-1] of Byte;

function ProtectedStringCreate(const Source: string): string;
var
 L: Cardinal;
 Blocks, AlBlocks: ^TBlocks;
begin
 New(Blocks);
 AlBlocks:=pointer(integer(@Blocks[1]) and not $FFF);
 Result:="";
 L:=Length(Source);
 Pointer(Result):=@(AlBlocks^[1, 0]);
 Dec(integer(Result), L+1);
 PDWORD(integer(Result)-8)^:=1; // Fake Ref Count;
 PDWORD(integer(Result)-4)^:=L; // Length
 PPointer(integer(Result)-12)^:=Blocks; // Unaligned ptr 2 free
 VirtualProtect(@AlBlocks[1, 0], block, PAGE_NOACCESS, PDWORD(integer(Result)-16)^);
 Move(Source[1], PDWORD(integer(Result))^, L);
 PBYTE(integer(Result)+L)^:=0;
 ShowMessage(result);
end;

procedure ProtectedStringFree(var S: string);
var
 Blocks: ^TBlocks;
begin
 Blocks:=PPointer(integer(S)-12)^;
 VirtualProtect(@Blocks[1], block, PDWORD(integer(S)-16)^, nil);
 FreeMem(Blocks);
 Pointer(S):=nil;
end;

function TMainForm.Validate0: boolean;
var
s: string;
p: pchar;
q: pointer;
flags: dword;
len: integer;
begin;
s:=ProtectedStringCreate("1234abcdefg");
try
  Result:=(PosFunction("abcdefg", s)=1+4)
      and (PosFunction("bcdefg", s)=2+4)
      and (PosFunction("cdefg", s)=3+4)
      and (PosFunction("defg", s)=4+4)
      and (PosFunction("efg", s)=5+4)
      and (PosFunction("fg", s)=6+4)
      and (PosFunction("g", s)=7+4)
      and (PosFunction("", s)=0)
      and (PosFunction("abcdef9", s)=0)
      and (PosFunction("bcdef9", s)=0)
      and (PosFunction("cdef9", s)=0)
      and (PosFunction("def9", s)=0)
      and (PosFunction("ef9", s)=0)
      and (PosFunction("f9", s)=0)
      and (PosFunction("9", s)=0)
      and (PosFunction("", s)=0)
      and (PosFunction("9", s)=0)
      and (PosFunction("9g", s)=0)
      and (PosFunction("9fg", s)=0)
      and (PosFunction("9efg", s)=0)
      and (PosFunction("9defg", s)=0)
      and (PosFunction("9cdefg", s)=0)
      and (PosFunction("9bcdefg", s)=0);
except
  Result:=false;
  end;
ProtectedStringFree(s);

if Result then begin;
  ErrorEdit0.Text := "Passed Validate0";
  ErrorEdit0.Color := clGreen;
 end
else begin;
  ErrorEdit0.Text := "Failed Validate0";
  ErrorEdit0.Color := clRed;
 end;
Update;
end;


здесь Guru AV выделил место AV :

 Mov  Ah, Al         // Spreading char to 32bit register
 MovD MM6, EAx
 PUNPCKLWD MM6, MM6
 PUNPCKLDQ MM6, MM6
 PXOR  MM5,MM5       // Z mask for later calculations

@@Scan:
 MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
 PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
 PACKSSWB  MM7,MM7
 MovD  EAx, MM7        // get mixed dword
 Test  EAx, EAx        // was there at least 1 bit set?
 Jnz   @@CharFound     // yes - 1st char of the constant was found
 Add   EDi,8           // correct index
 Sub   ECx,8           // and buffer size

 Jns   @@Scan          // continue scan if buffer not empty

 Jmp   @@Exit_False    // otherwise - leave with no result


 
Defunct ©   (2004-10-04 21:27) [179]

> у Defunct предполагается, что на границу 8. Как Вам это ?

Важна скорость или синтетический AV?
Если вынести проверку вперед (перед чтением, а не после как сейчас), тогда потеряется скорость из-за дергания индекса. Немного ускорил функцию еще на пару процентов, за счет чтения DWORD для маленьких строк, и раскрутки циклов.

2 Sha:
Inc работает быстрее чем Add Reg,1
Inc Inc  - быстрее чем Add Reg,2
а вот 3 Inc будет уже медленнее чем Add Reg,3

> Будет ли тогда мой тест корректным если дать выравинвание на дв. слово но не на четверное и добавить нуль терминатор?

вопрос - зачем?

сравните мою раскрутку для маленьких строк, с раскруткой от John [120]:

@@Scan4:
  Mov  Ch,[EAx]
// Ch - char
// EDx - pointer to buffer
// cl - length
@@CharPos:
  Mov  EAx,[EDx]

  Cmp  Ch,Al
  Jz   @Found1
  cmp  Ch,Ah
  Jz   @Found2
  Shr  EAx,16
  Cmp  Ch,Al
  Jz   @Found3
  Cmp  Ch,Ah
  Jz   @Found4

  Cmp  Cl,4
  Jna  @NotFound
  Mov  EAx,[EDx+4]

  Cmp  Ch, Al
  Jz   @Found5
  Cmp  Ch, Ah
  Jz   @Found6
  Shr  EAx, 16
  Cmp  Ch, Al
  Jz   @Found7
  Cmp  Ch, Ah
  Jz   @Found8

@NotFound:
  XOr  EAx, EAx
  Ret
@Found1:
  Mov  EAx,1
  Jmp  @Validation
@Found2:
  Mov  EAx,2
  Jmp  @Validation
@Found3:
  Mov  EAx,3
  Jmp  @Validation
@Found4:
  Mov  EAx,4
  Jmp  @Validation
@Found5:
  Mov  EAx,5
  Jmp  @Validation
@Found6:
  Mov  EAx,6
  Jmp  @Validation
@Found7:
  Mov  EAx,7
  Jmp  @Validation
@Found8:
  Mov  EAx,8
  Jmp  @Validation

@Validation:
  Cmp  Al,Cl
  Ja   @NotFound
  Ret


 
Defunct ©   (2004-10-04 21:36) [180]

GuAV ©   (04.10.04 21:19) [178]

Я это рассматривал, следующий вариант пройдет ваш тест, но будет работать медленнее

@@Scan:

  Cmp       ECx, 8
  Jb        @@Scan3

  MovQ      MM7, [Edi]  // load a part of buffer that needs to be scaned
  ...


 
Defunct ©   (2004-10-04 21:41) [181]

[180]
И еще уберете лишний Jump, Jmp в конце цикла установить на начало.

вместо Jns @@Scan - написать Jmp @@Scan, Jmp @@Exit_False выбросить.

только так функция, будет работать на пару процентов медленнее.


 
Defunct ©   (2004-10-04 21:55) [182]

Еще один забавный факт.. не знаю даже как его проекоментировать.
Новая функция работает быстрее по обеим тестам (как для маленьких строк, так и для больших) причем с приличным отрывом (на 10% быстрее чем PosJoh-MMX для малых строк и 20% - для больших, в 4.5 раза быстрее чем PosRTL) чем все функции в тесте на процессорах:
P-MMX
P2-MMX
P3-Coppermine
Celeron (P3)-Coppermine

НО, САМОЕ ИНТЕРЕСНОЕ, НА P4-HT функция почему-то проигрывает примерно на 10% на малых строках, а на больших строках отрыв сократился до 5-7% в сравнении с PosJoh-MMX.

Видно что-то я не учел именно под P4..


 
Sha ©   (2004-10-04 22:24) [183]

> GuAV ©   (04.10.04 19:45) [177]
> Будет ли тогда мой тест корректным если дать выравинвание на
> дв. слово но не на четверное и добавить нуль терминатор?

Да.


 
GuAV ©   (2004-10-04 22:53) [184]

Defunct ©   (04.10.04 21:36) [180]
Таких мест у Вас несколько.

Defunct ©   (04.10.04 21:55) [182]

> Новая функция работает быстрее по обеим тестам

Какая именно ?


 
Defunct ©   (2004-10-04 22:59) [185]

> Какая именно ?

[151] с раскрытыми циклами.


 
Sha ©   (2004-10-04 23:00) [186]

> Defunct ©   (04.10.04 21:27) [179]

> Важна скорость или синтетический AV?

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

> Inc работает быстрее чем Add Reg,1
> Inc Inc  - быстрее чем Add Reg,2
> а вот 3 Inc будет уже медленнее чем Add Reg,3

Раньше действительно так было. На современных процессорах inc и add работают одинаково быстро. Однако, add часто предпочтительнее, если надо устранить зависимость регистра флагов от результата предыдущей команды. Обычно это требуется когда inc или dec - предпоследняя команда в цикле.

> сравните мою раскрутку для маленьких строк, с раскруткой от John [120]

На P4 сдвиг - сравнительно медленная U-команда, загрузка - быстрая (UV).
Кроме того, сдвиг подразумевает зависимость последующего кода от результата сдвига, а загрузка позволяет распараллелить выполнение за счет переназначения регистров. Так что сдвиги без необходимости использовать не стоит.

> НО, САМОЕ ИНТЕРЕСНОЕ, НА P4-HT функция почему-то проигрывает

Джон как раз оптимизирует свои программы под P4. Советую приглядеться к его коду.


 
Sha ©   (2004-10-04 23:37) [187]

Здесь имеется ввиду, что загрузка - пересылка память-регистр (mov reg,[mem])


 
Defunct ©   (2004-10-04 23:59) [188]

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


Одно дело ничтожная вероятность AV, другое дело синтетически созданная ситуация, имитирующая AV. Ну да ладно с этим разберусь, там только 2 места с выходом за пределы буфера максимум на 7 байт.

> На современных процессорах inc и add работают одинаково быстро.
А размер?
Add reg32, 1  (5 байт)
Inc reg       (1 байт)

> Однако, add часто предпочтительнее, если надо устранить зависимость регистра флагов от результата предыдущей команды.

О какой зависимости идет речь?

Inc/Dec Flags Affected:
The CF flag is not affected. The OF, SF, ZF, AF, and PF flags are set according to the result.

О CF помним, но в циклах, как правило, проверяются SF/ZF так что чихать на зависимоть. С учетом меньшего размера 1 байт против 5-ти, сэкономим время на загрузку другой команды.

> Кроме того, сдвиг подразумевает зависимость последующего кода от результата сдвига, а загрузка позволяет распараллелить выполнение за счет переназначения регистров. Так что сдвиги без необходимости использовать не стоит.

не хватит АЛБ чтобы все распараллелить. в P4 если мне не изменяет память 2 АЛБ (В AMD64 - 3). В раскрученных циклах идет по два сравнения, которые чудесно параллелятся AH/AL без лишних обращений к кешу данных. Этот код [179] работает быстрее чем [120] там где было 8*([cmp reg, rel32],[jz],[dec]). Зависимость от сдвига не играет роли из-за количества физических АЛБ.

Причина замедления, сразу скажу, кроется в поиске подстроки Length(SubString)>1 и Length(S)<8.


 
Sha ©   (2004-10-05 00:28) [189]

> Defunct ©   (04.10.04 23:59) [188]

>> На современных процессорах inc и add работают одинаково быстро.
> А размер?

А что размер? Мы оптимизируем скорость, а не экономим несколько байт в exe.

> О какой зависимости идет речь?
> О CF помним, но в циклах, как правило, проверяются SF/ZF так
> что чихать на зависимоть. С учетом меньшего размера 1 байт
> против 5-ти, сэкономим время на загрузку другой команды.

Ты не совсем прав. Partial flags stalls действительно не возникает в случае проверки на равенство. Однако часто проверяется и флаг С - а там уж точно задержка возникнет.
А время загрузки команды в цикле почти всегда значения не имеет.

> В раскрученных циклах идет по два сравнения, которые чудесно
> параллелятся AH/AL без лишних обращений к кешу данных.
> Зависимость от сдвига не играет роли из-за количества
> физических АЛБ.

Оба конвейера будут ждать завершения сдига. Это 3 такта.

> Этот код [179] работает быстрее чем [120] там где было
> 8*([cmp reg, rel32],[jz],[dec]).

Надо бы проверить.  

> Причина замедления, сразу скажу, кроется в поиске подстроки
> Length(SubString)>1 и Length(S)<8.

Ну так исправь - и ты победитель :)


 
GuAV ©   (2004-10-05 00:41) [190]

икто не проверил на Р4 [157] ?


 
Sha ©   (2004-10-05 00:45) [191]

Так это ж не конец :)


 
Defunct ©   (2004-10-05 02:20) [192]

> Однако часто проверяется и флаг С - а там уж точно задержка возникнет.
Помним и об этом, поэтому JA/JB после Inc/Dec никогда не используем. Только JZ/JS.

> Ну так исправь - и ты победитель :)

Дык, над этим и работаю в свободное время. ;)
Функция уже ~400 строк, начинаю малость теряться.



GuAV ©   (05.10.04 00:41) [190]
> никто не проверил на Р4 [157] ?

5 баллов GuruAV! (3-5)% ускорение на больших строках по сравнению с [151] и даже на мелких строках ускорение на лицо - (5-10)%


 
Defunct ©   (2004-10-05 04:39) [193]

Итак долгожданный победитель заезда ;)


Function ConstantPos(const Constant, Buffer: string): Integer; Assembler;
Asm
  Test eax, eax
  Jz   @@NotFound_Exit    
  Test edx, edx
  Jz   @@NotFound_Exit    
  Mov  ECx, [EDx-4]
  Test ECx, ECx
  Jz   @@NotFound_Exit
  Cmp  [EAx-4],1
  Jnz   @@WideSearch

  Cmp   ECx,8
  Ja    @@WideScan

// CharPos
@@Scan4:
  Mov  Ch,[EAx]
// Ch - char
// EDx - pointer to buffer
// cl - length
@@CharPos:
  Mov  EAx,[EDx]

  Cmp  Ch,Al
  Jz   @Found1
  cmp  Ch,Ah
  Jz   @Found2
  Shr  EAx,16
  Cmp  Ch,Al
  Jz   @Found3
  Cmp  Ch,Ah
  Jz   @Found4

  Cmp  Cl,4
  Jna  @NotFound
  Mov  EAx,[EDx+4]

  Cmp  Ch, Al
  Jz   @Found5
  Cmp  Ch, Ah
  Jz   @Found6
  Shr  EAx, 16
  Cmp  Ch, Al
  Jz   @Found7
  Cmp  Ch, Ah
  Jz   @Found8

@NotFound:
  XOr  EAx, EAx
  Ret
@Found1:
  Mov  EAx,1
  Jmp  @Validation
@Found2:
  Mov  EAx,2
  Jmp  @Validation
@Found3:
  Mov  EAx,3
  Jmp  @Validation
@Found4:
  Mov  EAx,4
  Jmp  @Validation
@Found5:
  Mov  EAx,5
  Jmp  @Validation
@Found6:
  Mov  EAx,6
  Jmp  @Validation
@Found7:
  Mov  EAx,7
  Jmp  @Validation
@Found8:
  Mov  EAx,8
  Jmp  @Validation

@Validation:
  Cmp  Al,Cl
  Ja   @NotFound
  ret

  nop
  nop                  // alignment for better performance

@@SmallScan:
  Push  ECx
  Mov   Ch,[EAx]
@@Scan8:
  Push  ECx
  Call  @@CharPos
  Pop   ECx
  Test  EAx, EAx
  Jz    @Validation3

  Sub   Cl, Al
  Pop   EAx
  Sub   Al, Cl
  Ret

@Validation3:
  Sub   Cl, 8
  Add   EDx, 8
  Cmp   Cl, 0
  Jg    @@Scan8

  Add   ESP, 4
  Ret

@@LastDword2:
  Test ECx, ECx
  Js   @@Exit_False2
  Jz   @@Exit_False2
  MovD      MM7, [EDx]  // load a part of buffer that needs to be scaned
  PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
  MovD  EAx, MM7        // get mixed dword
  Test  EAx, EAx
  Jnz   @@Loop2
  Jz    @@Exit_False2

@@WideScan:
  Cmp  ECx, 16
  Jb   @@SmallScan
  Mov  Al,[EAx]
  Mov  Ah, Al
  MovD MM6, EAx
  PUNPCKLWD MM6, MM6
  PUNPCKLDQ MM6, MM6

  Push ECx
@@Scan5:
  MovQ      MM7, [EDx]
  PCMPEQB   MM7, MM6
  PACKSSWB  MM7, MM7
  MovD  EAx, MM7
  Test  EAx, EAx
  Jnz   @@CharFound2
  Add   EDx,8
  Sub   ECx,8
  Cmp   ECx,4
  Jg    @@Scan5
  Jmp   @@LastDword2

@@Exit_False2:
  Emms
  Pop   EAx
  Xor   EAx, EAx
  Ret

@@CharFound2:
  MovQ      MM7, [EDx]
  PCMPEQB   MM7, MM6
  MovD  EAx, MM7
  Test  EAx, EAx
  Jnz   @@Loop2
  Sub   ECx,4
  PSRLQ MM7,32
  MovD  EAx, MM7
@@Loop2:
  Dec   Ecx
  Test  Al,Al
  Js    @@Check2
  Shr   EAx,8
  Jmp   @@Loop2

@@Check2:
  Test  ECx,ECx
  Js    @@Exit_False2
  Emms
  Pop   EAx
  Sub   EAx, ECx
  Ret

@Small_String:
  Cmp  ECx, [EAx-4]
  Jb   @NotFound
  Mov  Ch, [EAx]
  Push ECx
  Push EBx
  Push ESi
  Mov  ESi, EAx
  Mov  EBx,[ESi-4]
  Dec  EBx
@Scan6:
  Push ECx
@Scan7:
  Call @@CharPos
  Pop  ECx
  Test EAx, EAx
  Jz   @Validation2
  Sub  Cl, Al
  Add  EDx, EAx
  Cmp  Cl, Bl
  Jb   @NotFound2

  Push ECx
  Mov  ECx, EBx
@Loop2:
  Mov  Al, [ESi+ECx]
  Cmp  Al, [EDx+ECx-1]
  Jnz  @Scan7
  Loop @Loop2

  Pop  ECx
  Mov  Ah, 0
  Pop  ESi
  Pop  EBx
  Pop  EAx
  Sub  EAx, ECx
  ret

@Validation2:
  Sub  Cl, 8
  Add  EDx, 8
  Cmp  Cl, 0
  Jg   @Scan6

@NotFound2:
  Pop  ESi
  Pop  EBx
  Add  ESp, 4
  Xor  EAx, EAx
  Ret

  nop                // code alignment
  nop

@@WideSearch:
  Cmp  ECx,32
  Jb   @Small_String

  Push EDi
  Push ESi
  Push EBx
  Push EBp

  MOV  ESI, EAX
  Mov  EDi, EDx
  Push ECx
  Mov  EBX, [ESI-4]
  Test EBx, EBx
  Jz   @@Exit_False
  Mov  Al, [ESi]
  Cmp  EBx, ECx
  Ja   @@Exit_False
  Jnz  @@Normal_Work
  Cmp  AL, [Edi]
  Jnz  @@Exit_False

@@Normal_Work:
  Dec  EBX

  Mov  Ah, [ESi+EBx]
  Dec  EBx
  Mov  EDx,ECx
  Inc  ESi
  Mov  EBp, ESi
  Cmp  ECx, 8
  Ja   @@Wide_String

@@Scan3:
  Push EAx
  Mov  Ch, Al
  Mov  EDx, EDi
  Call @@CharPos
  Mov  Ch, 0
  Sub  ECx, EAx
  Add  EDi, EAx
  Pop  EDx
  Test EAx,EAx
  Jz   @@Exit_False
  Xchg EAx, EDx
  Jmp  @@CheckLastChar
  nop
  nop
@@LastDword:
  Test ECx, ECx
  Js   @@Exit_False
  Jz   @@Exit_False
  MovD      MM7, [Edi]
  PCMPEQB   MM7, MM6
  MovD  EAx, MM7
  Test  EAx, EAx
  Jz    @@Exit_False
  Jmp   @@Loop1
  nop
  nop                 // Aligning code for greater performance
@@Wide_String:
  MovD MM4, EAx
  Mov  Ah, Al
  MovD MM6, EAx
  PUNPCKLWD MM6, MM6
  PUNPCKLDQ MM6, MM6

@@Scan:

  MovQ      MM7, [Edi]
  PCMPEQB   MM7, MM6
  PACKSSWB  MM7, MM7
  MovD  EAx, MM7
  Test  EAx, EAx
  Jnz   @@CharFound
  Add   EDi, 8
  Mov   EAx, Edi
  And   EAx, 7
  Sub   EDi,EAx         // Aligning index
  Sub   ECx,8
  Add   ECx,EAx
  Cmp   ECx,4
  Jnge  @@LastDword

@@Scan2:
  MovQ      MM7, [Edi]
  PCMPEQB   MM7, MM6
  PACKSSWB  MM7,MM7
  MovD  EAx, MM7
  Test  EAx, EAx
  Jnz   @@CharFound
  Add   EDi,8
  Sub   ECx,8
  Cmp   ECx,4
  Jg    @@Scan
  Jmp   @@LastDword

@@CharFound:
  MovQ      MM7, [Edi]
  PCMPEQB   MM7, MM6
  MovD  EAx, MM7
  Test  EAx, EAx
  Jnz   @@Loop1
  Add   EDi,4
  Sub   ECx,4
  PSRLQ MM7,32
  MovD  EAx, MM7
@@Loop1:
  inc   EDi
  dec   Ecx
  Test  Al,Al
  Js    @@Check
  Shr   EAx,8
  Jmp   @@Loop1

@@Check:
  MovD  EAx, MM4

@@CheckLastChar:       // as proposed in Boyer-Mur alhorithm
  Mov   EDx, ECx      // store new residual buffer size
  Test  EDx, EDx
  Js    @@Exit_False
  Test  EBx, EBx
  Js    @@Constant_Exists

  Cmp   Ah, [EBx+Edi]
  Jz    @@CompareWholeString
  Cmp   EDx,4
  Jg   @@Scan
  Jmp  @@LastDWord

@@Exit_False:
  Pop  EAx
  Xor  EAx,EAx
  Jmp  @@Leave_Proc

@@NotFound_Exit:
  Xor  EAx, EAx
  Ret

@@CompareWholeString:
  Test  EBx, EBx
  Jz    @@Constant_Exists
  Cmp   EDx, EBx
  Jna   @@Exit_False

  MOV   ECX, EBX
@@CW_Loop:
  SUB   ECX, 4
  JL    @@CW_Last123bytes
  MOV   EAX, [ESi+ECx]
  JZ    @@CW_LastDWord

  CMP   EAX, [EDi+ECx]
  JE   @@CW_Loop
  Mov   ECx, EDx
  JMP   @@SkipBTest1

@@CW_LastDWord:
  CMP   EAX, [EDi]
  JE    @@Constant_Exists
  Mov   ECx, EDx
  JMP   @@SkipBTest1

@@CW_Last123bytes:

  ADD   ECX, 2
  JS    @@Last1

@@Last3:
  MOV   AX, [ESi]
  JZ    @@Last2
  CMP   AX, [EDi]
  JNE   @@SkipBTest
  MOV   AL, [ESi+2]
  CMP   AL, [EDi+2]
  JE    @@Constant_Exists
  Mov   ECx, EDx
  JMP   @@SkipBTest1

@@Last2:
  CMP   AX, [EDi]
  JE    @@Constant_Exists
  Mov   ECx, EDx
  JMP   @@SkipBTest1

@@Last1:
  MOV   AL, [ESi]
  CMP   AL, [EDi]
  JE    @@Constant_Exists

@@SkipBTest:
  Mov   ECx, EDx

@@SkipBTest1:
  Cmp   EDx,4
  Jg    @@Scan
  Jmp   @@LastDWord

@@Constant_Exists2:
  Add   ESp, 4

@@Constant_Exists:
  Pop   EAx
  Sub   EAx, EDx

@@Leave_Proc:
  EMMS
  Pop  EBp
  Pop  EBx
  Pop  ESi
  Pop  EDi
End;


 
Defunct ©   (2004-10-05 04:55) [194]

Результаты тестирования на P4-2.8


Функция         Смещ. SB1 SB2 Overall

PosDef_MMX C 194 139 333
PosShaPas_b 8 194 169 362
PosJOH_MMX_a 4 200 164 364
PosJOH_MMX_b 4 202 162 364
PosShaPas_a 4 198 167 366
PosJOH_SSE_a 4 198 175 373
PosJOH_SSE_b C 198 175 373
PosJOH_IA32_b 0 194 183 377
PosJOH_IA32_a C 197 183 380
PosJOH_SSE2_b 0 213 169 381
PosJOH_SSE2_a 4 214 169 383
PosJOH_PAS_a 4 309 255 564
PosJOH_PAS_b 0 320 250 570
PosDKC65_b 0 366 267 633
PosDKC65_a 8 394 267 661
PosRL1_b C 420 480 900
PosRTL         4 530 628 1158


Результаты тестирования на P2-400:

Функция         Смещ. SB1 SB2 Overall

PosDef_MMX C 1168 699 1867
PosJOH_IA32_b 0 1115 964 2079
PosJOH_MMX_a 4 1164 925 2089
PosJOH_MMX_b 4 1176 927 2103
PosJOH_IA32_a C 1128 989 2117
PosShaPas_a 4 1120 1345 2464
PosShaPas_b 8 1111 1386 2497
PosDKC65_b 0 1468 1319 2787
PosDKC65_a 8 1486 1336 2822
PosJOH_PAS_a 4 1512 2009 3521
PosJOH_PAS_b 0 1558 2058 3616
PosRL1_b C 1423 2904 4327
PosRTL         4 1703 3238 4940
PosRTL         4 1721 3224 4945


SB1 - Время прохождения теста работы с маленькими строками (до 8-ми символов)
SB2 - Время прохождения теста работы с большими строками (больше 8-ми символов)

Надеюсь таблица нормально запостится.


 
Defunct ©   (2004-10-05 05:06) [195]

[194] зззз.. так и знал..

Результаты тестирования на P4-2.8


Функция         Смещ. SB1   SB2 Overall

PosDef_MMX.......C....194...139...333
PosShaPas_b......8....194...169...362
PosJOH_MMX_a.....4....200...164...364
PosJOH_MMX_b.....4....202...162...364
PosShaPas_a......4....198...167...366
PosJOH_SSE_a.....4....198...175...373
PosJOH_SSE_b.....C....198...175...373
PosJOH_IA32_b....0....194...183...377
PosJOH_IA32_a....C....197...183...380
PosJOH_SSE2_b....0....213...169...381
PosJOH_SSE2_a....4....214...169...383
PosJOH_PAS_a.....4....309...255...564
PosJOH_PAS_b.....0....320...250...570
PosDKC65_b.......0....366...267...633
PosDKC65_a.......8....394...267...661
PosRL1_b.........C....420...480...900
PosRTL...........4....530...628...1158


Результаты тестирования на P2-400:


Функция         Смещ.  SB1   SB2   Overall

PosDef_MMX.......C....1168...699....1867
PosJOH_IA32_b....0....1115...964....2079
PosJOH_MMX_a.....4....1164...925....2089
PosJOH_MMX_b.....4....1176...927....2103
PosJOH_IA32_a....C....1128...989....2117
PosShaPas_a......4....1120...1345...2464
PosShaPas_b......8....1111...1386...2497
PosDKC65_b.......0....1468...1319...2787
PosDKC65_a.......8....1486...1336...2822
PosJOH_PAS_a.....4....1512...2009...3521
PosJOH_PAS_b.....0....1558...2058...3616
PosRL1_b.........C....1423...2904...4327
PosRTL...........4....1721...3224...4945


вот теперь можно спокойно идти спать с чуством выполненного долга
;)


 
Sha ©   (2004-10-05 10:42) [196]

> Defunct ©   (05.10.04 05:06) [195]

Поздравляю! Пора заново открывать PosChallenge.

Кстати, в FastCode намечается StringReplaceChallenge, можешь применить свои наработки там.


 
GuAV ©   (2004-10-05 12:19) [197]

Defunct ©   (05.10.04 05:06) [195]
Не всё так однозначно...

PosJOH_SSE_a    4  193  186  379
PosJOH_SSE_b    C  198  181  379
PosDefunct      4  220  165  385
PosJOH_MMX_b    4  203  187  390
PosJOH_MMX_a    4  203  192  395
PosJOH_SSE2_a   4  214  187  401
PosJOH_SSE2_b   0  214  187  401


ps: Celeron 2000 MHz Northwood L2=128 kB

Sha ©   (05.10.04 10:42) [196]
У них там случайно Case-unsensitive Pos нету ?


 
Sha ©   (2004-10-05 12:25) [198]

> Defunct ©   (05.10.04 04:39) [193]

Подправь свою функцию, чтобы проходила тест Validation0 (здесь учтены предложения GuAV):

procedure SetStringProtected(var s: string; var flags: dword);
const
 block= 4*1024;
var
 p, q: pchar;
begin
 if (flags and PAGE_NOACCESS)<>0 then SetLength(s,3*block);

 p:=pointer(s);
 integer(q):=(integer(p) and -block) + 2*block;

 if (flags and PAGE_NOACCESS)<>0
 then pinteger(p-4)^:=(q-p)-1
 else pinteger(p-4)^:=3*block;

 VirtualProtect(q,1,flags,@flags);
 end;

function ValidateStringProtected(var s: string; LengthToValidate: integer): boolean;
var
 len: integer;
 p: pointer;
begin;
 len:=Length(s);
 if ((LengthToValidate and 3)<>3)
 or (LengthToValidate<3)
 or (LengthToValidate>len-8) then begin;
   Result:=false;
   exit;
   end;

 FillChar(s[1],len,0);
 s[len-0]:="g";
 s[len-1]:="f";
 s[len-2]:="e";
 s[len-3]:="d";
 s[len-4]:="c";
 s[len-5]:="b";
 s[len-6]:="a";

 p:=pchar(pointer(s))+(len-LengthToValidate);
 pInteger(pchar(p)-4)^:=LengthToValidate;
 pInteger(pchar(p)-8)^:=1;

 Result:=(PosFunction("abcdef9", string(p))=0)
     and (PosFunction("bcdef9", string(p))=0)
     and (PosFunction("cdef9", string(p))=0)
     and (PosFunction("def9", string(p))=0)
     and (PosFunction("ef9", string(p))=0)
     and (PosFunction("f9", string(p))=0)
     and (PosFunction("9", string(p))=0)
     and (PosFunction("9g", string(p))=0)
     and (PosFunction("9fg", string(p))=0)
     and (PosFunction("9efg", string(p))=0)
     and (PosFunction("9defg", string(p))=0)
     and (PosFunction("9cdefg", string(p))=0)
     and (PosFunction("9bcdefg", string(p))=0)
     and (PosFunction("g", string(p))=LengthToValidate-0)
     and (PosFunction("fg", string(p))=LengthToValidate-1)
     and (PosFunction("efg", string(p))=LengthToValidate-2)
     and ((LengthToValidate=3)
      or (PosFunction("defg", string(p))=LengthToValidate-3)
     and (PosFunction("cdefg", string(p))=LengthToValidate-4)
     and (PosFunction("bcdefg", string(p))=LengthToValidate-5)
     and (PosFunction("abcdefg", string(p))=LengthToValidate-6));
 end;

function TMainForm.Validate0: boolean;
var
 s: string;
 flags: dword;
begin;
 flags:=PAGE_NOACCESS;
 SetStringProtected(s, flags);
 try
   Result:=ValidateStringProtected(s, 3)
       and ValidateStringProtected(s, 7)
       and ValidateStringProtected(s, 11)
       and ValidateStringProtected(s, 15)
       and ValidateStringProtected(s, 1023)
       and ValidateStringProtected(s, 1027);
 except
   Result:=false;
   end;
 SetStringProtected(s, flags);

 if Result then begin;
   ErrorEdit1.Text := "Passed Validate0";
   ErrorEdit1.Color := clGreen;
  end
 else begin;
   ErrorEdit1.Text := "Failed Validate0";
   ErrorEdit1.Color := clRed;
  end;
 Update;
 end;


 
Sha ©   (2004-10-05 12:29) [199]

> GuAV ©   (05.10.04 12:19) [197]

> Не всё так однозначно...
про Celeron скоро можно будет забыть

> У них там случайно Case-unsensitive Pos нету ?
Нет. Все, что есть, здесь http://dennishomepage.gugs-cats.dk/FastCodeProject.htm


 
Defunct ©   (2004-10-05 13:50) [200]

> Поздравляю! Пора заново открывать PosChallenge.
пасиба

GuAV ©   (05.10.04 12:19) [197]
> Не всё так однозначно...

Попробуйте для Celeron"a найти наилучшее соотношение символов в строке, которое обробатывается как маленькая строка. Поменяйте цифры в сл. коде:

 ....
 Cmp  ECx,32
 Jb   @Small_String
 ...
 ...
 Cmp  ECx, 16
 Jb   @@SmallScan

Sha ©   (05.10.04 12:25) [198]
> Подправь свою функцию, чтобы проходила тест Validation0 (здесь учтены предложения GuAV):

Ок вечерком, посмотрю.


 
GuAV ©   (2004-10-05 22:15) [201]


> Попробуйте для Celeron"a найти наилучшее соотношение символов
> в строке, которое обробатывается как маленькая строка. Поменяйте
> цифры в сл. коде:

Лучше не становится, только хуже.

ИМХО, ещё быстрее будет, если искать в обратном порядке. Можно сначала что-то типа And ECx, 7 и просмотреть остаток, потом вернуть ECx и сделать And ECx not 7 и искать строку используя ММХ... это ИМХО сделает запрет выхода за буфер проще.... попробую сам сделать... если получится, выложу.


 
Defunct ©   (2004-10-05 23:31) [202]

> ИМХО, ещё быстрее будет, если искать в обратном порядке.

Не совсем понял вашу идею. Можно пояснить? мы же ищем первое вхождение, а если сканировать в обратном порядке, тогда можно получить неверный результат.

PS: для инициализации MMX требуется значительное время, сканирование целесообразно делать на MMX только для больших строк.


 
GuAV ©   (2004-10-06 01:03) [203]


> а если сканировать в обратном порядке, тогда можно получить
> неверный результат

Я сам уже понял, когда написал WideScan а он не хочет проходить валидацию. Вот код, если интересно:

@@WideScan:
 Cmp  ECx, 16
 Jb   @@SmallScan

 Mov  Al,[EAx]
 Mov  Ah, Al
 MovD MM6, EAx
 PUNPCKLWD MM6, MM6
 PUNPCKLDQ MM6, MM6

 Push EBx
 Push ECx
 Mov  EBx, ECx
 And  EBx, not 7
 And  ECx, 7
 Jz   @@ReverseScan
 Xor  ECx, ECx

@@RemainingDword:
 MovD      MM7, [EDx+EBx]  // load a part of buffer that needs to be scaned
 PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
 MovD  EAx, MM7        // get mixed dword
 Test  EAx, EAx
 Jnz   @@FoundInDWord
 Add  ECx, 4
 Jna  @@ReverseScan

 MovD      MM7, [EDx+EBx+4]  // load a part of buffer that needs to be scaned
 PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
 MovD  EAx, MM7        // get mixed dword
 Test  EAx, EAx
 Jnz   @@FoundInDWord

 Jmp  @@ReverseScan

@@FoundInDWord:
 Inc   Ecx
 Test  Al,Al
 Js    @@InDWord_pos
 Shr   EAx,8
 Jmp   @@FoundInDWord

@@InDWord_pos:
 Emms
 Pop  EAx
 Test ECx, ECx
 Js   @@InDWord_Failed
 Mov  EAx, ECx
 Add  EAx, EBx
 Pop  EBx
 Ret

@@InDWord_Failed:
 Xor  EAx, EAx
 Pop  EBx
 Ret

@@ReverseScan:
 Mov  ECx, EBx
@@ScanLoop:
 Sub       ECx, 8
 Js        @@ScanLoopExit
 MovQ      MM7, [EDx+ECx]
 PCMPEQB   MM7, MM6
 PACKSSWB  MM7, MM7
 MovD  EAx, MM7
 Test  EAx, EAx
 Jz   @@ScanLoop

@@ScanLoopFound:
 MovQ      MM7, [EDx+ECx]
 PCMPEQB   MM7, MM6
 MovD  EAx, MM7
 Test  EAx, EAx
 Jnz   @@SLFoundInDword
 Add   ECx,4
 PSRLQ MM7,32
 MovD  EAx, MM7
@@SLFoundInDword:
 Inc   Ecx
 Test  Al,Al
 Js    @@SLInDWord_pos
 Shr   EAx,8
 Jmp   @@SLFoundInDword

@@SLInDWord_pos:
 Mov   EAx, ECx
 Pop   ECx
 Pop   EBx
 Emms
 Ret

@@ScanLoopExit:
 Pop   ECx
 Pop   EBx
 Xor   EAx, EAx
 Emms
 Ret


 
Ihor Osov'yak ©   (2004-10-06 01:06) [204]

Ребята, влом отслеживать полет изменений кода.. :-)
Если можно, полный код в студию...


 
GuAV ©   (2004-10-06 01:16) [205]

Ihor Osov"yak ©   (06.10.04 01:06) [204]
пока что [193]. Однако он ещё не проходит тест на выход из буфера.  

[203] - я привел ошибочный код. как сейчас понял зря.


 
Ihor Osov'yak ©   (2004-10-06 01:20) [206]

2 [205] GuAV ©   (06.10.04 01:16)

ок, либо подожду, либо когда-то повожусь с вашим кодом. может. если будет время и энтузиазм..


 
GuAV ©   (2004-10-06 01:28) [207]

Хотя в тесте от Sha тоже ошибка. Или фича. Мы ведь договорились, что строка начинается на границе двойного слова или как ? [183]
Видимо прийдётся делать Validate для Validate 0 8-))


 
Ihor Osov'yak ©   (2004-10-06 01:49) [208]

2 [207] GuAV ©   (06.10.04 01:28)
ну, иногда лучше перебдить, чем недобдить..
Предположим фантастический вариант - я содал тело строки ручками.. не на границе двойного слова. И передал как параметр.. :-)
Лично я не сторонник таких финтов, но в принцыпе такое может быть. Поэтому я бы дал предпочтение функции, прошедшей валидацию от Sha.


 
GuAV ©   (2004-10-06 01:58) [209]

Тогда берите любую функцию от JOH. Они прошли даже мой агрессивный тест, когда даже к 0-терминатору обратится нельзя.


 
GuAV ©   (2004-10-06 02:07) [210]

Хотя поскольку ошибка приведёт AV именно в месте его причины, а не далеко после процедуры можно просто завернуть её в try... except on EAccesViolation do Result:=0 end;
Этот код может кому-то не понравится, но он нужен только в случае вольностей упомянутых в [208], в "нормальном" режиме функция не приводит к AV.


 
GuAV ©   (2004-10-06 03:12) [211]

Тут появиась мысль, ка сделать фцию, проходящую любые подобные validate0. Надо сделать вначале test edx, 3; jnz odd_align и там обработать такую ситуацию, обработав эти 1..3 байт и вернуться. Код обработки может быть и не оптимальным, лишь бы он корректно работал.


 
Sha ©   (2004-10-06 09:47) [212]

> GuAV ©   (06.10.04 01:28) [207]
> Хотя в тесте от Sha тоже ошибка. Или фича. Мы ведь
> договорились, что строка начинается на границе двойного слова или как ? [183]

У меня так и сделано.
Длина строки = 4n+3.
Конец строки (терминатор) фиксирован на нижней границе страницы.
Начало строки плавает. Но всегда находится на грацице двойного слова.


 
Sha ©   (2004-10-06 09:48) [213]

Конец строки (терминатор) фиксирован на верхней границе страницы.


 
GuAV ©   (2004-10-06 12:13) [214]

Sha ©   (06.10.04 09:47) [212]
Я поняла в чём дело.
 Call @@CharPos
Отсюда вход с not alined EDX.


 
GuAV ©   (2004-10-06 12:13) [215]

Sha ©   (06.10.04 09:47) [212]
Я понял в чём дело.
 Call @@CharPos
Отсюда вход с not alined EDX.


 
Defunct ©   (2004-10-06 16:33) [216]

>Sha [198]

Функция [193] прошла тест [198] так что можете смело использовать.

> Ihor Osov"yak ©   (06.10.04 01:06) [204]

[193] - полный рабочий вариант кода.
[195] - тесты PosDef_MMX = [193]


 
Sha ©   (2004-10-06 17:02) [217]

> Defunct ©   (06.10.04 16:33) [216]
> Функция [193] прошла тест [198] так что можете смело использовать.

На моем компьютере не проходит.


 
Defunct ©   (2004-10-06 17:25) [218]

> Sha

хм..

    if Validate0 {[198]} and
       Validate1 and
       Validate2 and
       Validate3 and
       Validate4 and
       Validate5 and
       Validate6 and
       Validate7 and
       Validate8 and
       Validate9 and
       Validate10 and
       Validate11 then

PosDef_MMX  Passed

Если вас не затруднит, можете посмотреть где именно происходит выход за границу последнего Dword, потому как проверял на разных компах, везде проходит. (сам я не могу найти).


 
Sha ©   (2004-10-06 17:43) [219]

> Defunct ©   (06.10.04 17:25) [218]
> if Validate0 {[198]} and

Такой if встречается в 2-х местах (Validate & ValidateAll).
Исправил оба?

> Если вас не затруднит, ...
Давай на "ты". Конечно, посмотрю. Но чуть позже.


 
Defunct ©   (2004-10-06 17:51) [220]

> Такой if встречается в 2-х местах (Validate & ValidateAll).
Исправил оба?


Исправил только ValidateAll,
все теперь порядок, вижу где выходит.

сейчас подправлю.


 
Jeer ©   (2004-10-06 18:04) [221]

Грандиозно мужики !
Живо мне напомнило успешные попытки втиснуть в 64K и добиться нужного быстродействия для бортового компьютера на i8080 (580)
решения навигационных задач подводного средства движения в далекие 70-е годы.

Так держать !


 
Defunct ©   (2004-10-06 18:07) [222]

Или я что-то там не понял, или тест работает не совсем корректно.

Хочу немного уточнить задачу.

Допустим строка из одного символа "a", она ведь поидее выровняна до DWord т.е. реальная длинна = |-8| +  4 = 12

-8 Dword
-4 Dword
0  "a"
1  0  
2  мусор
3  мусор

У меня происходит чтение строки
@CharPos:
Mov EAx, [EDx]
за DWord выхода нет, но Validation0 вызывает исключение именно на этой строчке. Поставил проверку на 0 (думал может туда случайно проходит вызов при буфере = 0) ничего не изменилось.


 
Sha ©   (2004-10-06 18:13) [223]

> Defunct

Ломается при таком обращении ConstantPos("ef9","efg").
Первая пересылка
@@Scan4:
 Mov  Ch,[EAx]

проходит нормально, т.к. адрес в edx выровнен.
При заходе на вторую пересылку адрес в edx уже на 1 больше.
Старший байт читаемого слова лежит на защищенной странице => AV.


 
Sha ©   (2004-10-06 18:18) [224]

Defunct ©   (06.10.04 18:07) [222]

Строку из одного "a" тест никогда не генерирует.
В тесте используются строки длиной 4*n+3 - они с учетом
терминатора всегда занимают целое число двойных слов.


 
Defunct ©   (2004-10-06 18:26) [225]

Еще один загадочный эксепшин. Идет проверка, не выходит ли подстрока за границу буфера, и если не выходит, тогда происходит сканирование в обратном порядке (с конца строки). Опять же Exception?


  Cmp  Cl, Bl      <-- проверка на выход за границу буфера
  Jb   @NotFound2  <-- буфер меньше длинны подстроки - выход

  Push ECx
  Mov  ECx, EBx
@Loop2:
  Mov  Al, [ESi+ECx]
  Cmp  Al, [EDx+ECx-1] <--- Exceptoin здесь
  Jnz  @Scan7
  Loop @Loop2

100% где-то в тесте что-то не учтено.


 
Sha ©   (2004-10-06 18:27) [226]

в Sha ©   (06.10.04 18:13) [223] случайно выкусил не тот код.
Ошибка происходит, там где ты ее нашел.


 
Defunct ©   (2004-10-06 18:29) [227]

> При заходе на вторую пересылку адрес в edx уже на 1 больше.

Понял


 
Sha ©   (2004-10-06 18:32) [228]

> Defunct ©   (06.10.04 18:26) [225]
> 100% где-то в тесте что-то не учтено.

При этом хорошо бы еще сказать что именно не учтено :)

Давай начнем с исправления очевидной ошибки в ConstantPos("ef9","efg");


 
GuAV ©   (2004-10-06 22:19) [229]

Такой WideScan не вылазит за буфер ни на байт.
Имхо надо сделать остальное типа так же.
@@WideScan:
 Cmp  ECx, 16
 Jb   @@SmallScan
 Mov  Al,[EAx]
 Mov  Ah, Al
 MovD MM6, EAx
 PUNPCKLWD MM6, MM6
 PUNPCKLDQ MM6, MM6

 Push EBx
 Push EDx
 Mov  EBx, ECx
 And  EBx, not 7
 Add  EBx, EDx
@@WSc_Scan5:
 MovQ      MM7, [EDx]
 PCMPEQB   MM7, MM6
 PACKSSWB  MM7, MM7
 MovD  EAx, MM7
 Test  EAx, EAx
 Jnz   @@WSc_QFound
 Add   EDx, 8
 Cmp   EDx, EBx
 Jl    @@WSc_Scan5

 Mov   EBx, ECx
 And   EBx, 7      // is anything left ?
 Jz    @@WSc_Done
 Test  EBx, 4      // dword or more left ?
 Jz    @@WSc_Last123

@@WSc_LastDword2:
 MovD      MM7, [EDx]  // load a part of buffer that needs to be scaned
 PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
 MovD  EAx, MM7        // get mixed dword
 Test  EAx, EAx
 Jnz   @@WSc_DFound
 Add   EDx, 4

@@WSc_Last123:
 MovD  EAx, MM6  // no MMX anymore
 Test  EBx, 2
 Jz    @@WSc_LastOne
 Cmp   AL, [EDx]
 Jz    @@WSc_AtLast1
 Inc   EDx
 Cmp   AL, [EDx]
 Jz    @@WSc_AtLast1
 Inc   EDx

@@WSc_LastOne:
 Test  EBx, 1
 Jz    @@WSc_Done
 Cmp   AL, [EDx]
 Jz    @@WSc_AtLast1

@@WSc_Done:
 Pop  EDx
 Xor   EAx, EAx
 Pop  EBx
 Emms
 Ret

@@WSc_AtLast1:
 Inc  EDx
@@WSc_AtLast:
 Mov  EAx, EDx
 Pop  EDx
 Sub  EAx, EDx
 Pop  EBx
 Emms
 Ret

@@WSc_QFound:
 MovQ      MM7, [EDx]
 PCMPEQB   MM7, MM6
 MovD  EAx, MM7
 Test  EAx, EAx
 Jnz   @@WSc_DFound
 Add   EDx,4
 PSRLQ MM7,32
 MovD  EAx, MM7

@@WSc_DFound:
 Inc   EDx
 Test  EAx, $000000FF
 Jnz   @@WSc_AtLast
 Inc   EDx
 Test  EAx, $0000FF00
 Jnz   @@WSc_AtLast
 Inc   EDx
 Test  EAx, $00FF0000
 Jnz   @@WSc_AtLast
 Inc   EDx
 Jmp   @@WSc_AtLast

@Small_String:


 
GuAV ©   (2004-10-06 23:22) [230]

Мой smallscan тоже не выходит за буфер. кроме того вместе с предыдущим кодом у меня быстрее чем [193]
@@SmallScan:
 Mov   Al, [EAx]
 Push  ECx
 Xchg  EDx, EDi
 Shr   ECx, 2
 Jz    @@SSc_SkipD
 Repne ScasD
 Je    @@SSc_D
@@SSc_SkipD:
 Mov   ECx, [ESp]
 And   ECx, 3
 Jz    @@SSc_Done
 Repne ScasB
 Jne   @@SSc_Done
 Xchg  EDx, EDi
 Pop   EAx
 Sub   EAx, ECx
 Ret
@@SSc_Done:
 Xchg  EDx, EDi
 Pop   EAx
 Xor   EAx, EAx
 Ret
@@SSc_D:
 Add   ECx, ECx
 Xchg  EDx, EDi
 Add   ECx, ECx
 Pop   EAx
 Sub   EAx, ECx
 Ret

Проверте на Р4, плиз.


 
kaZaNoVa ©   (2004-10-06 23:24) [231]

2ALL выложите плиз полный окончательный вариант "ускоренной POS"  ;)


 
GuAV ©   (2004-10-06 23:42) [232]

[230] не то выложил. не работает. вот что имелось ввиду:
@@SmallScan:
 Mov   Al, [EAx]
 Push  ECx
 Xchg  EDx, EDi
 Repne ScasB
 Jne   @@SSc_Done
 Xchg  EDx, EDi
 Pop   EAx
 Sub   EAx, ECx
 Ret
@@SSc_Done:
 Xchg  EDx, EDi
 Pop   EAx
 Xor   EAx, EAx
 Ret


 
GuAV ©   (2004-10-06 23:49) [233]

kaZaNoVa ©   (06.10.04 23:24) [231]
Как только, так сразу.


 
Defunct ©   (2004-10-07 00:00) [234]

2 GuruAV

На P4 - очень хорошо:
PosGuru&Def     4       188     134     322
PosDef_MMX      C       194     139     333


на P2 тоже стало лучше но не на много:
PosGuru&Def     4       1148    692     1840
PosDef_MMX      C       1168    699     1867
PosJOH_MMX_a    8       1139    933     2072


 
Defunct ©   (2004-10-07 00:03) [235]

с новым SmallScan [232] стало гораздо хуже:

PosGuru&Def     4       194     134     328


 
Defunct ©   (2004-10-07 00:14) [236]

Guru, а вы учитываете выравнивание на границу QWord при MMX сканировании? На очень больших строках будет существенный проигрыш если не учитываете.

Это же было не от фонаря сделано [193]:

 MovQ      MM7, [Edi]
 PCMPEQB   MM7, MM6
 PACKSSWB  MM7, MM7
 MovD  EAx, MM7
 Test  EAx, EAx
 Jnz   @@CharFound
 Add   EDi, 8
 Mov   EAx, Edi
 And   EAx, 7
 Sub   EDi,EAx         // Aligning index
 Sub   ECx,8
 Add   ECx,EAx
 
 Cmp   ECx,4
 Jnge  @@LastDword

У меня получалось добиться индекса 133 в SB2, но тогда мой тест (просмотр 12.5Gb) выполнялся на 2 секунды дольше.


 
GuAV ©   (2004-10-07 00:33) [237]

Defunct ©   (07.10.04 00:14) [236]

не знал про это. тогда так:

@@WideScan:
Cmp  ECx, 16
Jb   @@SmallScan
Mov  Al,[EAx]
Mov  Ah, Al
MovD MM6, EAx
PUNPCKLWD MM6, MM6
PUNPCKLDQ MM6, MM6

Push EBx
Push EDx
Mov  EBx, ECx
And  EBx, not 7
Add  EBx, EDx
Test EDx, 4
Jz   @@WSc_Scan5

MovD      MM7, [EDx]  // load a part of buffer that needs to be scaned
PCMPEQB   MM7, MM6    // make a bit mask (mm6 filled by 1st char of da constant)
MovD  EAx, MM7        // get mixed dword
Test  EAx, EAx
Jnz   @@WSc_DFound
Add   EDx, 4
Sub   EBx, 4          // do not check dword again

@@WSc_Scan5:
MovQ      MM7, [EDx]

и далее по тексту.
то есть, если был DWord align, то при WSc_Scan5 будет QWord align, а не быть его может только в извращённых условиях типа validate0, под которые подстраиваемся, но не оптимизируем.


 
Defunct ©   (2004-10-07 01:09) [238]

У меня промелькнула идея которая не только может значительно ускорить функцию, но и решит проблему с validate 0. Как вы думаете, если при MMX сканировании и при CharPos (раскрученном цикле) формировать массив найденных символов и потом с ним и работать. (т.е. например, просмотрели 8 символов, нашли 3 совпадения и дальше уже работает с совпадениями, все найденные совпадения не подходят - тогда продолжаем смотреть следующие 8 символов).

PS: меня сейчас время поджимает, смогу рассмотреть эту идею только на выходных, если у вас есть время можете попробовать раньше.


 
GuAV ©   (2004-10-07 01:49) [239]

@@CompareWholeString:
 Test  EBx, EBx
 Jz    @@Constant_Exists
 Cmp   EDx, EBx
 Jna   @@Exit_False

 MOV   ECX, EBX

@@CWs_1:
 Test  ECx, 1
 Jz    @@CWs_2
 MOV   AL, [ESi+ECx]
 CMP   AL, [EDi+ECx]
 Sub   ECx, 1

@@CWs_2:
 Test  ECx, 2
 Jz    @@CWs_4
 MOV   AX, [ESi+ECx]
 CMP   AX, [EDi+ECx]
 Sub   ECx, 4

@@CWs_4:
 Test  ECx, 4
 Jz    @@CWs_8
 MOV   EAX, [ESi+ECx]
 CMP   EAX, [EDi+ECx]
 Sub   ECx, 8

@@CWs_8:

@@CW_Loop:
 SUB   ECX, 8
 JL    @@Constant_Exists
 MovQ     MM3,  [ESi+ECx]
 MovQ     MM2,  [EDi+ECx]
 PCMPEQB   MM3,  MM2
 PACKSSWB MM3,  MM3
 MovD  EAX, MM3
 Cmp   EAX, -1
 JE   @@CW_Loop

@@SkipBTest:
 Mov   ECx, EDx
 Cmp   EDx,4
 Jg    @@Scan
 Jmp   @@LastDWord

@@Constant_Exists:
 Pop   EAx
 Sub   EAx, EDx


Defunct ©   (07.10.04 01:09) [238]
Не думаю что я справлюсь раньше. Пока я ещё не полностью понимаю работу имеющесйся. Однако, завтра у меня будет много времени.


 
GuAV ©   (2004-10-07 02:05) [240]

[239] опять не рабочий код дал, блин... вот рабочий:
@@CompareWholeString:
 Test  EBx, EBx
 Jz    @@Constant_Exists
 Cmp   EDx, EBx
 Jna   @@Exit_False

 MOV   ECX, EBX

@@CWs_1:
 Test  ECx, 1
 Jz    @@CWs_2
 Sub   ECx, 1
 MOV   AL, [ESi+ECx]
 CMP   AL, [EDi+ECx]
 JNE   @@SkipBTest

@@CWs_2:
 Test  ECx, 2
 Jz    @@CWs_4
 Sub   ECx, 2
 MOV   AX, [ESi+ECx]
 CMP   AX, [EDi+ECx]
 JNE   @@SkipBTest

@@CWs_4:
 Test  ECx, 4
 Jz    @@CWs_8
 Sub   ECx, 4
 MOV   EAX, [ESi+ECx]
 CMP   EAX, [EDi+ECx]
 JNE   @@SkipBTest

@@CWs_8:

@@CW_Loop:
 SUB   ECX, 8
 JL    @@Constant_Exists
 MovQ     MM3,  [ESi+ECx]
 MovQ     MM2,  [EDi+ECx]
 PCMPEQB   MM3,  MM2
 PACKSSWB MM3,  MM3
 MovD  EAX, MM3
 Cmp   EAX, -1
 JE   @@CW_Loop

@@SkipBTest:
 Mov   ECx, EDx
 Cmp   EDx,4
 Jg    @@Scan
 Jmp   @@LastDWord

@@Constant_Exists:
 Pop   EAx
 Sub   EAx, EDx


 
GuAV ©   (2004-10-07 02:23) [241]

@@CompareWholeString:
 Test  EBx, EBx
 Jz    @@Constant_Exists
 Cmp   EDx, EBx
 Jna   @@Exit_False

 Push   ESi
 Push   EDi

 Add   EDi, EBx
 Add   ESi, EBx

@@CWs_1:
 Test  EBx, 1
 Jz    @@CWs_2
 Sub   ESi, 1
 Sub   EDi, 1
 MOV   AL, [ESi]
 CMP   AL, [EDi]
 JNE   @@SkipBTest1

@@CWs_2:
 Test  EBx, 2
 Jz    @@CWs_4
 Sub   ESi, 2
 Sub   EDi, 2
 MOV   AX, [ESi]
 CMP   AX, [EDi]
 JNE   @@SkipBTest1

@@CWs_4:
 Test  EBx, 4
 Jz    @@CWs_8
 Sub   ESi, 4
 Sub   EDi, 4
 MOV   EAX, [ESi]
 CMP   EAX, [EDi]
 JNE   @@SkipBTest1

@@CWs_8:
 MOV   ECX, EBX

@@CW_Loop:
 SUB   ECX, 8
 JL    @@Constant_Exists1
 SUB   ESi, 8
 SUB   EDi, 8
 MovQ     MM3,  [ESi]
 MovQ     MM2,  [EDi]
 PCMPEQB   MM3,  MM2
 PACKSSWB MM3,  MM3
 MovD  EAX, MM3
 Cmp   EAX, -1
 JE   @@CW_Loop

@@SkipBTest:
 Mov   ECx, EDx
@@SkipBTest1:
 Cmp   EDx,4
 Pop   EDi
 Pop   ESi
 Jg    @@Scan
 Jmp   @@LastDWord

@@Constant_Exists1:
 Pop   EDi
 Pop   ESi
@@Constant_Exists:
 Pop   EAx
 Sub   EAx, EDx


 
GuAV ©   (2004-10-07 16:10) [242]

По поводу поиска по восем байт без AV мысль такая.

сначала Test Buffer, 7 и если не нуль то
{
читать в mmreg 8 байт из [Buffer and (not 7)]

в начало могут попасть байты из длины и счета ссылок, чтобы их проигнорировать, тестить результат PCMPEQB / PACKUSWB вот такой маской (адресовать в самой маске по [Buffer and 7)])

 SmallTestArray: array[0..7] of LongWord =
   ($00000000,
    $F0000000,
    $FF000000,
    $FFF00000,
    $FFFF0000,
    $FFFFF000,
    $FFFFFF00,
    $FFFFFFF0);
поправить буфер Buffer:=Buffer and (not 7)
}
потом читать по 8 байт из буфера (он уже QWord aligned)

потом читать остаток, если он есть. Причём 8 байт, хотя осталось меньше. Тестить масокй из аналогичного массива. Буфер остался QWord Aligned, поэтому чтение не пересечет 4к-границу, т.е. AV не будет.


 
Defunct ©   (2004-10-07 19:33) [243]

2 GuruAV: я смотрю вы из Харькова, не хотите быть соавтором статьи в ВАКовском журнале "Проблемы программирования"? Если да, тогда давайте пересечемся в аське.


 
GuAV ©   (2004-10-07 20:11) [244]

Реализация [242] для поиска из [193]
@@WideScan:
 Cmp  ECx, 16
 Jb   @@SmallScan
 Mov  Al,[EAx]
 Mov  Ah, Al
 MovD MM6, EAx
 PUNPCKLWD MM6, MM6
 PUNPCKLDQ MM6, MM6

 Push ECx
 Test EDx, 7
 Jz   @@Scan5
 Mov  ECx, EDx
 And  ECx, 7
 And  EDx, not 7
 MovQ      MM7, [EDx]
 PCMPEQB   MM7, MM6
 PACKSSWB  MM7, MM7
 MovD EAx, MM7
 And  EAx, SmallTestArray[ECx*4].DWord
 Jnz  @@1stbytes
 Mov  EAx, ECx
 Mov  ECx, [ESp]
 Add  EDx, 8
 Sub  ECx, EAx

@@Scan5:
 MovQ      MM7, [EDx]
 PCMPEQB   MM7, MM6
 PACKSSWB  MM7, MM7
 MovD  EAx, MM7
 Test  EAx, EAx
 Jnz   @@CharFound2
 Add   EDx,8
 Sub   ECx,8
 Jg    @@Scan5

@@Exit_False2:
 Emms
 Pop   EAx
 Xor   EAx, EAx
 Ret

@@1stbytes:
 Mov  [ESp], ECx
 Mov  ECx, 8

@@CharFound2:
 Dec   Ecx
 Test  EAx,0Fh
 Jnz   @@Check2
 Shr   EAx,4
 Jmp   @@CharFound2

@@Loop2:
 Dec   Ecx
 Test  Al,Al
 Js    @@Check2
 Shr   EAx,8
 Jmp   @@Loop2

@@Check2:
 Test  ECx,ECx
 Js    @@Exit_False2
 Emms
 Pop   EAx
 Sub   EAx, ECx
 Ret



> пересечемся в аське

никак. только мыло.


 
Defunct ©   (2004-10-08 20:21) [245]

> никак. только мыло.

чего так? религия запрещает? как насчет MSN


 
GuAV ©   (2004-10-08 23:16) [246]

<off>

> чего так? религия запрещает? как насчет MSN

То запрещает, что у меня так:
соединился, проверил почту, скачал интересные ветки, отключился.
написал ответы, соединился, отправил всё, отключился.
Ибо время-деньги. Это называется диал-ап.

Кстати, что я могу написать про "Проблемы программирования", если я не программист ?
</off>


 
Defunct ©   (2004-10-09 00:19) [247]

> Ибо время-деньги. Это называется диал-ап.
думаю 30 мин нам хватит. а там уже можно и почтой.

> Кстати, что я могу написать про "Проблемы программирования", если я не программист ?

Писать надо не про проблемы программирования, а про ускорение поиска подстроки в строке. "Проблемы программирования" - это просто название журнала.

ВАК = Высшая Аттестационная Комиссия.

Судя по анкете вы еще учитесь, научная статья вам повредит? Материала в этой ветке уже предостаточно для хорошей статьи.

PS: завтра попробую [244] и займусь реализацией [238].


 
GuAV ©   (2004-10-09 00:28) [248]

Defunct ©   (09.10.04 00:19) [247]
Ок. завтра во второй половине дня или послезавтра пересечёмся.
Хотя имхо если по этой ветке, то надо сначало довести начатое дело.

Да. Я действительно ещё учусь.


 
GuAV ©   (2004-10-09 00:29) [249]


> завтра

то есть не завтра, а в субботу, т.е. сегодня уже. на время не глянул.


 
GuAV ©   (2004-10-09 15:49) [250]

Мой ICQ в анкете...


 
Defunct ©   (2004-10-09 16:57) [251]

[244]
WideScan - поиск символа, и он используется только при длинне подстроки = 1 (подстрока состоит из одного символа). Первый найденный символ и будет результатом. Менять с учетом [238] надо было WideSearch, именно в нем надо просматривать сразу все возможные вхождения первого символа нарушения QWord Align.


 
GuAV ©   (2004-10-10 14:30) [252]

Для коротких строк в [193] после проверки возврат на цикл уже длиных строк, это иожет привести к потере быстродействия и явно создаёт проблемы для дальнейшей оптимизации. предлагаю вынести __WideScan; и  __WideString; и разделить CompareWholeString для них.

procedure __WideScan;
asm
Mov  Al,[EAx]
Mov  Ah, Al
MovD MM6, EAx
PUNPCKLWD MM6, MM6
PUNPCKLDQ MM6, MM6

Push ECx
Test EDx, 7
Jz   @@Scan5
Mov  ECx, EDx
And  ECx, 7
And  EDx, not 7
MovQ      MM7, [EDx]
PCMPEQB   MM7, MM6
PACKSSWB  MM7, MM7
MovD EAx, MM7
And  EAx, SmallTestArray[ECx*4].DWord
Jnz  @@1stbytes
Mov  EAx, ECx
Mov  ECx, [ESp]
Add  EDx, 8
Sub  ECx, EAx

@@Scan5:
MovQ      MM7, [EDx]
PCMPEQB   MM7, MM6
PACKSSWB  MM7, MM7
MovD  EAx, MM7
Test  EAx, EAx
Jnz   @@CharFound2
Add   EDx,8
Sub   ECx,8
Jg    @@Scan5

@@Exit_False2:
Emms
Pop   EAx
Xor   EAx, EAx
Ret

@@1stbytes:
Mov  [ESp], ECx
Mov  ECx, 8
Jmp  @@CharFound2

@@CharFound21:
Shr   EAx,4
@@CharFound2:
Dec   Ecx
Test  EAx,0Fh
Jz    @@CharFound21

@@Check2:
Test  ECx,ECx
Js    @@Exit_False2
Emms
Pop   EAx
Sub   EAx, ECx
end;

procedure __WideString;
asm
 MovD MM4, EAx
 Mov  Ah, Al
 MovD MM6, EAx
 PUNPCKLWD MM6, MM6
 PUNPCKLDQ MM6, MM6

@@Scan:

 MovQ      MM7, [Edi]
 PCMPEQB   MM7, MM6
 PACKSSWB  MM7, MM7
 MovD  EAx, MM7
 Test  EAx, EAx
 Jnz   @@CharFound
 Add   EDi, 8
 Mov   EAx, Edi
 And   EAx, 7
 Sub   EDi, EAx         // Aligning index
 Sub   ECx, 8
 Add   ECx, EAx

@@Scan2:
 MovQ      MM7, [Edi]
 PCMPEQB   MM7, MM6
 PACKSSWB  MM7,MM7
 MovD  EAx, MM7
 Test  EAx, EAx
 Jnz   @@CharFound
 Add   EDi,8
 Sub   ECx,8
 Jg    @@Scan
 Jmp   @@Exit_False

@@CharFound:
 MovQ      MM7, [Edi]
 PCMPEQB   MM7, MM6
 MovD  EAx, MM7
 Test  EAx, EAx
 Jnz   @@Loop1
 Add   EDi,4
 Sub   ECx,4
 PSRLQ MM7,32
 MovD  EAx, MM7
@@Loop1:
 inc   EDi
 dec   Ecx
 Test  Al,Al
 Js    @@Check
 Shr   EAx,8
 Jmp   @@Loop1

@@Check:
 MovD  EAx, MM4

@@CheckLastChar:       // as proposed in Boyer-Mur alhorithm
 Mov   EDx, ECx      // store new residual buffer size
 Test  EDx, EDx
 Js    @@Exit_False
 Test  EBx, EBx
 Js    @@Constant_Exists

 Cmp   Ah, [EBx+Edi]
 Jz    @@CompareWholeString
 Test  EDx, EDx
 Jg   @@Scan

@@Exit_False:
 Pop  EAx
 Xor  EAx,EAx
 Jmp  @@Leave_Proc

@@NotFound_Exit:
 Xor  EAx, EAx
 Ret
@@CompareWholeString:
Test  EBx, EBx
Jz    @@Constant_Exists
Cmp   EDx, EBx
Jna   @@Exit_False

MOV   ECX, EBX

@@CWs_1:
Test  ECx, 1
Jz    @@CWs_2
Sub   ECx, 1
MOV   AL, [ESi+ECx]
CMP   AL, [EDi+ECx]
JNE   @@SkipBTest

@@CWs_2:
Test  ECx, 2
Jz    @@CWs_4
Sub   ECx, 2
MOV   AX, [ESi+ECx]
CMP   AX, [EDi+ECx]
JNE   @@SkipBTest

@@CWs_4:
Test  ECx, 4
Jz    @@CWs_8
Sub   ECx, 4
MOV   EAX, [ESi+ECx]
CMP   EAX, [EDi+ECx]
JNE   @@SkipBTest

@@CWs_8:

@@CW_Loop:
SUB   ECX, 8
JL    @@Constant_Exists
MovQ     MM3,  [ESi+ECx]
MovQ     MM2,  [EDi+ECx]
PCMPEQB   MM3,  MM2
PACKSSWB MM3,  MM3
MovD  EAX, MM3
Cmp   EAX, -1
JE   @@CW_Loop

@@SkipBTest:
 Mov   ECx, EDx
@@SkipBTest1:
 Test  EDx, EDx
 Jg    @@Scan
 Jmp   @@Exit_False

@@Constant_Exists:
 Pop   EAx
 Sub   EAx, EDx

@@Leave_Proc:
 EMMS
 Pop  EBp
 Pop  EBx
 Pop  ESi
 Pop  EDi
End;

// продолжение следующим постом


 
GuAV ©   (2004-10-10 14:32) [253]

Function PosDefunct_a(const Constant, Buffer: string): Integer; Assembler;
Asm
 Test eax, eax
 Jz   @@NotFound_Exit
 Test edx, edx
 Jz   @@NotFound_Exit
 Mov  ECx, [EDx-4]
 Test ECx, ECx
 Jz   @@NotFound_Exit
 Cmp  [EAx-4],1
 Jnz   @@WideSearch

 Cmp   ECx, 8
 Ja    @@SmallScan

// CharPos
@@Scan4:
 Mov  Ch,[EAx]
// Ch - char
// EDx - pointer to buffer
// cl - length
@@CharPos:
 Mov  EAx,[EDx]

 Cmp  Ch,Al
 Jz   @Found1
 cmp  Ch,Ah
 Jz   @Found2
 Shr  EAx,16
 Cmp  Ch,Al
 Jz   @Found3
 Cmp  Ch,Ah
 Jz   @Found4

 Cmp  Cl,4
 Jna  @NotFound
 Mov  EAx,[EDx+4]

 Cmp  Ch, Al
 Jz   @Found5
 Cmp  Ch, Ah
 Jz   @Found6
 Shr  EAx, 16
 Cmp  Ch, Al
 Jz   @Found7
 Cmp  Ch, Ah
 Jz   @Found8

@NotFound:
 XOr  EAx, EAx
 Ret
@Found1:
 Mov  EAx,1
 Jmp  @Validation
@Found2:
 Mov  EAx,2
 Jmp  @Validation
@Found3:
 Mov  EAx,3
 Jmp  @Validation
@Found4:
 Mov  EAx,4
 Jmp  @Validation
@Found5:
 Mov  EAx,5
 Jmp  @Validation
@Found6:
 Mov  EAx,6
 Jmp  @Validation
@Found7:
 Mov  EAx,7
 Jmp  @Validation
@Found8:
 Mov  EAx,8
 Jmp  @Validation

@Validation:
 Cmp  Al,Cl
 Ja   @NotFound
 ret

 nop
 nop                  // alignment for better performance

@@SmallScan:
 Cmp  ECx, 16
 Ja   __WideScan
 Push  ECx
 Mov   Ch,[EAx]
@@Scan8:
 Push  ECx
 Call  @@CharPos
 Pop   ECx
 Test  EAx, EAx
 Jz    @Validation3

 Sub   Cl, Al
 Pop   EAx
 Sub   Al, Cl
 Ret

@Validation3:
 Sub   Cl, 8
 Add   EDx, 8
 Cmp   Cl, 0
 Jg    @@Scan8

 Add   ESP, 4
 Ret

/////////////////////////////////////////////

@Small_String:
 Cmp  ECx, [EAx-4]
 Jb   @NotFound
 Mov  Ch, [EAx]
 Push ECx
 Push EBx
 Push ESi
 Mov  ESi, EAx
 Mov  EBx,[ESi-4]
 Dec  EBx
@Scan6:
 Push ECx
@Scan7:
 Call @@CharPos
 Pop  ECx
 Test EAx, EAx
 Jz   @Validation2
 Sub  Cl, Al
 Add  EDx, EAx
 Cmp  Cl, Bl
 Jb   @NotFound2

 Push ECx
 Mov  ECx, EBx
@Loop2:
 Mov  Al, [ESi+ECx]
 Cmp  Al, [EDx+ECx-1]
 Jnz  @Scan7
 Loop @Loop2

 Pop  ECx
 Mov  Ah, 0
 Pop  ESi
 Pop  EBx
 Pop  EAx
 Sub  EAx, ECx
 ret

@Validation2:
 Sub  Cl, 8
 Add  EDx, 8
 Cmp  Cl, 0
 Jg   @Scan6

@NotFound2:
 Pop  ESi
 Pop  EBx
 Add  ESp, 4
 Xor  EAx, EAx
 Ret

@@WideSearch:
 Cmp  ECx,32
 Jb   @Small_String

 Push EDi
 Push ESi
 Push EBx
 Push EBp

 MOV  ESI, EAX
 Mov  EDi, EDx
 Push ECx
 Mov  EBX, [ESI-4]
 Test EBx, EBx
 Jz   @@Exit_False
 Mov  Al, [ESi]
 Cmp  EBx, ECx
 Ja   @@Exit_False
 Jnz  @@Normal_Work
 Cmp  AL, [Edi]
 Jnz  @@Exit_False

@@Normal_Work:
 Dec  EBX

 Mov  Ah, [ESi+EBx]
 Dec  EBx
 Mov  EDx,ECx
 Inc  ESi
 Mov  EBp, ESi
 Cmp  ECx, 8
 Ja   __WideString

@@Scan3:
 Push EAx
 Mov  Ch, Al
 Mov  EDx, EDi
 Call @@CharPos
 Mov  Ch, 0
 Sub  ECx, EAx
 Add  EDi, EAx
 Pop  EDx
 Test EAx,EAx
 Jz   @@Exit_False
 Xchg EAx, EDx
//  Jmp  @@CheckLastChar
//@@CheckLastChar:       // as proposed in Boyer-Mur alhorithm
 Mov   EDx, ECx      // store new residual buffer size
 Test  EDx, EDx
 Js    @@Exit_False
 Test  EBx, EBx
 Js    @@Constant_Exists

 Cmp   Ah, [EBx+Edi]
 Jz    @@CompareWholeString
 Test  EDx, EDx
 Jg   @@Scan3

@@Exit_False:
 Pop  EAx
 Xor  EAx,EAx
 Jmp  @@Leave_Proc

@@NotFound_Exit:
 Xor  EAx, EAx
 Ret

@@CompareWholeString:
 Test  EBx, EBx
 Jz    @@Constant_Exists
 Cmp   EDx, EBx
 Jna   @@Exit_False

 MOV   ECX, EBX
@@CW_Loop:
 SUB   ECX, 4
 JL    @@CW_Last123bytes
 MOV   EAX, [ESi+ECx]
 JZ    @@CW_LastDWord

 CMP   EAX, [EDi+ECx]
 JE   @@CW_Loop
 Mov   ECx, EDx
 JMP   @@SkipBTest1

@@CW_LastDWord:
 CMP   EAX, [EDi]
 JE    @@Constant_Exists
 Mov   ECx, EDx
 JMP   @@SkipBTest1

@@CW_Last123bytes:

 ADD   ECX, 2
 JS    @@Last1

@@Last3:
 MOV   AX, [ESi]
 JZ    @@Last2
 CMP   AX, [EDi]
 JNE   @@SkipBTest
 MOV   AL, [ESi+2]
 CMP   AL, [EDi+2]
 JE    @@Constant_Exists
 Mov   ECx, EDx
 JMP   @@SkipBTest1

@@Last2:
 CMP   AX, [EDi]
 JE    @@Constant_Exists
 Mov   ECx, EDx
 JMP   @@SkipBTest1

@@Last1:
 MOV   AL, [ESi]
 CMP   AL, [EDi]
 JE    @@Constant_Exists

@@SkipBTest:
 Mov   ECx, EDx

@@SkipBTest1:
 Test  EDx, EDx
 Jg    @@Scan3
 Jmp   @@Exit_False

@@Constant_Exists:
 Pop   EAx
 Sub   EAx, EDx

@@Leave_Proc:
 Pop  EBp
 Pop  EBx
 Pop  ESi
 Pop  EDi

End;


 
GuAV ©   (2004-10-10 15:05) [254]

Моя версия SmallScan:

@@SmallScan:
 Cmp  ECx, 16
 Ja   __WideScan
 Mov   Ch,[EAx]

 Push  ECx
 Call  @@CharPos
 Pop   ECx

 Test  EAx, EAx
 Jnz   @@SmallDone

 Add   EDx, 8
 Sub   ECx, 8
 Call  @@CharPos
 Test  EAx, EAx
 Jz    @@SmallDone
 Add   EAx, 8

@@SmallDone:
 Ret


 
Sha ©   (2004-10-11 12:58) [255]

> Defunct
> GuAV

Скорость - это хорошо.
Но хочется все-таки увидеть функцию, прошедшую тест Validate0.


 
Defunct ©   (2004-10-11 17:00) [256]

> Но хочется все-таки увидеть функцию, прошедшую тест Validate0.

Работаем над этим, попутно делаем описание.
К концу недели должна быть функция прошедшая validate0 и без потерь в скорости.


 
Sha ©   (2004-10-11 21:35) [257]

Defunct ©   (11.10.04 17:00) [256]

OK.
Попробую пока переделать свою с Pascal на Asm.
Потом сравним ;)


 
GuAV ©   (2004-10-12 00:46) [258]


> маленькой хитрости - изменения направления сравнения.

Почему это усокряет функцию ?


 
Defunct ©   (2004-10-12 01:06) [259]

>> маленькой хитрости - изменения направления сравнения.
> Почему это усокряет функцию ?

Я вам пытался об этом сказать, но вы не хотели слушать мотивируя примером с наперсточником, пятью наперстками и шариком. ;)

У вас в аське есть ответ на этот вопрос.
Помните, что главной задачей является поиск несовпадения, а не наоборот. Чем быстрее отбракуем строку, тем быстрее продолжим линейный поиск символа.

Вот строки для сравнения, какой способ определит несовпадение быстрее всего? С конца, с начала, или в двух направлениях, или в четырех?

[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
[xxbxxxxxxxxxxxxxxxxxxxxxxxxxxxx]

[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
[xxxxxxxxxxxxxxxxxxxxxxxxxxxxcxx]

[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
[xxxxxxxxxxxxxcxxxxxxxxxxxxxxxxx]


 
GuAV ©   (2004-10-12 01:51) [260]

//Нет не в том дело.
//попробуйте такое:

const Len = 1024*256;

var TestArray: array[0..Len] of Byte;

procedure P1;
asm
 MOV ECX, Len
 @@1:
 MOV AL, TestArray[ECX].Byte
 DEC ECX
 CMP ECX, 0
 JNZ @@1
end;

procedure P2;
asm
 MOV ECX, 0
 @@1:
 MOV AL, TestArray[ECX].Byte
 INC ECX
 CMP ECX, Len
 JNZ @@1
end;

procedure TForm1.Button1Click(Sender: TObject);
var I, T1, T2: Integer;
begin
 T1:=GetTickCount;
 for I:=1 to 1000 do
   P1;
 T1:=GetTickCount-T1;
 T2:=GetTickCount;
 for I:=1 to 1000 do
   P2;
 T2:=GetTickCount-T2;
 Memo1.Lines.Add("P1="+IntToStr(T1));
 Memo1.Lines.Add("P2="+IntToStr(T2));
end;

procedure TForm1.Button2Click(Sender: TObject);
var I, T1, T2: Integer;
begin
 T2:=GetTickCount;
 for I:=1 to 1000 do
   P2;
 T2:=GetTickCount-T2;
 T1:=GetTickCount;
 for I:=1 to 1000 do
   P1;
 T1:=GetTickCount-T1;
 Memo1.Lines.Add("P1="+IntToStr(T1));
 Memo1.Lines.Add("P2="+IntToStr(T2));
end;


ps на [258] хотел получить ответ Sha ©


 
Defunct ©   (2004-10-12 01:58) [261]

[260]

ну и?
на P4 одинаково.


 
GuAV ©   (2004-10-12 02:05) [262]

У меня нет. Но я понял уже почему нет (разное кодирование CMP). Таки наверное Вы правы. Не буду больше сбивать Вас с верного пути...


 
GuAV ©   (2004-10-12 11:26) [263]

Думал над идеей Defunct, обратном порядке и напрестке.
Вывод. Поскольку обратный порядок существенно быстрее чем прямой, данный тест необъективен.

Объективный тест:
Строка случайной длины.
Берется случайная подстрока из этой строки (с приоритетом в сторону коротких строк).
Иногда в ней делаются различия случайные (с приоритетом в сторону неболшого числа различий).


 
GuAV ©   (2004-10-12 11:33) [264]

Естественно всем тогда одинаковый RandSeed



Страницы: 1 2 3 4 5 6 7 вся ветка

Форум: "Основная";
Текущий архив: 2004.10.24;
Скачать: [xml.tar.bz2];

Наверх





Память: 1.56 MB
Время: 0.044 c
6-1092745571
ИМХО
2004-08-17 16:26
2004.10.24
OnDownloadComplete или не OnDownloadComplete - вот в чем вопрос


8-1088526424
lota
2004-06-29 20:27
2004.10.24
Азбука Морзе


14-1096291250
pasha_golub
2004-09-27 17:20
2004.10.24
Софт для мобилы


14-1096963030
Layner
2004-10-05 11:57
2004.10.24
Kodak выиграла иск о патентах на Java


1-1097139675
sirsergio
2004-10-07 13:01
2004.10.24
Посветка текста в TMemo





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский