본문 바로가기
프로그래밍/Code Craft

[2부 코드의 비밀스러운 일생] Chapter11 속도의 필요성

by Ohdumak 2017. 11. 14.

장의 내용

효울적인 코드가 중요한가

효율적인 코드 설계하기

기존 코드의 성능 향상시키기

 

인생에는 속도를 높이는 것보다 중요한 일이 있습니다. - 마햐트마 간디

 

다른 어떤 이유보다 많은 컴퓨팅 죄악이 효율성이라는 이름으로 - 효율성을 성취하지도 못하면서 - 자행됩니다. 맹목적인 어리석음으로 인한 것보다 많습니다.  - W. A. 울프 (유명한 컴퓨터 과학자)

 

최적화는 소프트웨어 개발을 위협하는 망령이다!!

 

최적화는 낡아빠진 주제라서 이미 모든 사람들이 그에 대한 의견을 내놓았고, 똑같은 충고가 재삼재사 거론되어 왔다.

그럼에도 불구하고 아직도 많은 코드들이 현명하게 개발되지 못하고 있다

효율성, 성능, 필요하지 않은 최적화 등을 하고 있다.

 

최적화란 무엇인가?

최적화

순수한 의미는 개선을 하고, 좋은 무언가를 만드는

일반적으로 "코드가 빠르게 실행되도록 만드는 "으로 생각하지만 그림의 일부이다.

 

최적화가 의미하는 것들

프로그램의 실행 속도를 높이는

실행 파일의 크기를 줄이는

코드의 질을 향상시키는

출력의 정확성을 높이는

기동 시간(startup time) 최소화하는

단위 시간당 데이터의 처리량을 늘리는 (반드시 실행 속도와 같지는 않음)

저장 장치의 부담을 줄이는 (예를 들면 데이터베이스의 크기)

 

최적화하지 마라.

(전문가용) 최적화하지 마라. - M. A. 잭슨

 

어떠한 희생을 치르더라도 최적화는 피해야 한다. 처음에는 무시하고, 개발의 마지막에 코드가 충분히 빠르게 돌아가지 않으면 그때 생각만 해보자

=> 성능은 코드가 행도 작성되기 전에, 개발이 시작되는 바로 초라한 시점부터 고려해야 사항이다

 

코드의 성능은 다음과 같은 요소에 의해 결정된다.

실행 플랫폼

전개 또는 설치 구성

아키텍쳐에서의 소프트웨어 관련 결정 사항

저급 모듈 설계

기존에 만들어져 있는 것들 (: 시스템의 기존 부분과의 상호 작용의 필요성)

소스 코드 행의 품질

 

이러한 많은 것들이 있으니 처음 시작할 때부터 프로그램의 성능에 대해 생각하자

올바른 코드가 빠른 코드보다 훨씬 중요하다

 

코드의 최적화를 막는 것은?

복잡성

불필요한 복잡성을 줄이고, 간단하고 빠른 작업들로 분할하자

간접성

모든 문제는 간접성의 수준을 높여서 해결 있다

중복성

중복은 종종 피할 있고, 필연적으로 코드의 성능을 망친다

) configure 파일은 계속 읽는

잘못된 설계

가장 근본적이고, 가장 미묘하고, 가장 풀기 어려운 성능 문제가 있다

I/O

사용자, 디스크, 네트워크 연결을 통해 입출력을 하는 프로그램

 

최적화가 되나?

초창기에 컴퓨터가 느려서 최적화가 결정적인 스킬이었으나, 지금은 시대가 다르다

 

코드의 최적화는 바람직한 품질 하나를 다른 하나와 맞바꾸는 행위이다

코드의 최적화를 피해야 하는 가장 이유

가독성 결여

최적화된 코드가 원래의 코드와 똑같이 명료하게 읽히는 경우는 드물다

복잡성 증가

복잡성은 좋은 코드의

유지보수/확장의 어려움

가독성 결여와 복잡성 증가로 인해 코드의 관리가 힘들어질 것이다.

이해와 충돌

특정 플랫폼에 맞춰서 최적화를 하는 경우 다른 플랫폼에서의 작업은 희생될지도 모른다

많은 일거리

우리는 긴박한 다른 문제로 주의를 집중해야 한다

처음부터 주의해서 효율적인 코드를 작성한다면 최적화의 필요성은 어차피 줄어들 것이다

 

다른 대안

  1. 지금 성능이 받아들일 없는 수준입니까? - ) ext3, ext4 SWADE
  2. 프로그램을 빠른 기계에서 실행 시키자 (CPU)
  3. 하드웨어 해결책을 찾자 (메모리)
  4. 사용자 인터페이스에 대한 작업을 해서 사용자가 모르게 혹은 신경 쓰지 못하게 만들자
  5. 공격적인 옵티마이저가 포함된 신형 컴파일러 사용

 

최적화를 하는가?

가지 분야에서는 반드시 최적화가 필요하다

게임프로그래밍

DSP

자원이 제한된 환경(임베디드 플랫폼)

리얼타임 시스템

금융 분야나 과학 연구에 사용되는 수치 프로그래밍

 

코드를 언제 최적화할 필요가 있는지 알아두고, 처음부터 고품질의 효율적인 코드를 작성하는 것이 좋다

 

세세한 실무

프로그램의 속도를 높이는 6단계

  1. 지나치게 느리다는 것을 확인하고, 정말 최적화가 필요한지 증명
  2. 가장 느린 코드를 식별해서 지점을 타깃을 삼는다 - 프로파일러 사용
  3. 최적화할 타깃의 성능을 테스트 한다
  4. 코드를 최적화한다
  5. 코드가 여전히 작동하는지 테스트한다
  6. 속도가 향상되었는지 테스트하고, 다음에 무슨 일을 할지 결정한다

 

당신의 코드를 다른 부분과 별도로 최적화 하고, 개발 빌드가 아닌 릴리즈 빌드를 최적화 하자

 

"우리는 작은 효율성, 예를 들면 시간을 97% 정도로 줄이는 것에 대해서는 잊어야 합니다: 조급한 최적화는 악의 근원입니다" - 커누스는 C. A. R. 호어의 말을 인용

 

 

최적화 테크닉

최적화란

설계 변경

코드 변경

 

최적화 전략

느린 일의 속도를 높인다

느린 일을 자주 한다

느린 일을 정말 필요할 때까지 미루었다가 한다

설계 변경

느린 데이터 접근을 개선 하거나, 오래 걸리는 계산의 되풀이를 방지하기 위해 캐싱 또는 버퍼링 레이어 추가하기

리소스 풀을 만들어서 자원 할당의 부담 줄이기

속도를 위해 불필요한 정확성 희생시키기

데이터 저장 포맷이나 디스크 상에서의 표현을 고속 작동에 더 알맞게 바꾸기

병렬화 기술과 스레드를 활용해서 한 동작이 다른 동작 뒤에 늘어서는 직렬화 방지하기

스레드 효율적으로 사용하기

과도한 익셉션 사용 피하기

코드 공간을 차지하는 언어 설비 사용 안 하기 (RTTI: runtime type information) 실행 시간 타입 정보 

RTTI: http://blog.naver.com/lovinghc/30013832987

기능 제거하기.

설계의 품질 떨어뜨려서 속도 얻기.

 

복잡성 표시법

알고리즘의 복잡성 알고리즘의 스케일이 얼마나 적절하게 커지는지 측정하는 도구

복잡성은 알고리즘이 반드시 행햐애 하는 작업의 (기초 오퍼레이션의 ) 의해 결정

기초 오퍼레이션이란 계산, 할당, 조건 테스트, 데이터 읽기/쓰기 같은 오퍼레이션을 말한다

 

알고리즘의 복잡성은 독일의 수학 이론가 에드문트 란다우가 발견한 Big O표시법을 이용해서 표현

 

 


 - O(1) : 상수형 빅오, 데이터 수에 상관 없이 연산횟수가 고정됨. 연산 횟수가 5회라도 O(1)로 나타냄.

 - O(log n) : 로그형 빅오, 데이터 수 증가율에 비해 연산 횟수 증가율이 낮은 알고리즘.

 - O(n^(1/2)):

 - O(n) : 선형 빅오, 데이터 수와 연산 횟수가 비례하는 알고리즘.

 - O(n log n) : 선형로그형 빅오, 데이터 수가 두 배로 늘 때 연산 횟수는 두 배보다 조금 더 증가하는 알고리즘. 퀵소트

 - O(N^2) : 데이터 수의 두 배에 해당하는 연산 횟수를 요구하는 알고리즘. 버블 소트

 - O(N^3) : 데이터 수의 세 배에 해당하는 연산 횟수를 요구하는 알고리즘.

 - O(2^n) : 지수형 빅오, 지수적 증가는 엄청난 연산 횟수의 증가를 보임. 사용하기에 비현실적 알고리즘.

 - O(n!) 펙토리얼형 빅오, 최악 anagram algorithm

알고리즘

느린 알고리즘은 코드 구현을 땜질하지 말고, 빠른 다른 알고리즘으로 교체하는 것이 좋다

데이터 구조

선택한 알고리즘과 밀접하게 연관되어 있다

 

코드 변경

일단. 최적화 옵션을 켜두거나, 최적화 수준을 높게 설정 하자

루프 풀기

루프의 본체가 아주 짧을 경우에는 루프의 골격이 반복되는 동작보다 많은 부담이 있다

코드 인라인

언어에서 인라인 지원이 되면 소스 코드 안에서 인라인을 요청 있다

상수 접기

return 6+4; => return 10;

컴파일 시간으로 이동

강도 축소

x = x/4 => x = x >>2 곱셈/나눗셈 보다 쉬프트 연산자가 빠르다

부분식

int first = (a*b)+10;
int second = (a*b)/c;

 

Int temp = a * b ;
int first = temp + 10;
int second = temp / c;

죽은 코드의 제거

정적분석으로 사용하지 않는 코드는 모두 제거해라.

그런데 API 성으로 만들어놓았지만 사용하지 않는 기능들도 모두 제거 해야하나???

 

널리 용납될 수 있는 실행 속도 높히기

시간이 오래 걸리는 함수가 반복적으로 호출된다는 사실을 발견했으면 캐시해 재사용한다.

함수를 다른 언어로 다시 구현해라. 예를들면 JNI 를 이용해 C코드를 작성하라.

실행 속도 높히기

작업이 절대적으로 필요할 때까지 최대한 미루어서 실행해라.

조건 검사 함수를 위쪽으로 끌어올려 불필요한 작업을 피해라.

결과값이 바뀌지 않는 계싼은 루프 밖으로 옮겨라

복잡한 계산은 lookup table을 이용해 시간과 공간을 교환하라.

단락 회로 평가 (short-circuit evaluation)을 활용해 실패 가능성이 있는 테스트를 제일 앞에두어 시간을 절약해라

바퀴를 다시 발명하지 말아라. 이미 성능 튜닝이 되어 있는 표준 루틴을 재사용해라.

 

크기 최적화

실행 전에 압축이 풀리는 압축 실행 파일 만들기

공통 코드 요소를 추출해서 공유함수에 넣음으로써 중복피하기

거의 사용되지 않는 함수는 방해되지 않는 곳으로 치우기

 

효율적인 코드 작성하기

성능을 위한 설계 - 효율적인 코드 작성 - 코드의 최적화 - 시스템 효율이 향상된다

 

좋은 프로그래머

최적화가 절대적으로 필요하다는 사실이 증명되지 않으면 최적화를 하지 않는다

사려 깊고 신중한 접근 방법으로 체계적인 최적화를 시도한다

코드 수준의 최적화에 호소하기 전에, 일찍 대안을 찾고 설계 개선에 대해 연구한다

코드의 품질을 파괴하지 않는 최적화를 선호한다

나쁜 프로그래머

코드의 부적절성을 증명하기도 전에 최적화부터 시작한다

측정이나 조사도 하지 않고 보틀넥이라고 생각하는 코드를 공격한다

자기가 하는 최적화가 다른 코드 영역이나 사용 패턴에 대해 어떤 의미를 갖는지 전체적으로 고려하지 않는다

코드의 품질보다 속도가 중요하다고 생각한다

 

 

 


728x90

댓글