C/C++ 배열 접근 옵티마이징 테스트 결과 해석 좀 부탁 드려요.

ssehoony의 이미지

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: 
첨부파일 크기
Plain text icon TestCode.cpp.txt1.48 KB
longin 귀찮아의 이미지

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 에 의해서 설명이 가능하지 않을까 조심스럽게 추측합니다.

winner의 이미지

추가로 쓰겠습니다.

...
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 입력으로 인하여 입출력 연산자를 쓰면 곤란하군요.
글이 고스란히 저장되어 있기는 한데 화면에 안보이니...

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.