PC최적화와 관련 소프트웨어에 대한 내용은 최적화 프로그램 문서 참고하십시오.
1. 개요
소프트웨어의 효율성을 높이는 일.적은 자원으로 높은 효율을 낸다면, 소프트웨어 최적화가 잘 되었다고 말한다. 최적화된 프로그램들은 최적화되지 않은 프로그램들에 비해 메모리를 적게 사용하거나 실행 시간이 줄어드는 이점이 생긴다. 최적화가 제대로 안 되었다면 많은 자원을 먹는데도 영 좋지 않은 효율이 나오고, 발적화나 개적화 등의 표현으로 불리며 까인다.
좁은 범위로는 기기의 성능을 업그레이드 시키는 펌웨어 업그레이드부터, 넓게는 윈도우즈 시리즈의 서비스팩 급과 같은 대규모 업데이트까지도 포함된다.
임베디드 시스템 프로그래밍에서는 속도와 용량 사이의 상반관계 [1]를 감안해야 하기 때문에 어느 쪽을 우선시 하느냐에 따라 최적화의 방향이 달라질 수도 있다. 그래서 윈도우 임베디드 컴팩트를 연구하는 이들에게는 뼈저리게 다가오는 단어다.
사용자 경험에 가장 크게 기여하는 개념이기도 하다. 대표적인 예로 조립 컴퓨터가 있다. 아무리 고성능 부품들만 써도 최적화가 잘 안 되어 있으면 오히려 성능이 떨어지는 모습을 보인다.
단, 어느 쪽이든 하드웨어를 바꾸는 업그레이드는 예외. 한정된 성능에서 최적의 모습을 보여주어야 하기 때문에 별 의미가 없다. 몇몇 회사들은 최적화에 한계가 있는 부분을 하드웨어를 업그레이드하여 커버하기도 한다. 이를 하드웨어로 최적화한다고 표현하기도 하지만 그런 개념은 없다.
프로그램 규모가 커지면 개발중 컴파일 자체에 대해서도 최적화가 필요한데, 소스코드가 많아지고 빌드 규모가 커지면 빌드타임이 수 분을 넘는 경우도 많아 테스트 런을 돌려볼때마다 시간을 잡아먹기 때문이다. 보통 라이브러리 등을 이용한 모듈화, 컴파일용 원시 소스 코드 파일 수 줄이기[2], 멀티코어 컴파일 등을 통해 해결하며, 이 정도로 모자랄 경우에 결국 하드웨어 수를 늘려 분산 컴파일 시스템을 구축하게 된다.
2. 분야별 특징
최적화를 잘 하기로 유명한 기업에는 애플 [3]과 심비안 버프의 노키아가 있다. 국내에서는 코원이 유명하다.2.1. 군사 & 우주용 CPU
이쪽 분야에서는 1980년대부터는 절대로 최신 CPU를 구입하지 않고 오래된 구식 CPU를 쓴다. [4] 시중엔 스레드리퍼 같은 64코어 프로세서까지 나왔는데도 펜티엄급 프로세서를 사용한다. 최신 CPU일수록 성능은 좋은 대신 수명과 내구도는 떨어지는 경향이 있다. 신형일수록 민감도가 심해져 극한의 환경을 버틸 수 없다. 회로 선폭이 좁아질수록 집적도와 속도가 향상되지만 물리적인 내구성은 당연히 취약해질 수 밖에 없다. 이런 이유로 선폭이 굵은 구식 회로 설계를 한 예전 CPU를 쓰는 것이다. 물론 상용 CPU를 그대로 쓰는 것이 아니라, 회로 구조를 유지한 채 웨이퍼의 재료로 실리콘 대신 튼튼한 사파이어( Silicon on Sapphire)를 사용하는 등 여러가지 방식으로 회로의 내구성을 향상시키는 처리를 한다. 이런 처리를 거친( Radiation-Hardened) 제품은 속도를 일부 희생하는 대신 통상 실리콘 소자쯤은 순식간에 망가뜨리는 우주 배경 방사선을 맞아도 정상 작동하는 엄청난 내구성을 자랑한다.2.2. 게임 프로그래머
밸브 코퍼레이션이 최적화로 유명한 편이고, 크라이텍도 2011년부터 최적화로 유명해졌다. 크라이시스 2가 전작과는 달리 높은 그래픽에 비해 최적화가 잘 되어있기 때문. EVE 온라인의 경우 한 때 서버의 발적화로 욕을 많이 먹었다가 발적화를 싹 해결한 프로그래머가 (플레이어 중에 IT 업계 종사자가 많은 덕분에) 슈퍼스타 취급을 받기도 했다. 그리고 락스타 게임즈의 Grand Theft Auto IV는 심각한 개적화였지만 후속작인 Grand Theft Auto V는 엄청난 신적화로 환호를 받았다. 또한 프로스트바이트 엔진을 사용한 게임들( 배틀필드 4, 등)도 최적화가 잘 되어있는 편이다. 블리자드 엔터테인먼트 게임들의 경우 들쭉날쭉한 편. 와우, 디아블로 3와 오버워치는 어지간한 컴퓨터에서는 다 돌아가는 이른바 갓적화로 칭송받는다. 그러나 히어로즈 오브 더 스톰과 스타크래프트 2의 경우 엔진 자체의 문제 때문에 컴퓨터를 민감하게 타는 편이라 최적화가 좋다고 하기 어렵고, 하스스톤의 경우에도 PC와 모바일 모두 그다지 뛰어나지 않은 편. 가이진 엔터테이먼트의 워썬더 또한 최적화로 유명하다.EA DICE 역시 최적화에 있어 상당한 실력을 보여주고 있으며, 특히 스타워즈: 배틀프론트는 신적화라는 반응까지 나왔다. 배틀필드 1은 몇 가지 논란이 있기는 했지만 전체적으로 보면 훌륭한 최적화가 이루어졌다.
소프트웨어 최적화 전문가로서 가장 유명하고 장수하는 인물로는 마이크로소프트에서 존 카맥의 이드 소프트웨어로 스카우트되어 퀘이크 엔진을 최적화했던 전적의 마이클 압래쉬(Michael Abrash)가 있다. 현재까지도 최적화 최고수로 인정받고 있으며, 코드 한 줄 한 줄, 어셈블리어 하나 하나를 극한까지 쥐어 짜내는 것으로 유명하다.
닌텐도의 경우는 조금 특수한 사례인데, 하드웨어를 값싸게 보급하고 게임 판매로 수익을 메꾸는 경쟁사의 방식은 정면으로 거부하고 하드웨어 판매로도 최소한의 수익을 내도록 하는 정책 때문에 닌텐도 DS부터 Wii U에 이르기까지 닌텐도의 하드웨어는 동세대 경쟁사 기기에 비해 성능 상으로 크게 뒤쳐졌었다. 여기서 끝이 아니고 요코이 군페이로부터 내려져오는 '고사한 기술의 수평사고' 철학에 따라 DS의 듀얼 스크린, Wii의 모션 인식, 3DS의 3D 기능 등 닌텐도의 하드웨어에는 항상 타사에는 없는 특수한 기능이 달려서 나왔다. 때문에 닌텐도 하드웨어는 게임 개발자에게 높은 최적화 역량을 요구하였으며, 닌텐도의 정책 자체도 서드 파티 개발사에게 불친절한 편이어서 조악한 개발 툴은 물론 서드 파티에게는 개발에 필요한 최적화 기술조차도 완전히 알려주지 않을 정도였다. 때문에 서드 파티 입장에서는 닌텐도 퍼스트 파티 게임 홀로 판매량을 견인하는 플랫폼에 비집고 들어가기 위해 극단적인 최적화, 경량화는 물론 기기 고유의 기능마저 살리는 것은 도박이나 다름없었고, 특히나 멀티 플랫폼 게임이라면 굳이 퀄리티를 깎아가며 악평을 감수하기보다는 그럴 시간에 PC 버전이라도 준비하는 것이 나았다. 반대로 닌텐도 퍼스트 파티 입장에서는 이미 기존까지 만들어왔던 하드웨어가 다 그런 식이니 베테랑 개발자들에 의해 축적된 최적화 기술도 충분했고, 애초에 닌텐도 게임기에서만 구동할 목적으로 만드는 게임이니만큼 타사처럼 저성능 환경에 맞춰서 스케일을 줄이고 경량화를 할 지 말 지 다툴 여지조차도 없었다. 결과적으로 닌텐도 게임기로 출시되는 타이틀은 닌텐도 퍼스트 파티 타이틀의 경우 수준높은 최적화는 물론 하드웨어의 특징까지도 잘 살리는 최고의 퀄리티를 보여주었지만, 반대로 서드 파티 타이틀의 경우 발적화로 나오기 일쑤였다. 이러한 문제는 닌텐도 스위치가 닌텐도의 개발자조차 '최적화를 위한 어떠한 꼼수도 부리지 않았던 적은 스위치가 처음'이라고 언급할 정도로 기존까지의 '닌텐도 게임기=저성능' 공식을 깨는 준수한 성능으로 나오면서 크게 개선되었다. 무엇보다도 불친절한 개발 환경에서도 탈피해 SDK는 OpenGL과 Vulkan API를 네이티브로 지원하는 것은 물론 언리얼 엔진, 유니티 엔진 등의 메이저 게임 엔진의 참여를 통해 기존의 PC 게임이나 모바일 게임 개발에 사용했던 대부분의 기술도 그대로 사용할 수 있었다. 덕분에 떠나갔던 서드 파티들도 조금이나마 돌아와서 크게 밀리지 않는 퀄리티의 타이틀을 발매하고 있다.
3. 주 최적화 기법
-
알고리즘 개선
최적화의 기본 전제가 되는 것은 적합한 알고리즘이다. 알고리즘이 쓸데없는 낭비 없이 최적의 상태로 설계된 상태에서나 프로그램 코드 튜닝이 의미가 있지, 비효율적인 알고리즘을 쓰면서 온갖 최적화 기법을 동원해 봐야 전혀 의미가 없다. 극단적인 경우엔 1%의 성능을 높이기 위하여 99%의 시간을 쓰게 될 것이다.
-
병목 현상(bottleneck) 제거
대부분의 프로그램은 프로그램이 사용하는 모든 부분이 느리기 보다는, 조그마한 부분에서 50% 이상 느려지는 등의 병목이 발견되는 경우가 많다. 이러한 병목 현상을 줄이는 것은 적은 노력으로 엄청난 성능 향상을 가져올 수 있다.
3.1. 마이크로 튜닝
최적의 알고리즘을 사용하고 주요 병목을 제거했는데도 시원치 않을 경우 군데군데를 손 봐서 성능을 튜닝한다. 이런 최적화는 코드의 가독성이나 유지보수성을 등가 교환해야 하는 경우가 많다. 이 상반 관계를 감수하는 것은 프로그래머의 몫. 일반적으로 병목을 제거하는 것만으로도 충분히 빨라졌다면 튜닝을 굳이 할 필요는 없다. [5] 반면 그래도 좀 더 나은 속도가 필요하다면 다음과 같은 튜닝을 시도하기도 한다.- 하위 레벨 언어로 최적화
- (2000년대 초중반까지) 구문을 인라인 어셈블리로 대체하여 컴파일러가 할 수 없는 수준의 최적화를 시행한다.
그러나 2000년 이후 일반적인 환경에서 이런 작업을 필요로 하는 경우는 거의 없다. 컴파일러 최적화 기술의 발전에 따라 프로그래머에 준할 정도로 똑똑해졌기 때문이다. 레지스터는 많아졌고, cpu의 구조는 훨씬 복잡해졌다. 컴파일러가 할 수 없는 수준의 최적화를 할 수 있는 프로그래머는 얼마 되지 않는다. 2020년대 기준으로 컴파일러보다 뛰어난 코드를 짤 가능성이라도 있는 사람은 컴파일러 전공 박사나 아키텍처 제작자 정도밖에 없다. 허나 이 사람들도 인라인 어셈블리가 가진 치명적인 문제 [6]를 누구보다도 잘 알기 때문에 원하는 어셈블리가 생성되도록 C언어를 이리저리 조작하는 게 거의 전부이다. 근래에는 특정 하드웨어가 지원하는 인스트럭션을 쓰고 싶다거나 (예시로 rdtsc, cpuid), 하드웨어를 직접 조작해야 하는 커널 드라이버 혹은 임베디드 프로그래밍의 특정한 부분, 인트린식으로 컴파일러의 최적화를 도와주거나 SIMD를 이용하는 정도가 한계.
-
작은 크기의 자료형을 쓰면 캐시에 더 많이 담을 수 있다.
메모리 및 캐시 등의 자원이 매우 한정적일 때 사용하는 방법. 예를 들어서 최대 10000이 들어갈 변수에 int형 대신 short int를 쓰는 식이다. 2000년 이후에는 이런 식으로는 사실상 속도 차이가 안 나지만, 임베디드 환경처럼 제한된 자원만을 사용해서 최대한의 성능을 내야 하는 경우에는 사용될 수 있다. 몇 MB가 넘는 아주 큰 배열에 대해 SIMD를 사용한 수학적 연산에서는 어느 정도 효과를 발휘할 수는 있다.
단, 단점이 있다. CPU는 데이터를 1Word 단위로 처리한다. 이 크기를 벗어나는 데이터를 처리하는 과정에서는 오버 헤드가 발생하게 되는데, 이는 1Word 단위보다 작은 char, byte, short 등에도 적용된다. 즉, 메모리 효율성과 접근 처리 시간 효율성 중에서 선택을 해야 하는 것이다. 예상되는 값의 크기가 낮다고 해서 무조건 작은 데이터형을 쓰는 것은 아니란 것.
-
멀티 스레드 프로그래밍을 한다.
코어의 개수에 비례하는 성능 향상을 얻을 수도 있다. 하지만, 프로그래밍을 정말 잘하지 않으면 오류가 발생하거나 오히려 성능이 떨어지기 쉽다. 멀티 스레드 구현 시 Lock 문제가 필연적으로 따라오고 그 부분이 성능 병목이 된다. 그래서 싱글 스레드와 멀티 스레드 모두에서 성능을 끌어내는 알고리즘은 거의 불가능하다. 굳이 둘 다 지원하려면 매크로 떡칠해서 두 개의 알고리즘을 각각 만드는 게 보통이다. 따라서 멀티 스레드를 지원하려면 C++의 경우 Intel TBB(Threading Building Blocks) 같은 멀티 스레드용 외부 라이브러리를 사용해야 한다. TBB는 싱글 스레드 중심으로 개발된 라이브러리의 메모리 할당자를 덮어쓰거나 성능을 개선해주는 매우 빠른 할당자를 지원하며, 멀티 스레드 자료구조 구현이 되어 있다. 그게 아니라면 세부 구현을 직접 짜야 한다. 싱글 스레드 중심으로 개발된 라이브러리의 템플릿을 바꾸어 메모리 할당이나 비교 연산 등을 제어할 수 있다. 또 Lock을 걸어주는 래핑 클래스를 따로 만들어주는 방법도 있다. 또한 처리해야 할 데이터의 크기가 작으면 스레드를 만드는 부하가 처리 속도 향상보다 더 커져서 작업 단위를 쪼갤수록 더 느려지는 현상이 발생할 수 있다.
3.2. 컴파일러에서의 최적화
현대의 컴파일러들은 충분히 똑똑해져서, 적당한 설정을 해 주면 프로그래머를 대신하여 어느 정도 최적화를 해 준다. [7] 다만 최적화된 실행 파일은 프로그래머가 만든 소스 코드와 달라져[8] 이 때문에 DWARF나 PDB같은 디버깅 심볼이 없는 경우 디버깅 난이도가 조금 올라간다는 단점이 있다.그런데 컴파일러는 프로그램의 구문들을 인식할 수 있지만 인간처럼 프로그램의 흐름을 이해할 수는 없다.
3.2.1. 컴파일러의 한계 예시
다음 코드를 보자.#!syntax cpp
void func1(int *xp, int *yp)
{
*xp += *yp;
*xp += *yp;
}
void func2(int *xp, int *yp)
{
*xp += 2 * (*yp);
}
func1
와 func2
는 동일한 기능을 하는 함수처럼 보이지만 함정이 숨어 있다. 만일 xp == yp
[9]라면 두 함수의 결과가 달라진다. xp == yp
이고 *xp == 1
이었다면 func1
수행 후에는 4가, func2
후에는 3이 *xp
의 값이 된다. func1
을 불필요하게 대입 연산자와 덧셈 연산자를 한번 더 사용한다고 판단해 func2
의 형태로 수정한다면 결과가 달라지는 부분이 존재하며 이는 숨은 버그의 원인이 된다. 또한 펌웨어나 임베디드 혹은 디바이스 드라이버 등을 작성을 할 때에는 시간에 따라 레지스터나 메모리의 값이 변화해야 하는 경우가 있다. 주로 Memory-Mapped I/O를 수행할 때이다. 즉, func1
을 func2
와 같은 형태로 변화시킬 경우 개발자가 의도한 것과는 완전히 다른 동작을 하게 된다. 이렇게 컴파일러에 의한 최적화를 사전에 방지해야만 하는 경우에는 해당 변수에 'volatile'를 붙여 선언해야 한다. 또한 위의 예제처럼 인자 두 개가 모두 포인터이고 쓰기 연산을 할 때에는
데이터 위험이 존재할 가능성이 있기 때문에 보통 컴파일러는 이러한 함수에 대해 최적화를 하지 않는다.3.3. 최적화 예시
다음과 같은 코드가 있다고 하자#!syntax cpp
extern bool isZero(float x, float y, float z)
{
return (!x && !y && !z) ? true : false;
}
이 경우 다음과 같이 번역될 수 있다.
mov r1, 0 // Result
mov r2, x // x
mov r3, y // y
mov r4, z // z
cmp r2, 0 // Compare x
jne .false // Jump if r2 != 0
cmp r3, 0 // compare y
jne .false // jump if r3 != 0
cmp r4, 0 // compare z
jne .false // jump if r4 != 0
jmp .true // jump true
.true
mov r1, 1 // := true
.false
ret // return
프로세서에서 가장 오래 걸리는 처리는 메모리에서 값을 읽어오는 명령이다.
위의 예시에서는 각 값 x, y, z를 레지스터로 이동하는데 가장 오랜 시간이 걸리게 되는데 이는 파이프라인 스톨을 유발시킨다.
jne
는 비교 명령인 cmp
보다 먼저 실행될 수 없으며, cmp
명령은 메모리에서 값을 읽어오는 mov
이전에 실행될 수 없다.cmp r2, 0
은 순차적 실행 환경에서는 모든 mov 동작이 끝나기 전에는 실행되지 못한다. 하지만
비순차적 실행이 가능한 환경에서는 r2, r3, r4는 서로 아무런 종속성이 없는 환경이므로 메모리 읽기가 끝났다면 cmp r2, 0
의 실행이 가능해지고 이 결과가 참이라면 모든 mov
명령이 실행되기 이전에 서브루틴을 빠져 나오는 것이 가능해진다.이 때문에 일부 컴파일러들의 경우 순차적 실행 프로세서들을 위해 명시적으로 루틴을 최적화 하는 경우가 있다.
mov r1, 0 // Result
mov r2, x // x
cmp r2, 0 // Compare x
jne .false // Jump if r2 != 0
mov r3, y // y
cmp r3, 0 // compare y
jne .false // jump if r3 != 0
mov r4, z // z
cmp r4, 0 // compare z
jne .false // jump if r4 != 0
jmp .true // jump false
.true
mov r1, 1 // := true
.false
ret // return
이전 코드는 각 값을 모두 읽은 후 비교를 수행했다면, 최적화가 이루어진 코드는 값을 메모리에서 읽자 마자 비교를 수행하며 파이프라인 스톨을 최소한으로 유지시킨다.
4. 단점
"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%."
97%의 사소한 효율성은 모두 잊자. 섣부른 최적화가 만악의 근원이다. 하지만 가장 귀중한 그 3%의 기회는 놓치지 말아야 한다.
- 도널드 커누스, <The Art of Computer Programming> 中[10][11]
97%의 사소한 효율성은 모두 잊자. 섣부른 최적화가 만악의 근원이다. 하지만 가장 귀중한 그 3%의 기회는 놓치지 말아야 한다.
- 도널드 커누스, <The Art of Computer Programming> 中[10][11]
More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason - including blind stupidity.
맹목적인 어리석음을 포함해 그 어떤 핑계보다 효율성이라는 이름 아래 행해진 컴퓨팅 죄악이 더 많다.
- 윌리엄 A 울프
맹목적인 어리석음을 포함해 그 어떤 핑계보다 효율성이라는 이름 아래 행해진 컴퓨팅 죄악이 더 많다.
- 윌리엄 A 울프
The First Rule of Program Optimization: Don't do it.
The Second Rule of Program Optimization (for experts only!): Don't do it yet.
최적화를 할 때는 다음 두 규칙을 따르라. 첫 번째. 하지 마라. 두 번째, (전문가 한정) 아직 하지 마라.
- 미카엘 A 잭슨
The Second Rule of Program Optimization (for experts only!): Don't do it yet.
최적화를 할 때는 다음 두 규칙을 따르라. 첫 번째. 하지 마라. 두 번째, (전문가 한정) 아직 하지 마라.
- 미카엘 A 잭슨
일정 이상의 모든 마이크로튜닝은 가독성을 훼손하고, 추상화 계층을 우회하여 추상화 누수(abstraction leak)를 발생시킨다. 그렇지 않은 경우에도 보편적인 최적화가 아닌 튜닝이 들어가므로 원래 인력을 대체하기 힘들어진다. 따라서, 마이크로 튜닝은 코드 베이스를 유지 보수하고 및 변화시키는데 비용과 시간이 더 들어가게 바꾸는 부작용이 있다. 또한 운영적인 관점에서 볼 때 모든 최적화는 잠재적인 버그를 동반한다. 따라서 안정화 과정이 필요한데 이 비용이 성능 개선으로 인한 이득보다 크다고 생각되면 최적화를 하지 않는 경우도 있다.
분명히 완벽히 똑같은 동작을 하는 서버라면 C++이 몇 배 ~ 십 몇 배의 트래픽을 더 버틸 수 있었을 것이다. 이렇게 성능이 향상되면 유저 경험도 좋아진다. 하지만 경영적 관점에서 개발 비용과 기간 [12] 아끼는 데는 Python이 낫다. 스타트업은 자금이 쪼달리고 시간이 없기 때문에 후자를 선호한다.
더구나 개발 기간이 매우 줄어드는 것은 강력한 비즈니스적 메리트가 있는데, 첫 번째 릴리즈를 빨리 내서 빠르게 현 시장의 반응을 확인할 수 있기 때문이다. 모든 것을 최적화해서 한번에 냈다가는 엄청난 비용을 잡아먹고 몇 번이고 연기되다가 출시되었다 시류 변화로 인해 조용히 사라졌을 수도 있다. 특히나 애자일 주도 개발을 한다면 릴리즈 주기는 훨씬 줄어들고 돌아오는 피드백에 대응할 시간도 폭포수 모델에 비해 짧아진다. 이는 역으로 말해 개발 기간 단축이 애자일 프로세스 적용과 생산성 향상에 굉장한 메리트가 될 수 있음을 시사한다.
이외에도 법적인 규제 혹은 어른의 사정으로 인해 반드시 사용해야 하는 라이브러리, DB가 느린 경우가 있다. 이 경우 사용하는 라이브러리가 오픈소스가 아닌 이상 최적화 자체가 다른 회사 손에 달려있다.
5. 관련 문서
[1]
Trade-off, 양면성
[2]
비주얼 스튜디오는 이것을 Unity(JUMBO)빌드라는 이름으로 IDE 차원에서 지원한다.
[3]
소프트웨어와 하드웨어를 동시에 디자인하기 때문이다.
물론 소프트웨어와 하드웨어를 같이 디자인한다고 무조건 최적화가 잘 되는 건 아니다 그래서 최적화에는 엄청난 강점을 보이는 기업이고 눈으로 보면 하드웨어는 아주 고스펙은 아닌데 거의 최대한의 퍼포먼스를 뽑아낼 때가 많다. 단, 애플에서 내놓은 윈도우 프로그램은
발적화도 모자라서
악성코드급인 경우도 있다. 자세한 것은
iTunes와
QuickTime Player 항목 참조.
[4]
'1980년대부터'라고 했기 때문에 나사의 최적화로 가장 유명한 예시인 보이저 호에는 적용되지 않는다. 보이저 호가 발사된 것(1977년)은 인텔에서 8086을 발표(1978년)하기도 전의 일이라는 점을 고려하면 그 크기에 그 정도 스펙이면 당시로서는 상당히 훌륭한 수준이었다. 그래도 부족한 컴퓨팅 파워로 성능과 신뢰도를 얻어내기 위해 높은 수준의 최적화를 한 것은 맞다.
[5]
충분히 빠르다면 튜닝하는 게 오히려 해가 된다고 하는 조언이 있다.
[6]
가장 심각한 건 이식성의 파괴와 가독성의 포기
[7]
대표적인 C 컴파일러인
GCC 컴파일러의 경우 -O1, -O2 등의 옵션을 통해 최적화된 코드를 작성할 수 있다.
[8]
기능상의 차이점은 전혀 없지만, Loop unrolling과 같이 속도 위주의 최적화를 사용하는 경우 컴파일된 코드 사이즈가 늘어나거나 흐름이나 순서 등에서 차이가 생길 수 있다. 컴파일러의 최적화 단계에 따라 결과 값의 차이가 있다면 Undefined behavior를 사용했을 경우가 높다. 즉 코드의 문제.
[9]
두 포인터가 가리키는 값이 같다는 뜻이 아니라 두 포인터가 가리키는 메모리 주소가 같음을 의미한다.
[10]
최적화 관련 명언 중에서 늘 언급되는 최고의 문장. 보통 '섣부른 최적화가 만악의 근원이다' 부분만 알려져 있지만 전채 맥락은 초보 개발자들이 엉뚱한 장소에서 최적화를 하느라 정작 중요한 부분을 놓친다는 것을 경고하는 의미이다.
[11]
TACP 책에 등장하는 원본 전체는 다음과 같다: "Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%."
[12]
초기 개발 비용, 지속적인 유지 보수 및 디버깅 비용, 인건비