Содержание материала

 

Попробуем некоторые варианты и посмотрим, как они исполняются. Наиболее существенно является строка:

cmp  al,[edx+ecx]

Она генерирует две микроинструкции. Одна для загрузки байта по адресу [EDX+ECX] и вторая для сравнения его со значением в AL. Данная строка может быть закодирована также как:

mov ah, byte ptr [edx+ecx]

cmp al, ah

Данный вариант также генерирует две микроинструкции, но это также требует и дополнительный регистр (AH).

Если мы готовы выделить лишний регистр, то это также можно сделать также как:

movzx efx, byte ptr [edx+ecx]

cmp   al, fh

Инструкция MOVZX это пересылка с расширением нуля. Она загружает один байт в младшую часть регистра EFX и заполняет отставшие биты нулями. Конечно, нет такой вещи как регистр efx, но два неиспользуемых регистра ESI и EDI не могут быть доступны на байтовой основе. Поэтому если есть свободный регистр EAX, EBX, ECX или EDX, подставьте это место EDI или ESI и используйте подстановку EBX вместо "EFX".

Данная функция демонстрирует первый вариант.

Code:

function CharPos30(Chr : Char; const Str : AnsiString) : Cardinal;

asm

push ebx

//if StrLenght > 0 then

test edx,edx

jz   @Else1Begin

//StrLenght := Length(Str);

mov  ebx,[edx-$04]

//I := 0;

xor ecx,ecx

dec  edx

@RepeatBegin :

//Inc(I);

inc  ecx

//until((Str[I] = Chr) or (I > StrLenght));

mov  ah, [edx+ecx]

//cmp  al,[edx+ecx]

cmp  al,ah

jz   @Exit

cmp  ebx,ecx

jnl  @RepeatBegin

@Else1Begin :

//Result := 0;

xor eax,eax

pop  ebx

ret

@Exit :

mov  eax,ecx

pop  ebx

end;

 

А эта функция демонстрирует второй вариант.

Code:

function CharPos31(Chr : Char; const Str : AnsiString) : Cardinal;

asm

push ebx

push edi

//if StrLenght > 0 then

test edx,edx

jz   @Else1Begin

//StrLenght := Length(Str);

mov  edi,[edx-$04]

//I := 0;

xor ecx,ecx

dec  edx

@RepeatBegin :

//Inc(I);

inc  ecx

//until((Str[I] = Chr) or (I > StrLenght));

movzx ebx, byte ptr [edx+ecx]

//cmp  al,[edx+ecx]

cmp  al, bl

jz   @Exit

cmp  edi,ecx

jnl  @RepeatBegin

@Else1Begin :

//Result := 0;

xor eax,eax

pop  edi

pop  ebx

ret

@Exit :

mov  eax,ecx

pop  edi

pop  ebx

end;

 

 

Вместо сложения EDX и ECX при расчете адреса в каждой итерации, мы можем их сложить до цикла. Затем если необходимо вычитать их друг из друга для получения счетчика цикла при возврате результата. Это выполняется с помощью инструкции SUB во второй строке поле метки Exit.

Code:

function CharPos32(Chr : Char; const Str : AnsiString) : Cardinal;

asm

push  ebx

push  edi

//if StrLenght > 0 then

test  edx,edx

jz    @Else1Begin

//StrLenght := Length(Str);

mov   edi,[edx-$04]

//I := 0;

xor   ecx,ecx

//dec  edx

add   ecx,edx

@RepeatBegin :

//Inc(I);

//until((Str[I] = Chr) or (I > StrLenght));

movzx ebx, byte ptr [ecx]

inc   ecx

cmp   al, bl

jz    @Exit

//cmp  edi,ecx

test  bl, bl

jnz   @RepeatBegin

@Else1Begin :

//Result := 0;

xor   eax,eax

pop   edi

pop   ebx

ret

@Exit :

mov   eax,ecx

sub   eax,edx

pop   edi

pop   ebx

end;

 

Теперь у нас есть четыре функции для сравнения производительности: CharPos29, CharPos30, CharPos31 и CharPos32.

Результаты на P4 1920 следующие:

CharPos29 716

CharPos30 973

CharPos31 710

CharPos32 702

Победитель функция CharPos32

Результаты на P3 1400 следующие:

CharPos29  949

CharPos30  921

CharPos31  950

CharPos32 1403

Победитель функция CharPos30

Суммарное время

CharPos29 716 + 949  = 1665

CharPos30 973 + 921  = 1894

CharPos31 710 + 950  = 1660

CharPos32 702 + 1403 = 2105

Winner is CharPos31

На P4 выигрышный цикл следующий:

@RepeatBegin :

movzx ebx, byte ptr [ecx]

inc   ecx

cmp   al, bl

jz    @Exit

test  bl, bl

jnz   @RepeatBegin

На P3 выигрышный цикл следующий:

@RepeatBegin :

inc  ecx

mov  ah, [edx+ecx]

cmp  al,ah

jz   @Exit

cmp  ebx,ecx

jnl  @RepeatBegin

При работе на обеих платформах выигрышный цикл следующий:

@RepeatBegin :

inc   ecx

movzx ebx, byte ptr [edx+ecx]

cmp   al, bl

jz    @Exit

cmp   edi,ecx

jnl   @RepeatBegin

Победитель на P4 очень плох на P3 и не может быть использован в библиотеках на других платформах, кроме P4, таких как Delphi RTL. Победитель на P3 выполняется очень отвратительно на P4 и поэтому не должен быть использован в библиотеках для обеих платформ. Победитель для обеих платформ, это функция CharPos31, которая имеет результаты близкие к оптимальным для P4 и также достаточно оптимальные и для P3. Данная функция подходящий выбор для библиотек класса Delphi RTL. Это показывает, что можно оптимизировать функцию для обоих типов процессоров, без потери производительности не более, чем на несколько процентов.

Сравнение производительности P3 и P4 на основе такт-такт всегда немного спектакль. Появляется необоснованная тенденция думать, что P4 имеет as having an inferior design, но это не подтверждается нашим кодом. Взяв победителя для смешанной платформы и сделав приведение результата к 1400 MHz процессору, то мы получим 950 для P3 и 710 * (1920/1400) = 973 для P4. Производительность процессоров почти одинаковая при одинаковой частоте.

 

Добавить комментарий

Не использовать не нормативную лексику.

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

ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!


Защитный код
Обновить