temp   7년 전

어떤 코드를 분석하다가 

재귀함수에서

int &a=b 라고 표기하는 부분이 있었습니다. b는 전역변수인데요.

궁금한것은 제가 여기서 int a라고만하니 엄청나게 시간이 걸렸고 int &a를 하니 속도가 비약적으로 빨라졌습니다.

지금까지 &를 활용하는건 메모리 절약에 도움이 된다는건 알았지만 속도에도 이렇게 큰 차이를 보일줄은 몰랐는데요..

그 이유가 뭘까요? 단순히 재귀안에서 사용하는데도 이렇게 큰 차이를 내는 이유가 뭔가요?


예시소스는 다음과 같습니다.

int b=20;

void foo()

{

      int &a=b;

}

indioindio   7년 전

컴파일러가 a가 사용되는 부분을 그냥 b로 치환하고 int &a = b 를 하는 코드를 삭제한게 아닐까 싶네요.

isac322   7년 전

c++에서 int &a = b을 해석하면,
a는 b 그 자체 (같은 주소)를 의미합니다.
하지만 int a = b에서 a는 b와 같은 값을 가지는 다른 변수입니다.
따라서 아래의 경우에는 메모리할당, b로부터 값 복사가 실행되지만, 첫번째는 없기 때문에 속도 측면에서만 레퍼런스를 보면 더 빠르죠

yukariko   7년 전

@isac322 대부분의 컴파일러에서 레퍼런스의 동작은 내부적으로 포인터의 동작과 같게 구현되어 있습니다.

참조: http://stackoverflow.com/questions/57483/what-are-...

따라서 int a = b와 int& a = b는 둘 다 같이 메모리 할당, 값 복사가 이루어질 것입니다. (후자는 주소 복사) 

다음은 레퍼런스를 사용했을 경우와 그렇지 않은 경우의 어셈블리 명령어들입니다. (g++ 4.8.4 64bit)

코드의 구현은 위 글과 같은 형태를 띄고 있습니다.


레퍼런스를 사용 (레퍼런스로 접근)

.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq $b, -8(%rbp)
movq -8(%rbp), %rax
movl (%rax), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf


레퍼런스 사용 x(전역에서 바로 접근)

.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl b(%rip), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf


여기서 b가 전역변수입니다.

보시다 싶이 레퍼런스를 사용할 경우 메모리 공간을 더 할당하는것을 볼 수 있습니다.

그렇다면 왜 성능의 차이가 존재하는것인가?

그 답은 O2 최적화에 있습니다.

@indioindio 님의 말씀대로 O2 최적화에서 레퍼런스를 사용하지 않게 코드를 삭제했기 때문에 성능에 변화가 없는것입니다.


다음은 레퍼런스를 사용한 코드를 O2 최적화를 통해 컴파일 했을때의 어셈블리 명령어들을 보여줍니다.


.cfi_startproc
movl b(%rip), %edx
movl $.LC0, %esi
movl $1, %edi
xorl %eax, %eax
jmp __printf_chk
.cfi_endproc


결론은 레퍼런스는 내부적으로 포인터로 동작하며 성능의 차이는 최적화에 의한것이다 입니다. (물론 표준에서 레퍼런스의 상세한 구현은 다루고 있지 않기 때문에 컴파일러마다 다를 수 있습니다.)

yukariko   7년 전

그리고 사실 int형 변수 하나의 메모리할당과 값복사는 시간이 거의 들지 않습니다.

제 생각에 저 차이로 인해 시간차이가 발생한 이유는 캐시 hit에 의한것으로 생각됩니다.

위에 언급한대로 O2 최적화를 통해 레퍼런스 변수는 b로 치환이 되어 사용됩니다. 따라서 b의 주소가 CPU의 캐시에 등록되어있다면

b를 접근할 때마다 계속 hit가 발생하여 성능이 향상될 것입니다.

하지만 레퍼런스가 아닌 일반 변수를 통해 값을 복사했다면, b를 접근할 경우와 a를 접근할 경우 두 변수의 메모리 공간이 다르기 때문에 캐시 hit fail이 발생할 수 있습니다.

이 차이가 성능에 영향을 미치고 있다고 생각됩니다.

댓글을 작성하려면 로그인해야 합니다.