Co będzie wydajniejsze na x86?

0

Mam dwa kody (architektura x86):

fld tword [pewna stała]
fstp tword [inna stała]

oraz

mov eax, dword [pewna stała+0]
mov ebx, dword [pewna stała+4]
mov cx, word [pewna stała+8]
mov dword [inna stała+0], eax
mov dword [inna stała+4], ebx
mov word [inna stała+8], cx

Pytanie brzmi - który z nich jest wydajniejszy?
Ogólnie ich celem jest skopiowanie 80-bitowego floata z jednego miejsca w pamięci do drugiego; niby mógłbym wykonać jakiś benchmark, lecz odnoszę wrażenie, że na takim krótkim kodzie miałbym problemy z mierzeniem czasu (:P), stąd wolę zapytać znawców ;)

0

Nie wiem co będzie szybsze, ale jak już robić benchmarki to porównać też z tym:

mov esi, pewna stała
mov edi, inna stała
cld
movsd
movsd
movsw
2

GCC generuje wersję drugą, pewnie nie bez powodu.
Dodatkowo dokumentacja Intela wspomina, że instrukcje x87 mają dodatkowy narzut czasowy.
Jeśli możesz skorzystać SSE to proponowałbym wyrównać zmienne do 16 bajtów i użyć movdqa.
Ale dlaczego chcesz w ogóle to optymalizować? Przy takiej ilości danych zauważalnej różnicy nie będzie. Chyba, że chcesz przenosić wiele takich long double'i to napisz, bo to trochę zmienia postać rzeczy.

EDIT:
OK, zrobiłem benchmarki. Oto wyniki:

[bits 32]
[section .text]
[global main]
main:
mov ecx, 0xffffffff
l1:
   fld tword [src]
   fstp tword [dst]
   loop l1
xor eax, eax
ret
 
[section .data]
src: dt 128.256
dst: dt 0.0
real        0m11.470s
user        0m11.408s
sys        0m0.000s
[bits 32]
[section .text]
[global main]
 
main:
mov ecx, 0xffffffff
l1:
   mov eax, dword [src+0]
   mov ebx, dword [src+4]
   mov dx, word [src+8]
   mov dword [dst+0], eax
   mov dword [dst+4], ebx
   mov word [dst+8], dx
   loop l1
xor eax, eax
ret
 
[section .data]
src: dt 128.256
dst: dt 0.0
real        0m7.718s
user        0m7.720s
sys        0m0.000s
[bits 32]
[section .text]
[global main]
 
main:
mov ecx, 0xffffffff
l1:
   mov esi, src
   mov edi, dst
   cld
   movsd
   movsd
   movsw
   loop l1
xor eax, eax
ret
 
[section .data]
src: dt 128.256
dst: dt 0.0
real        0m26.316s
user        0m26.328s
sys        0m0.000s

To mnie nieźle zaskoczyło.

[bits 32]
[section .text]
[global main]
 
main:
mov ecx, 0xffffffff
l1:
   movdqa xmm0, [src]
   movdqa [dst], xmm0
   loop l1
xor eax, eax
ret
 
[section .data]
align 16
src: dt 128.256
align 16
dst: dt 0.0
align 16
real        0m6.388s
user        0m6.344s
sys        0m0.000s

Czyli wersja z SSE jest faktycznie najszybsza (przynajmniej na moim komputerze).

1
lukasz1235 napisał(a)

Ale dlaczego chcesz w ogóle to optymalizować?

Piszę kompilator JIT i zastanawiałem się, które z tych dwóch rozwiązań będzie wydajniejsze.

Czyli wersja z SSE jest faktycznie najszybsza (przynajmniej na moim komputerze).

Niestety te moje adresy nie są wyrównane do 16 bajtów (oraz miałbym spory problem próbując je zaalokować na takich adresach), więc użycie tego odpada :P

Wniosek: wersja ze zwykłymi mov wypada lepiej, niż zaciąganie do tego FPU - pora więc zmienić parę rzeczy w kompilatorze ;)

3
Patryk27 napisał(a):

Niestety te moje adresy nie są wyrównane do 16 bajtów (oraz miałbym spory problem próbując je zaalokować na takich adresach), więc użycie tego odpada :P
Skoro piszesz kompilator to zaimplementowanie wyrównania jest bardzo ważne dla wydajności:
Assembler/Compiler Coding Rule 46. (H impact, H generality) Align data on natural operand size address boundaries. If the data will be accessed with vector instruction loads and stores, align the data on 16-byte boundaries.
Ogólnie polecam przeczytać "Intel® 64 and IA-32 Architectures Optimization Reference Manual".

0

Kiedyś czytałem o tym, że wyrównanie adresów zwiększa wydajność, lecz wypadło mi z głowy - dzięki, pora nieco odświeżyć pamięć :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1