C/C++ 배열 접근 옵티마이징 테스트 결과 해석 좀 부탁 드려요.
글쓴이: ssehoony / 작성시간: 일, 2006/04/30 - 5:30오후
C/C++ 코드 옵티마이징 방법중 꽤 대표적이라고 할 수 있는
Loop 내에서 배열 접근시 배열 인덱스 값이 아닌 포인터를 이용해서
접근하는 것이 더 좋다 라는 것에 대해
이론적으로 의심할 여지가 없으나 실제 실험에서 잘 나오나 궁금해서
그동안 의심하지 않았던 이론을 실제 코드로 테스트를 해봤습니다.
그런데 잘 이해가 가지 않는 결과가 나왔는데요
실험 결과는 다음과 같습니다.
No Optimize Array Pointer => 6500 ms Array Index => 7437 ms Optimize Array Pointer => 1359 ms Array Index => 953 ms
위 실험은 int 형 배열 100000 사이즈의 배열을 대상으로, 반복을 10000번씩 했을 때의 결과입니다.
실험 환경은 Windows XP, Visual Studio 2005, Release Mode, CPU 3.0GHz, RAM 1GB 입니다.
위는 여러번 테스트한 것의 평균값입니다.
시간을 얻기 위해서 GetTickCount() 함수를 이용했습니다.
컴파일 옵션의 유일한 차이는 옵티마이징 옵션의 On/Off 여부 입니다.
No Optimizing 일때는 예상한대로 결과가 나오지만 Optimizing 일때는
예상과 반대의 결과가 나왔습니다.
게다가 Optimizing 과 No Optimizing 의 시간차도 너무 크다는게 좀 이상합니다.
제가 테스트한 소스 코드는
DWORD CTestCode::ArrayAccessByIndex(const int repeat, const int arraySize) { ASSERT(arraySize > 0); ASSERT(repeat > 0); int* data = new int[arraySize]; m_Dummy.resize(arraySize); // 배열 초기화 for(int i = 0; i < arraySize; ++i) { data[i] = i; } // 시작 시간 기록 DWORD start = GetTickCount(); for(int j = 0; j < repeat; ++j) { int max = data[0]; int min = data[0]; int sum = 0; for(int i = 0; i < arraySize; ++i) { max = max(data[i], max); min = min(data[i], min); sum += data[i]; } m_Dummy.push_back(max + min + sum); } // 종료 시간 기록 DWORD end = GetTickCount(); return end - start; } DWORD CTestCode::ArrayAccessByPtr(const int repeat, const int arraySize) { ASSERT(arraySize > 0); ASSERT(repeat > 0); int* data = new int[arraySize]; int* it = data; int* itE = data + arraySize; m_Dummy.resize(arraySize); // 배열 초기화 for(int i = 0; i < arraySize; ++i, ++it) { *it = i; } // 시작 시간 기록 DWORD start = GetTickCount(); for(int j = 0; j < repeat; ++j) { int max = data[0]; int min = data[0]; int sum = 0; for(it = data; it != itE; ++it) { max = max(*it, max); min = min(*it, min); sum += *it; } m_Dummy.push_back(max + min + sum); } // 종료 시간 기록 DWORD end = GetTickCount(); return end - start; }
입니다.
테스트 코드에 보면
m_Dummy.push_back(max + min + sum);
이런 코드가 있는데
이건 옵티마이징에 의한 Loop Unrolling 일 발생하지 않도록 하기 위한
장치이며 별다른 기능없는
vector<int>
입니다.
이런 결과에 대해 해석 좀 해주시면 감사하겠습니다.
에궁... 코드 인덴트가 이상하게 표시되는군요.
File attachments:
첨부 | 파일 크기 |
---|---|
![]() | 1.48 KB |
Forums:
매우 micro 한 최적화군요.
compiler 에 의한 최적화가 발달한 현재 이런 미세한 직접적인 source 최적화는 의미가 없다는 것이 보통입니다.
즉
i++;
++i
i += 1;
i = i + 1;
중 무엇이 가장 빠른가를 묻는 것과 같습니다.
또는 개행을 할 경우
printf("\n");
putchar('\n');
cout << '\n';
cout << endl;
의 차이를 묻는 것도 같은 문제입니다.
하지만 computer 는 물론이고 compiler 또한 사람이 만든 것이니 경우에 따라 희한한 결과를 낳을 수도 있기는 합니다.
어찌되었든 현재 위의 증가연산과 말씀하신 배열참조비교는 별 차이 없는 결과를 냅니다. 달리 말하면 compiler 나 hardware 에 따라 달라진다고 할 수 있습니다.
개행의 경우에는 함수를 호출하니 조금 더 의미있는 차이를 보여줄지 모르겠습니다.
예전에 전웅씨의 글에서 비슷한 예를 본 적이 있는데 잘 검색이 안되더군요.
pointer 연산을 한 것이 index 연산을 하는 것보다 좋다는 것은 결국 근거는 없는 믿음일 뿐입니다.
source 의 해석의 관점에서 보면 이 차이 역시 사람들의 개인차일뿐 객관적인 우열은 없다고 생각합니다. 오히려 pointer 에 경기일으키는 사람이 많죠. 저는 C++ STL 을 많이 쓰다보니 pointer 의 iterator 와 같은 표현을 즐깁니다만...
(이것은 댓글로서라기 보다는 두가지의 부차적인 영향에 대한 제 생각입니다. 즉 저로서는 index 표현을 추천합니다.)
기억이 잘 나지 않습니다만 Intel CPU 의 경우 index 연산이 오히려 빠른 결과를 낳는다는 글을 위에서 말한 전웅씨의 글에서 본 것 같습니다.
strlen 의 예였는지 strcpy 의 예에서였는지 기억이 가물가물...
즉 CPU 내부에서 벌어지는 최적화 단계에서 두가지의 결과를 이해할 수 있을 거라고 봅니다. 음.. cache hit rate 로는 설명이 안될 것 같고 pipeline 과 register 에 의해서 설명이 가능하지 않을까 조심스럽게 추측합니다.
제기랄 글 짤렸다.
추가로 쓰겠습니다.
...
cout '\n';
cout endl;
중 무엇이 빠른가를 묻는 것 과 같습니다.
이러한 것은 hareware 나 compiler 에 따라 다른 결과(source 상에서의 최적화보다 더 큰 의미를 가지는)를 혹은 완전히 같은 결과를 낼 수 있습니다. 즉 pointer 연산이 index 보다 좋다는 것은 근거없는 믿음입니다.
예전에 전웅씨의 글에서 비슷한 예를 본적이 있는데 잘 검색이 안되네요.
strlen 의 예였는지. strcpy 의 예였는지 기억이 잘 안 나는군요..
하지만 computer 는 물론이고, compiler 또한 사람이 만든 것이니 의미있는 차이를 낼 수도 있을 겁니다. 특히 개행의 경우 함수를 호출하니 의미있는 차이를 보여줄 공산은 커보입니다.
위에서 말한 전웅씨의 글에서 Intel CPU 의 경우 index 연산이 오히려 빠른 결과를 낳는다는 것을 봤던 것 같습니다.
즉 CPU 내부의 최적화단계에서 설명을 할 수 있을텐데 cache hit rate 는 아닐 것 같고, register 와 pipeline 에 의한 설명이 가능하지 않을까 조심스럽게 추측합니다.
부차적으로 SE 관점에서 pointer 와 index 에 의해 source 의 해석관점의 차이역시 개인차일뿐 객관적인 차이는 없다고 생각합니다. 저는 C++ STL 을 많이 쓰다보니 pointer 의 iterator 와 같은 표현을 즐겨씁니다만 보통 pointer 에 경기일으키는 사람이 많죠. 결국 저는 index 표현을 추천합니다.
음 결국 HTML 입력으로 인하여 입출력 연산자를 쓰면 곤란하군요.
글이 고스란히 저장되어 있기는 한데 화면에 안보이니...
댓글 달기