DNA 포럼 API 서비스 모음 DNA Lens

C/C++에서 integer변수 swap 방법 비교


  • 박동식 (검색포털본부 신규검색엔진개발팀)

초록(Abstract)

C++에서 integer변수의 swap을 할때 어떤 방법이 가장 빠른지 문득 궁금증이 들어서 테스트해보았습니다.

서론 #

  • 변수를 바꿔치기 하는데 일반적으로 사용하는 방법인 std::swap, xor를 이용한 swap macro, tmp변수를 이용한 swap방법 세가지의 속도를 비교해보았습니다.

std::swap #

  • std::swap : c++에서 일반적으로 가장 널리쓰이는 안전한 방법

#include <iostream>
#include <algorithm>
#include<stdio.h>

int main(void)
{
    int a = 1;
    int b = 2;
    for(int i=0;i<50000000;i++)
    {
        std::swap(a,b);
    }

    return printf("%d %d",a, b);
}

결과
$ time ./swap.out
1 2
real    0m0.560s
user    0m0.557s
sys     0m0.003s

-O2옵션을 붙혀 테스트한 결과
$ time ./a.out
1 2
real    0m0.048s
user    0m0.047s
sys     0m0.001s

xor를 이용한 방법 #

  • c에서 예전에 많이 사용되던 방법 임시변수를 사용하지 않아서 좋다는 장점이 있음.
  • 오래된 CPU에서는 이 방법이 빨랐으나 최근 CPU에서는 bit operation이 느려서 그다지 좋지 않다고 알려져있습니다.

#include <iostream>
#include <algorithm>
#include<stdio.h>

#define SWAP(a,b) { if (a!=b) { a^=b; b^=a; a^=b; }}

int main(void)
{
    int a = 1;
    int b = 2;
    for(int i=0;i<50000000;i++)
    {
        SWAP(a,b);
    }

    return printf("%d %d",a, b);
}

결과
$ time ./xor.out
1 2
real    0m0.504s
user    0m0.502s
sys     0m0.003s
-O2옵션을 붙혀 테스트한 결과
$ time ./a.out
1 2
real    0m0.071s
user    0m0.070s
sys     0m0.001s

임시변수를 이용한 방법 #

  • 가장 기본적인 방법. 임시변수를 사용한다는 단점이 있습니다.
#include <iostream>
#include <algorithm>
#include<stdio.h>

#define SWAP(a,b) { int t=a; a=b; b=t; }

int main(void)
{
    int a = 1;
    int b = 2;
    for(int i=0;i<50000000;i++)
    {
        SWAP(a,b);
    }

    return printf("%d %d",a, b);
}

결과
$ time ./a.out
1 2
real    0m0.274s
user    0m0.272s
sys     0m0.002s
-O2옵션을 붙혀 테스트한 결과
$ time ./a.out
1 2
real    0m0.048s
user    0m0.046s
sys     0m0.002s

-O2를 붙였을 경우 std::swap과 임시변수는 성능이 거의 같았음.

결론 #

  • 최근의 cpu환경에서 integer를 swap하는경우 그냥 임시변수를 선언하여 swap하는게 빠르다는 결론을 얻을 수 있었습니다.
  • 단순히 룹을 돌며 테스트한거라 실제 프로그램 수행중엔 조금 다르게 동작할 수 있을것으로 생각됩니다.
  • integer에 한정된 테스트결과임으로 다른 타입인경우 달라질 수 있습니다.
  • 옵티마이즈 옵션을 켜고 돌려보았습니다. std::swap과 SWAP매크로의 성능이 거의 비슷한것을 확인할 수 있었습니다.

근소한 차이가 궁금하여 옵티마이징후의 어셈코드를 분석해봤습니다.

std::swap
.L6:
        movl    %edi, %edx     // edi:a edx:tmp a->tmp
        decl    %eax           // loop counter
        movl    %ecx, %esi     // ??
        movl    %ecx, %edi     // ecx:b edi:a   b->a
        movl    %edx, %ecx     // edx:tmp ecx:b tmp->b
        jns     .L6

swap macro
.L5:
        movl    %ecx, %esi     // ecx:a esi:tmp a->tmp
        decl    %eax           // loop counter
        movl    %edx, %ecx     // edx:b ecx:a   b->a
        movl    %esi, %edx     // esi:tmp edx:b tmp->b
        jns     .L5

std:swap에서는 %esi레지스터 값을 업데이트 하는데 function call을 옵티마이즈 하면서 남은 더미가 아닐까 싶습니다. g++이 아닌 다른 컴파일러에서 옵티마이징을 할 경우 다른 결과가 나올 수 있을것으로 생각합니다.