2011-12-19 14 views
5

Chyba wszyscy słyszeliście o "problemie z zamianą"; SO jest pełne pytań na ten temat. Wersja wymiany bez użycia trzeciej zmiennej jest często uważana za szybszą, ponieważ, cóż, masz jedną zmienną mniej. Chciałem wiedzieć, co dzieje się za kurtyną i napisał dwie następujące programy:Zmienna zamiana zi bez zmiennej pomocniczej - która jest szybsza?

int main() { 
    int a = 9; 
    int b = 5; 
    int swap; 

    swap = a; 
    a = b; 
    b = swap; 

    return 0; 
} 

i wersję bez zmiennej trzecie:

int main() { 
    int a = 9; 
    int b = 5; 

    a ^= b; 
    b ^= a; 
    a ^= b; 

    return 0; 
} 

I wygenerowany kod montaż przy użyciu szczęk i dostał to dla pierwsza wersja (która wykorzystuje trzeciej zmiennej):

... 
Ltmp0: 
    movq %rsp, %rbp 
Ltmp1: 
    movl $0, %eax 
    movl $0, -4(%rbp) 
    movl $9, -8(%rbp) 
    movl $5, -12(%rbp) 
    movl -8(%rbp), %ecx 
    movl %ecx, -16(%rbp) 
    movl -12(%rbp), %ecx 
    movl %ecx, -8(%rbp) 
    movl -16(%rbp), %ecx 
    movl %ecx, -12(%rbp) 
    popq %rbp 
    ret 
Leh_func_end0: 
... 

a to dla drugiej wersji (które nie korzysta z trzeciej zmiennej):

... 
Ltmp0: 
    movq %rsp, %rbp 
Ltmp1: 
    movl $0, %eax 
    movl $0, -4(%rbp) 
    movl $9, -8(%rbp) 
    movl $5, -12(%rbp) 
    movl -12(%rbp), %ecx 
    movl -8(%rbp), %edx 
    xorl %ecx, %edx 
    movl %edx, -8(%rbp) 
    movl -8(%rbp), %ecx 
    movl -12(%rbp), %edx 
    xorl %ecx, %edx 
    movl %edx, -12(%rbp) 
    movl -12(%rbp), %ecx 
    movl -8(%rbp), %edx 
    xorl %ecx, %edx 
    movl %edx, -8(%rbp) 
    popq %rbp 
    ret 
Leh_func_end0: 
... 

Druga jest dłuższa, ale nie wiem zbyt wiele na temat kodu zespołu, więc nie mam pojęcia, czy to oznacza, że ​​jest wolniejsza, więc chciałbym usłyszeć opinię kogoś bardziej kompetentnego na ten temat.

Która z powyższych wersji zmiennej swap jest szybsza i zajmuje mniej pamięci?

+4

Aby dowiedzieć się, który jest szybszy, dlaczego nie porównujesz go? –

+0

Nie wiedziałbym, jak zmierzyć użycie pamięci oraz jestem również zainteresowany przyczynami tego problemu. – shutefan

+4

Nie wygląda na to, że kompilowano przy włączonych optymalizacjach. W tym zgromadzeniu jest dużo puchu. –

Odpowiedz

7

Obejrzyj zoptymalizowany zespół. Od

void swap_temp(int *restrict a, int *restrict b){ 
    int temp = *a; 
    *a = *b; 
    *b = temp; 
} 

void swap_xor(int *restrict a, int *restrict b){ 
    *a ^= *b; 
    *b ^= *a; 
    *a ^= *b; 
} 

gcc -O3 -std=c99 -S -o swapping.s swapping.c produkowane

.file "swapping.c" 
.text 
.p2align 4,,15 
.globl swap_temp 
.type swap_temp, @function 
swap_temp: 
.LFB0: 
.cfi_startproc 
movl (%rdi), %eax 
movl (%rsi), %edx 
movl %edx, (%rdi) 
movl %eax, (%rsi) 
ret 
.cfi_endproc 
.LFE0: 
.size swap_temp, .-swap_temp 
.p2align 4,,15 
.globl swap_xor 
.type swap_xor, @function 
swap_xor: 
.LFB1: 
.cfi_startproc 
movl (%rsi), %edx 
movl (%rdi), %eax 
xorl %edx, %eax 
xorl %eax, %edx 
xorl %edx, %eax 
movl %edx, (%rsi) 
movl %eax, (%rdi) 
ret 
.cfi_endproc 
.LFE1: 
.size swap_xor, .-swap_xor 
.ident "GCC: (SUSE Linux) 4.5.1 20101208 [gcc-4_5-branch revision 167585]" 
.section .comment.SUSE.OPTs,"MS",@progbits,1 
.string "Ospwg" 
.section .note.GNU-stack,"",@progbits 

Dla mnie swap_temp wygląda tak wydajna, jak może być.

+0

Niezła, dzięki za optymalizację! Czy jest tak szybki/krótki, jak to robi? Btw, czy robi to jakąś różnicę, jeśli zamiast zmiennych zmienię wskaźniki? – shutefan

+0

Śmiem twierdzić, że 'swap_temp' jest optymalny. Dla 'swap_xor', bez kwalifikatorów' restrict', gcc generuje jedną instrukcję mniej, staje się trzema 'movl a, b; xorl c, d' gdzie w każdym op, jednym z argumentów jest rejestr ('% eax', zawsze) i jeden wskaźnik dereferencji (' (% rsi) 'lub' (% rdi) '). Według moich pomiarów jest wolniejszy (ale jeśli funkcja jest widoczna na stronie połączenia, inline może usunąć różnicę). Odnośnie różnicy między zamianą zmiennych a wskaźnikami swapowymi, zmiennych zmiennych nigdy nie można ukryć, więc optymalizacje często mogą je całkowicie wyeliminować. –

+0

Dobra, dziękuję i przyjmuję odpowiedź! – shutefan

0

Aby zorientować się w kosztach, należy wyobrazić sobie, że każde polecenie ma koszt do wykonania, a także adresowanie pośrednie ma swój własny koszt.

movl -12(%rbp), %ecx 

Linia ta będzie musiała coś w jednostce czasu dostępu wartość w rejestrze ECX, jedną jednostkę czasu dostępu RBP, inny dla stosowania przesunięcia (-12) i więcej czasu jednostek (powiedzmy dowolnie 3) za przeniesienie wartości z adresu zapisanego w ecx na adres wskazany z -12 (% rbp).

Jeśli zliczysz wszystkie operacje w każdym wierszu i całej linii, druga metoda jest z pewnością droższa niż pierwsza.

+1

Jest to prawdą w tym przypadku, ale nie ogólne, ponieważ ignoruje możliwości tworzenia potoków. – gnometorule

+0

Uzgodniono, ale musisz wiedzieć, jak zoptymalizować kod w celu zoptymalizowania potokowania i minimalizacji rozgałęzień. Myślę, że łatwiej naszym przyjaciołom zacząć od zminimalizowania niepotrzebnych odniesień i nadmiernych poleceń, a następnie przejść do bardziej zaawansowanych technik. –

2

Problem z sztuczką XOR Swap polega na tym, że jest ściśle sekwencyjny. Może wydawać się zwodniczo szybki, ale w rzeczywistości tak nie jest. Istnieje instrukcja o nazwie XCHG, która zamienia dwa rejestry, ale może być wolniejsza niż po prostu użycie 3 MOVs, ze względu na jej atomową naturę. Wspólna technika z tempem jest doskonałym wyborem;)

+0

-1 nie ma problemów z synchronizacją z 'xchg reg1, reg2'. Problemy z synchronizacją pojawiają się tylko w 'xchg' z operandami pamięci. – Johan

+0

@Johan whoa, wszechmocny, dobrze wiedzieć, dziękuję! :) (poprawiłem odpowiedź) – ScarletAmaranth

+0

Edytowałeś odpowiedź, ale wciąż jest ona niepoprawna. 'XCHG reg, reg' does ** not ** ma problemy z atomowością, a zatem nie wolniej niż 3' MOV's. W zależności od procesora XCHG może (lub nie musi) rozbić się na wiele mikrooperacji. Tylko 'XCHG reg, [reg]' (który wymienia reg z lokalizacją w pamięci) jest powolny, ponieważ ma dołączony do niego niejawny przedrostek 'LOCK'. Jest to przedrostek 'LOCK', który spowalnia go. – Johan

Powiązane problemy