О производительности ассемблерных вставок...

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Аватара пользователя
beria
постоялец
Сообщения: 130
Зарегистрирован: 29.09.2016 07:57:13

О производительности ассемблерных вставок...

Сообщение beria »

Задача : побайтовый обмен в массиве, квадратно-гнездовым способом.... Да, я знаю и использую avx, и да, он быстрее, но это не о нем...

Код: Выделить всё

procedure Swap1(Len: ptrUInt; P1, P2: Pointer); nostackframe; inline;
  assembler;
asm
         DEC     RCX
         @L:
         MOV     AL,[RDX+RCX]
         MOV     BL,[R8+RCX]
         MOV     [RDX+RCX],BL
         MOV     [R8+RCX],AL
         LOOP    @L

end ['RCX', 'RDX', 'R8', 'AX'];   

Код: Выделить всё

procedure Swap2 (Len: ptrUInt; P1, P2: Pointer); inline;
var
  j: byte;
  i: ptrUInt;
begin
  Dec(Len);
  for i := Len downto 0 do
  begin
    j := byte(P1^);
    byte(P1^) := byte(P2^);
    byte(P2^) := j;
    Inc(P1);
    Inc(P2);
  end;
end;
Результаты 5000000 прогонов на sizeof(shortstring) байт
1. 2218 мс
2. 907 мс

ИМХО по моему что-то не так с ассемблером в FPC... :o :o :o
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

Как-то кривовато это всё выглядит. Что случится, если в процедуры будет передана нулевая длина? Кроме того, цикл в ассемблерной процедуре кажется выполняет на одну итерацию меньше, чем требуется. Ну и, если правильно путаю, инструкция LOOP дороговатая, её лучше избегать.
С учётом сказанного:

Код: Выделить всё

procedure Swap1(Len: ptrUInt; P1, P2: Pointer); assembler; nostackframe; inline;
asm
  TEST  RCX, RCX
  JZ    @Done
@L:
  MOV   AL,[RDX+RCX]
  MOV   BL,[R8+RCX]
  MOV   [RDX+RCX],BL
  MOV   [R8+RCX],AL
  LOOP  @L
@Done:
end;

Код: Выделить всё

procedure Swap2(Len: ptrUInt; P1, P2: Pointer); inline;
var
  pb1: PByte absolute P1;
  pb2: PByte absolute P2;
  b: Byte;
  I: ptrUInt;
begin
  for I := 1 to Len do begin
    b := pb1^;
    pb1^ := pb2^;
    pb2^ := b;
    Inc(pb1);
    Inc(pb2);
  end;
end; 

Код: Выделить всё

procedure Swap3(Len: ptrUInt; P1, P2: Pointer); assembler; nostackframe; inline;
asm
  test  rcx, rcx
  jz    @Done
@Loop:
  dec   rcx
  mov   al, [rdx+rcx]
  mov   bl, [r8+rcx]
  mov   [rdx+rcx], bl
  mov   [r8+rcx], al
  jnz   @Loop
@Done:
end; 
Результат 5000000 прогонов на sizeof(shortstring):

Код: Выделить всё

Swap1: 1925
Swap2: 889
Swap3: 1029
А вообще-то, чтобы сравнивать сравнимое Swap2 должна выглядеть как-то так:

Код: Выделить всё

procedure Swap2(Len: ptrUInt; P1, P2: Pointer); inline;
var
  pb1: PByte absolute P1;
  pb2: PByte absolute P2;
  b: Byte;
  I: ptrUInt;
begin
  if Len = 0 then exit;
  for I := Len - 1 downto 0 do begin
    b := pb1[I];
    pb1[I] := pb2[I];
    pb2[I] := b;
  end;
end;
и тогда результат:

Код: Выделить всё

Swap1: 1905
Swap2: 1035
Swap3: 1029
Alex2013
долгожитель
Сообщения: 3220
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

??? Народ не понял в чем прикол! :shock:
В начале топикастер как-то странно удивляется, что ассемблеровский код быстрее.
А потом iskander "исправляет" положение и обе асм-вставки работают медленнее кода на паскале.
Последний раз редактировалось Alex2013 03.10.2022 09:37:21, всего редактировалось 1 раз.
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

Alex2013 писал(а):??? Народ не понял в чем прикол!
Так обычно и бывает, если читать невнимательно.
Alex2013
долгожитель
Сообщения: 3220
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

О сори, не увидел этого . :oops:
Swap3: 1029
Аватара пользователя
beria
постоялец
Сообщения: 130
Зарегистрирован: 29.09.2016 07:57:13

Сообщение beria »

Alex2013 писал(а):??? Народ не понял в чем прикол! :shock:
В начале топикастер как-то странно удивляется, что ассемблеровский код быстрее.
ассемблеровский код медленнее. В - значит "внимательность" )
Alex2013
долгожитель
Сообщения: 3220
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

beria писал(а): ассемблеровский код медленнее. В - значит "внимательность" )
Уже заметил ...
Хотя вначале написано .
1. 2218 мс
2. 907 мс
Разумеется я соотнес 1. 2218 мс к Swap1, а 2. 907 мс Swap2.
Зы
Ладно в любом случае извиняюсь.
( Надо было самому проверить, а потом высказаться. Доберусь до лазаруса гляну ! :idea: )
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

beria писал(а):Результаты 5000000 прогонов на sizeof(shortstring) байт
1. 2218 мс
2. 907 мс
Alex2013 писал(а): Разумеется я соотнес 1. 2218 мс к Swap1, а 2. 907 мс Swap2.
Это круто.

Добавлено спустя 16 часов 29 минут 57 секунд:
Хех, совсем забыл, что согласно Windows x64 ABI, RBX не относится к изменяемым регистрам. Забавно, что оно при этом не падало.
В итоге Swap3 должна выглядеть как-то так:

Код: Выделить всё

procedure Swap3(Len: ptrUInt; P1, P2: Pointer); assembler; nostackframe;
asm
  test  rcx, rcx
  jz    @Done
@Loop:
  dec   rcx
  mov   al, [rdx+rcx]
  mov   r9b, [r8+rcx]
  mov   [rdx+rcx], r9b
  mov   [r8+rcx], al
  jnz   @Loop
@Done:
end; 
Аватара пользователя
beria
постоялец
Сообщения: 130
Зарегистрирован: 29.09.2016 07:57:13

Сообщение beria »

iskander писал(а):
beria писал(а):Результаты 5000000 прогонов на sizeof(shortstring) байт
то.

Добавлено спустя 16 часов 29 минут 57 секунд:
Хех, совсем забыл, что согласно Windows x64 ABI, RBX не относится к изменяемым регистрам. Забавно, что оно при этом не падало.
В итоге Swap3 должна выглядеть как-то так:
При том то вы действительно правы, но все равно не падает, где бы не встречалдся в коде ...
И при этом код ниже все равно быстрее на длину shortstring и цикл в 5000000
Причины конечно этого понял, но это не комплементарно к FPC, а кривая и недоделанная реализация там ассемблера, но с другой стороны компилятор в плане оптимизации чуть лучше, чем я думал ранее, правда до лучших образцов все равно не дотягивает.....

Код: Выделить всё

procedure Swap1(var P1, P2); inline;
var
  j: byte;
begin
  j := byte(P1);
  byte(P1) := byte(P2);
  byte(P2) := j;
end;


procedure Swap1(Len: PtrUint; var P1, P2); inline;
var
  i: ptrUInt;
  Pt1, Pt2: Pointer;
begin
  Pt1 := @P1;
  Pt2 := @P2;
  for i := 1 to Len do
  begin
    Swap1(Pt1^, Pt2^);
    Inc(Pt1);
    Inc(Pt2);
  end;
end;     
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

beria писал(а):И при этом код ниже все равно быстрее на длину shortstring и цикл в 5000000
Этот код, вероятно, правильнее сравнивать с чем-то таким:

Код: Выделить всё

procedure Swap2(Len: ptrUInt; P1, P2: Pointer); assembler; nostackframe; 
asm
  test  rcx, rcx
  jz    @Done
@Loop:
  dec   rcx
  mov   al, [rdx]
  mov   r9b, [r8]
  mov   [rdx], r9b
  mov   [r8], al
  lea   rdx, [rdx+1]
  lea   r8, [r8+1]
  jnz   @Loop
@Done:
end;
На моей машине

Код: Выделить всё

Swap1: 905
Swap2: 873 
Аватара пользователя
beria
постоялец
Сообщения: 130
Зарегистрирован: 29.09.2016 07:57:13

Сообщение beria »

iskander писал(а):
beria писал(а):И при этом код ниже все равно быстрее на длину shortstring и цикл в 5000000
На моей машине

Код: Выделить всё

Swap1: 905
Swap2: 873 

for i := 0 to 5000000 do Data.Swap1(sizeof(shortstring),sh1,sh2);875
for i := 0 to 5000000 do Data.Swap2(sizeof(shortstring),sh1,sh2);844

Ассемблер все таки победил! Ура. Мир вернулся в нормальное состояние... ))))))))
зы: кстати а вы не в курсе, вот таким способом "mov r9b, [r8]", можно побайтово получать содержимое 32-х байтовых AVX регистов?
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

Емнип в AVX для извлечения произвольных байтов из регистра была отдельная специально обученная инструкция VPEXTRB.
Seenkao
энтузиаст
Сообщения: 578
Зарегистрирован: 01.04.2020 02:37:12
Контактная информация:

Сообщение Seenkao »

Код: Выделить всё

procedure Swap1(Len: ptrUInt; P1, P2: Pointer); nostackframe; inline;
  assembler;
asm
    test rcx, rcx
    jz @done
    dec rcx
    cmp rcx, 8
    jz @L1
@L2:
    mov rax,[rdx+rcx]
    mov rbx,[r8+rcx]
    mov [rdx+rcx], rbx
    mov [r8+rcx], rax
    sub rcx, 8
    jnc @L2;  //  тут может быть ошибка
    add rcx, 8
@L1:
    mov al,[rdx+rcx]
    mov bl,[r8+rcx]
    mov [rdx+rcx],bl
    mov [r8+rcx],al
    dec rcx
    jnz @L1

@done:

end ['RCX', 'RDX', 'R8', 'AX'];
на коленке накидал.

Исправлено.
Последний раз редактировалось Seenkao 06.10.2022 19:08:34, всего редактировалось 1 раз.
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

Seenkao писал(а):procedure Swap1(Len: ptrUInt; P1, P2: Pointer); nostackframe; inline;
  assembler;
asm
    test rcx, rcx
    jz @done
    dec rcx
    cmp rcx, 4
    jz @L1
@L2:
    mov rax,[rdx+rcx]
    mov rbx,[r8+rcx]
    mov [rdx+rcx],bl
    mov [r8+rcx],al
    sub rcx, 4
    ja @L2;  //  тут может быть ошибка
    add rcx, 4
@L1:
    mov al,[rdx+rcx]
    mov bl,[r8+rcx]
    mov [rdx+rcx],bl
    mov [r8+rcx],al
    dec rcx
    jnz @L1

@done:

end ['RCX', 'RDX', 'R8', 'AX'];

на коленке накидал.
Такое впечатление, что прямо на заднице.
Seenkao
энтузиаст
Сообщения: 578
Зарегистрирован: 01.04.2020 02:37:12
Контактная информация:

Сообщение Seenkao »

iskander писал(а):Такое впечатление, что прямо на заднице.
если только на твоей.

Мне интересно, где ваша голова? Вместо предложения правок, хрень надо обязательно пронести.

Исправил недочёты в коде.

На самом деле надо ещё в начале делать проверку, чтоб данные не накладывались друг на друга.
Ответить