[include(틀:다른 뜻1, other1=PC최적화와 관련 소프트웨어, rd1=PC최적화)] [목차] == 개요 == 소프트웨어가 적은 자원으로 높은 효율을 낼 경우 [[최적화]]가 잘 되었다고 한다. 최적화된 프로그램들은 최적화되지 않은 프로그램들에 비해 메모리를 적게 사용하거나 실행 시간이 줄어드는 이점이 생긴다. 이 최적화 작업이 제대로 이루어지지 않는다면 많은 자원을 먹는데도 [[영 좋지 않은]] 효율을 보여 [[발적화]]나 개적화 등의 표현으로 불리며 까인다. 좁은 범위로는 기기의 성능을 업그레이드 시키는 [[펌웨어]] 업그레이드부터, 넓게는 윈도우즈 시리즈의 서비스팩 급과 같은 대규모 업데이트까지도 포함된다. [[임베디드 시스템]] 프로그래밍에서는 속도와 용량 사이의 상반관계[* Trade-off, 양면성]를 감안해야 하기 때문에 어느쪽을 우선시 하느냐에 따라 최적화의 방향이 달라질 수도 있다. 그래서 [[윈도우 임베디드 컴팩트]]를 연구하는 이들에게는 뼈저리게 다가오는 단어다. [[사용자 경험]]에 가장 크게 기여하는 개념이기도 하다. 대표적인 예로 조립 컴퓨터가 있다. 아무리 고성능부품들만 써도 최적화가 잘 안 되어 있으면 오히려 성능이 떨어지는 모습을 보인다. 단, 어느 쪽이든 하드웨어를 바꾸는 [[업그레이드]]는 예외. 한정된 성능에서 최적의 모습을 보여주어야 하기 때문에 별 의미가 없다. 몇몇 회사들은 최적화에 한계가 있는 부분을 하드웨어를 업그레이드하여 커버하기도 한다. 이를 하드웨어로 최적화한다고 표현하기도 하지만 그런 개념은 없다. == 분야별 특징 == 최적화를 잘 하기로 유명한 기업에는 [[Apple|애플]][* 소프트웨어와 하드웨어를 동시에 디자인하기 때문이다.--[[마이크로소프트 서피스#s-5|물론 소프트웨어와 하드웨어를 같이 디자인한다고 무조건 최적화가 잘되는건 아니다]]-- 그래서 최적화에는 엄청난 강점을 보이는 기업이고 눈으로 보면 하드웨어는 아주 고스펙은 아닌데 거의 최대한의 퍼포먼스를 뽑아낼 때가 많다. 단, 애플에서 내놓은 윈도우 프로그램은 [[발적화]]도 모자라서 [[악성코드]]급인 경우도 있다. 자세한 것은 [[iTunes]]와 [[QuickTime Player]] 항목 참조.]과 [[심비안]] 버프의 [[노키아]]가 있다. 국내에서는 [[코원]]이 유명하다. [[닌텐도]]의 경우 최적화를 잘 하기는 하나 닌텐도가 최적화를 잘 한다기보다는 닌텐도 게임기로 타이틀을 발매하는 서드파티가 자사의 게임에 발적화를 해서 닌텐도가 최적화되어 보이는 것 뿐. 실질적으로 개발사인 닌텐도만 하드웨어의 특징을 잘 살린다고 볼 수 있다.[* 닌텐도가 서드파티에게 개발시 필요한 최적화 기술을 알려주지 않아 그렇다는 말이 있다. 이건 닌텐도64용 게임을 만들었던 디자이너의 인터뷰를 통해 진짜였음이 드러났다. 다만 스위치가 나온 이후에는 이런 말도 거의 없어졌다.] === [[군사 & 우주용 CPU]] === 이쪽 분야에서는 1980년대부터는 절대로 최신 [[CPU]]를 구입하지 않고 오래된 구식 CPU를 쓴다.[* '1980년대부터'라고 했기 때문에 나사의 최적화로 가장 유명한 예시인 보이저호에는 적용되지 않는다. 보이저호가 발사된 것(1977년)은 인텔에서 8086을 발표(1978년)하기도 전의 일이라는 점을 고려하면 그 크기에 그 정도 스펙이면 당시로서는 상당히 훌륭한 수준이었다. 그래도 부족한 컴퓨팅 파워로 성능과 신뢰도를 얻어내기 위해 높은 수준의 최적화를 한 것은 맞다.] 시중엔 [[AMD RYZEN 시리즈/Threadripper|스레드리퍼]] 같은 64코어 프로세서까지 나왔는데도 [[인텔 펜티엄 시리즈|펜티엄]]급 프로세서를 사용한다. 최신 CPU일수록 성능은 좋은 대신 수명과 내구는 떨어지는 경향이 있다. 신형일수록 민감도가 심해져 극한의 환경을 버틸 수 없다. 회로 선폭이 좁아질수록 집적도와 속도가 향상되지만 물리적인 내구성은 당연히 취약해질 수 밖에 없다. 이런 이유로 선폭이 굵은 구식 회로 설계를 한 예전 CPU를 쓰는 것이다. 물론 상용 CPU를 그대로 쓰는 것이 아니라, 회로 구조를 유지한 채 웨이퍼의 재료로 실리콘 대신 튼튼한 '''[[사파이어]]'''([[http://en.wikipedia.org/wiki/Silicon_on_sapphire|Silicon on Sapphire]])를 사용하는 등 여러가지 방식으로 회로의 내구성을 향상시키는 마개조를 한다. 이런 처리를 거친([[http://en.wikipedia.org/wiki/Radiation_hardening|Radiation-Hardened]]) 제품은 속도를 일부 희생하는 대신 통상 실리콘 소자 쯤은 순식간에 망가뜨리는 우주 배경 방사선을 맞아도 정상 작동하는 엄청난 내구성을 자랑한다. ~~질리도록~~ 오래도록 사용할 수 있는 안정적인 처리장치만을 요구한다. === [[게임 프로그래머]] === [[밸브 코퍼레이션]]이 최적화로 유명한 편이고, [[크라이텍]]도 2011년부터 최적화로 유명해졌다. [[크라이시스 2]]가 전작과는 달리 높은 그래픽에 비해 최적화가 잘 되어있기 때문. [[EVE 온라인]]의 경우 한 때 서버의 발적화로 욕을 많이 먹었다가 발적화를 싹 해결한 프로그래머가 (플레이어 중에 IT 업계 종사자가 많은 덕분에) 슈퍼스타 취급을 받기도 했다. 그리고 [[락스타 게임즈]]의 [[GTA 4]]는 심각한 개적화였지만 후속작인 [[GTA 5]]는 엄청난 신적화로 환호를 받았다. 또한 [[프로스트바이트 엔진]]을 사용한 게임들([[배틀필드 4]], 등)도 최적화가 잘 되어있는 편이다. [[블리자드 엔터테인먼트]] 게임들의 경우 들쭉날쭉한 편. 와우, 디아블로 3와 오버워치는 어지간한 컴퓨터에서는 다 돌아가는 이른바 갓적화로 칭송받는다. 그러나 히어로즈 오브 더 스톰과 스타크래프트 2의 경우 엔진 자체의 문제 때문에 컴퓨터를 민감하게 타는 편이라 최적화가 좋다고 하기 어렵고, 하스스톤의 경우에도 PC와 모바일 모두 그다지 뛰어나지 않은 편. [[EA DICE]] 역시 최적화에 있어 상당한 실력을 보여주고 있으며, 특히 [[스타워즈: 배틀프론트(2015)|스타워즈: 배틀프론트]]는 '''신적화'''라는 반응까지 나왔다. [[배틀필드 1]]은 몇 가지 논란이 있기는 했지만 전체적으로 보면 훌륭한 최적화가 이루어졌다. 소프트웨어 최적화 전문가로서 가장 유명하고 장수하는 인물로는 마이크로소프트에서 [[존 카맥]]의 [[이드 소프트웨어]]로 스카우트되어 퀘이크 엔진을 최적화했던 전적의 마이클 압래쉬(Michael Abrash)가 있다. 현재까지도 최적화 최고수로 인정받고 있으며, 코드 한줄 한줄, [[어셈블리어]] 하나 하나를 극한까지 쥐어짜내는 것으로 유명하다. == 주 최적화 기법 == * [[알고리즘]] 개선 [[점근 표기법|최적화의 기본 전제가 되는 것은 적합한 알고리즘이다.]] 알고리즘이 쓸데없는 낭비 없이 최적의 상태로 설계된 상태에서나 프로그램 코드 튜닝이 의미가 있지, 비효율적인 알고리즘을 쓰면서 온갖 최적화 기법을 동원해 봐야 전혀 의미가 없다. 극단적인 경우엔 1%의 성능을 높이기 위하여 99%의 시간을 쓰게 될 것이다. * [[병목 현상|병목 현상(bottleneck)]] 제거 대부분의 프로그램은 프로그램이 사용하는 모든 부분이 느리기 보다는, 조그마한 부분에서 50% 이상 느려지는 등의 병목이 발견되는 경우가 많다. 이러한 병목 현상을 줄이는 것은 적은 노력으로 엄청난 성능 향상을 가져올 수 있다. === 마이크로 튜닝 === 최적의 알고리즘을 사용하고 주요 병목을 제거했는데도 시원치 않을 경우 군데군데를 손봐서 성능을 튜닝한다. 이런 최적화는 코드의 가독성이나 유지보수성을 등가교환해야 하는 경우가 많다. 이 상반관계를 감수하는 것은 프로그래머의 몫. 일반적으로 병목을 제거하는 것만으로도 충분히 빨라졌다면 튜닝을 굳이 할 필요는 없다.[* 충분히 빠르다면 튜닝하는게 오히려 해가 된다고 하는 조언이 있다.] 반면 그래도 좀 더 나은 속도가 필요하다면 다음과 같은 튜닝을 시도하기도 한다. * 구문을 인라인 어셈블리로 대체하여 컴파일러가 할 수 없는 수준의 최적화를 시행한다. 그러나 2000년 이후 일반적인 환경에서 이런 작업을 필요로 하는 경우는 거의 없다. 컴파일러 최적화 기술의 발전에 따라 프로그래머에 준할 정도로 똑똑해졌기 때문이다. 더구나 컴파일러가 할 수 없는 수준의 최적화를 할 수 있는 프로그래머는 얼마 되지 않는다. 정말 쥐어짜내더라도 컴파일러가 지원하는 인트린식으로 컴파일러의 최적화를 도와주거나 SIMD를 이용하는 정도가 한계. * 작은 크기의 자료형을 쓰면 캐시에 더 많이 담을 수 있다. 메모리 및 캐시 등의 자원이 매우 한정적일 때 사용하는 방법. 예를 들어서 최대 10000이 들어갈 변수에 int형 대신 short int를 쓰는 식이다. 2000년 이후에는 이런 식으로는 사실상 속도 차이가 안 나지만, 임베디드 환경처럼 제한된 자원만을 사용해서 최대한의 성능을 내야 하는 경우에는 사용될 수 있다. 단, 단점이 있다. CPU는 데이터를 1Word 단위로 처리한다. 이 크기를 벗어나는 데이터를 처리하는 과정에서는 오버헤드가 발생하게 되는데, 이는 1Word 단위보다 작은 char, byte, short 등에도 적용된다. 즉, 메모리 효율성과 접근처리 시간 효율성 중에서 선택을 해야 하는 것이다. 예상되는 값의 크기가 낮다고 해서 무조건 작은 데이터형을 쓰는 것은 아니란 것. * 멀티스레드 프로그래밍을 한다. 코어의 개수에 비례하는 성능향상을 얻을 수도 있다. 하지만, 프로그래밍을 정말 잘하지 않으면 오류가 발생하거나 오히려 성능이 떨어지기 쉽다. 멀티 스레드 구현 시 Lock 문제가 필연적으로 따라오고 그 부분이 성능 병목이 된다. 그래서 싱글 스레드와 멀티 스레드 모두에서 성능을 끌어내는 알고리즘은 거의 불가능하다. 굳이 둘 다 지원하려면 매크로 떡칠해서 두 개의 알고리즘을 각각 만드는 게 보통이다. 따라서 멀티스레드를 지원하려면 C++의 경우 Intel TBB(Threading Building Blocks) 같은 멀티스레드용 외부 라이브러리를 사용해야 한다. TBB는 싱글 스레드 중심으로 개발된 라이브러리의 메모리 할당자를 덮어쓰거나 성능을 개선해주는 매우 빠른 할당자를 지원하며, 멀티스레드 자료구조 구현이 되어 있다. 그게 아니라면 세부 구현을 직접 짜야 한다. 싱글 스레드 중심으로 개발된 라이브러리의 템플릿을 바꾸어 메모리 할당이나 비교 연산 등을 제어할 수 있다. 또 Lock을 걸어주는 래핑 클래스를 따로 만들어주는 방법도 있다. 또한 처리해야 할 데이터의 크기가 작으면 스레드를 만드는 부하가 처리 속도 향상보다 더 커져서 작업 단위를 쪼갤수록 더 느려지는 현상이 발생할 수 있다. === 컴파일러에서의 최적화 === 현대의 [[컴파일러]]들은 충분히 똑똑해져서, 적당한 설정을 해 주면 [[프로그래머]]를 대신하여 어느 정도 최적화를 해 준다.[* 대표적인 C 컴파일러인 [[GCC]] 컴파일러의 경우 -O1, -O2 등의 옵션을 통해 최적화된 코드를 작성할 수 있다.] 다만 최적화된 실행파일은 프로그래머가 만든 소스코드와 달라져[* 기능상의 차이점은 전혀 없지만, Loop unrolling과 같이 속도 위주의 최적화를 사용하는 경우 컴파일된 코드 사이즈가 늘어나거나 흐름이나 순서 등에서 차이가 생길 수 있다. 컴파일러의 최적화 단계에 따라 결과값의 차이가 있다면 Undefined behavior를 사용했을 경우가 높다. 즉 코드의 문제.] 이 때문에 DWARF나 PDB같은 디버깅 심볼이 없는 경우 디버깅 난이도가 조금 올라간다는 단점이 있다. 그런데 컴파일러는 프로그램의 구문들을 인식할 수 있지만 인간처럼 프로그램의 흐름을 이해할 수는 없다. --이게 되면 프로그래머들 다 굶어 죽는다-- 컴파일러의 최적화는 최적화 테크닉을 적용 가능한 몇몇 구문들을 인식하면서 실행되는데, 요령있게 짜이지 않은 코드의 경우 컴파일러가 구문을 잘 인식할 수가 없어 자동으로 최적화가 되지 않는다. 이런 식으로 컴파일러의 자동 최적화를 막는 코드들을 '최적화 장애물(optimization blocker)'이라 부른다. 최적화 장애물을 치우는 것은 프로그래머들의 몫이다. 프로그래머들은 최적화 장애물을 치워 컴파일러가 최적화를 잘 할 수 있도록 도와야 한다. ==== 컴파일러의 한계 예시 ==== 다음 코드를 보자. {{{#!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''라면 두 함수의 결과가 달라진다. ''xp''==''yp''이고 *''xp''==1이었다면 ''func1'' 수행 후에는 4가, ''func2'' 후에는 3이 *''xp''의 값이 된다. ''func1''을 불필요하게 대입연산자와 덧셈 연산자를 한번 더 사용한다고 판단해 ''func2''의 형태로 수정한다면 결과가 달라지는 부분이 존재하며 이는 숨은 버그의 원인이 된다. 또한 펌웨어나 임베디드 혹은 디바이스 드라이버 등을 작성을 할 때에는 시간에 따라 레지스터나 메모리의 값이 변화해야 하는 경우가 있다. 주로 Memory-Mapped I/O를 수행할 때이다. 즉, ''func1''을 ''func2''와 같은 형태로 변화시킬 경우 개발자가 의도한 것과는 완전히 다른 동작을 하게 된다. 이렇게 컴파일러에 의한 최적화를 사전에 방지해야만 하는 경우에는 해당 변수에 'volatile'를 붙여 선언해야 한다. 또한 위의 예제처럼 인자 두 개가 모두 포인터이고 쓰기 연산을 할 때에는 [[데이터 의존성|데이터 위험]]이 존재할 가능성이 있기 때문에 보통 컴파일러는 이러한 함수에 대해 최적화를 하지 않는다. == 어려운 이유 == * 사용하는 의존성이 느린 경우: 법적인 규제 혹은 [[어른의 사정]]으로 인해 반드시 사용해야하는 라이브러리, DB가 느린 경우가 있다. 이 경우 사용하는 라이브러리가 오픈소스가 아닌 이상 최적화 자체가 다른 회사 손에 달려있다. * 개발사는 지속적으로 업데이트를 통해 성능을 개선하고 있지만 초기 버전의 악명이 이어져서 여전히 느리다고 생각하는 경우도 있다. 윈도우 비스타가 대표적인 예시다. === 프로그램 유지보수성과 최적화의 상반관계 === 일정 이상의 모든 마이크로튜닝은 가독성을 훼손하고, 추상화 계층을 우회하여 추상화 누수(abstraction leak)를 발생시킨다. 그렇지 않은 경우에도 보편적인 최적화가 아닌 튜닝이 들어가므로 원래 인력을 대체하기 힘들어진다. 따라서, 마이크로 튜닝은 코드베이스를 유지보수하고 및 변화시키는데 비용과 시간이 더 들어가게 바꾸는 부작용이 있다. 어느 상황에서나 비즈니스 요구사항이 항상 바뀔 수 있고 인력도 바뀔 수 있다. 모든 비즈니스에서 실행 속도만큼 중요한 것은 개발 속도 및 변화 속도를 늦추지 않는 것이다. 반대로 어느 정도 사용례가 고정되어 있는 안정된 라이브러리같은 경우는 마이크로 튜닝이 들어가기도 한다. === 프로그램 안정성과 최적화의 상반관계 === 운영적인 관점에서 볼 때 모든 최적화는 잠재적인 버그를 동반한다. 따라서 안정화 과정이 필요한데 이 비용이 성능 개선으로 인한 이득보다 크다고 생각되면 최적화를 하지 않는 경우도 있다. 특히 보수적인 회사인 경우. ==== [[기회비용]]의 문제 ==== >서비스 서버를 [[Python]]이 아닌 [[C++]]로 짰다면 서버가 과부하로 터질 일이 없었을거야 분명히 완벽히 똑같은 동작을 하는 서버라면 C++이 몇배~십몇배의 트래픽을 더 버틸 수 있었을 것이다. 이렇게 성능이 향상되면 유저 경험도 좋아진다. 하지만 경영적 관점에서 개발비용과 기간[* 초기 개발비용, 지속적인 유지보수 및 디버깅 비용, 인건비] 아끼는 데는 [[Python]]이 낫다. 스타트업은 자금이 쪼달리고 시간이 없기 때문에 후자를 선호한다. 더구나 개발 기간이 매우 줄어드는 것은 강력한 비즈니스적 메리트가 있는데, 첫번째 릴리즈를 빨리 내서 빠르게 현 시장의 반응을 확인할 수 있기 때문이다. 모든 것을 최적화해서 한번에 냈다가는 엄청난 비용을 잡아먹고 몇번이고 연기되다가 출시되었다 시류 변화로 인해 조용히 사라졌을 수도 있다. [각주] [[분류:프로그래밍]] [include(틀:문서 가져옴,title=최적화,version=139)] [include(틀:문서 가져옴,title=표준 템플릿 라이브러리,version=33)]