컨텐츠 바로가기

효율적인 코딩을 위한 규칙

http://YSocks.egloos.com/416453

요즘들어서는 컴퓨터의 사양이 높아지면서 저를 포함한 많은 프로그래머(개발자^^;;) 들이 
퍼포먼스에 대해서는 별로 신경을 쓰지 않는것 같습니다.
연산을 하는데 드는 비용따위는 신경안써도 프로그램은 잘 돌아가니 뭐 귀찮게 그런거 
생각하기도 싫고 그런거 신경쓰다가 오히려 생산성(시간)이 떨어져 납기일 못맞추는것 보다는 
그냥 손가는 데로 코딩하는게 속편하다고 생각하죠.

하지만 방심하다가 낭패를 보는경우가 종종 있기도 합니다.
개발툴이 개발자 위주로 편하게 나오다보니 그 무게를 고사양PC도 감당하기 힘들지요.
이런전차로 코드를 튜닝하고자하는 개발자들을 위해 참고가 될만한 자료를 올립니다.

 

■ 시간단축을 위한 공간규칙

   ① Data Structure Augmentation
       - 빈번한 연산을 하는데 필요한 시간은 종종 부가적 정보로 데이터 구조를 늘리거나 
         또는 데이터 구조 내의 정보를 변경하여 더 쉽게 접근할 수 있도록 함으로써

         감소시킬 수 있다
   ② Store Precomputed Results
      - 비용이 많이 드는 함수값을 다시 계산하는 비용은 함수를 한번만 계산하고 
        그 결과를 저장하는 방법으로 감소시킬 수 있다. 그 이후에 함수가 호출될 때는 
        다시 계산하기보다는 테이블을 검색하여 처리한다.
   ③ Cashing
        - 가장 빈번하게 접근하는 데이터는 그 접근 비용이 가장 적어야 한다.
   ④ Lazy Evaluation
        - 어떤 아이템이 실제로 필요하기 전까지는 평가(evalution)를 하지 않음으로써 
           아이템에 대한 불필요한 평가를 피한다.

■ 공간절약을 위한 시간규칙

   ① Packing
        - 촘촘한 데이터 구조를 사용하면 데이터를 저장하고 검색하는 데 필요한 시간은

            늘어나지만 메모리비용을 줄일 수 있다.
        - 패킹은 종종 메모리를 절약하기 위해 시간을 희생하는 것이지만, 
           때로는 더 작은 데이터 표현으로 인해 처리속도가 더 빨라질 수도 있다.
   ② Interpreters
        - 어떤 프로그램을 표현하기 위한 메모리는 많이 발생하는 일련의 오퍼레이션을

           간결하게 표현하는 인터프리터를 사용하여 감소시킬 수 있는 경우가 종종 있다. 

■ 루프 규칙

   ① Code Motion Out of Loops
        - 어떤 계산을 루프가 반복될 때마다 실행하는 것보다는 루프 밖에서 한번만 실행하는

           것이 낫다.
   ② Combining Tests
        - 효율적인 내부 루프(inner loop)는 가장 적은 테스트를 포함하고 있어야하며, 
           단지 하나의 테스트만을 포함하고 있는 것이 바람직하다. 따라서 프로그래머는 
           루프의 몇 가지 종료 조건을 다른 종료 조건을 이용하여 시뮬레이트해봐야 한다.
   ③ Loop Unrolling
        - 루프를 펼치는 것은 루프 인덱스 값을 변경해야 하는 비용을 제거하고, 
           또한 파이프라인 지연(pipeline stall)을 피하도록 도와주며,분기를 감소시키고, 
           명령어(instruction) 수준의 병렬처리를 증가시킨다.
   ④ Transfer-Drven Loop Unrolling
        - 만약 내부 루프에서 많은 비용이 단순한 대입문에 소모된다면,

          이런 대입문은 코드를 반복하고 변수의 사용을 배경하여 제거할 수 있다.

          특히, i=j와같은 대입문을 제거하면, 이와 관련된 코드에서 j가 i 인것처럼 다루어야한다.
   ⑤ Unconditional Branch Removal
        - 빠른 루프에는 무조건적 분기를 포함하면 안된다.

           루프의 마지막 부분에 있는 무조건적 분기는 루프를 회전시켜 루프의 마지막에 조건적

           분기를 갖도록 하여 제거할 수 있다.
   ⑥ Loop Fusion
        - 근처에 있는 두 루프가 같은 요소의 집합에 대해 동작하고 있다면,

           동작 부분을 묶어 하나의 루프에서 처리하도록 한다.

■ 논리규칙

   ① Exploit Algebraic Identities
        - 만약 논리식을 평가하는 데 비용이 많이 든다면, 평가하는 데 비용이 덜 드는 대수적으

          로 동치인 논리식으로 대체한다.
   ② Short-Circuiting Monotone Functions
        - 만약 여러변수에 대한 어떤 단조 증가 함수가 특정 한계를 초과하는지 검사하려 할 때,

          특정 한계를 한 번 넘은 다음에는 다른 변수에대해서 평가를 할 필요가 없다. 
   ③ Reordering Tests
        - 논리식은비용이 적게 들고 결과가 참인 경우가 많은 식이 비용이 많이 들고 결과가

          참인 경우가 거의 없는 식보다 앞에 와야 한다.
   ④ Precompute Logical Functions
        - 작고 유한한 도메인에 대한 논리 함수는 그 도메인을 나타내는 테이블에 대한 검색으로

          대체할 수 있다.
   ⑤ Boolean Variable Elimination
        - 불리언(Boolean)변수 v에대한 대입식을 v가 참일 때와 거짓일 대를 처리하는

          if-else문으로 바꾸어 프로그램에서 불리언 변수를 제거할 수 있다.

■ 프로시저 규칙

   ① Collasping Function Hierarchies
        - 함수가 다른 함수를 호출하는 구조로 되어 있는 경우 호출되는 함수를 인라인화하고

           넘겨지는 변수를 묶어서 함수를 재작성하면 종종 실행시간을 단축할 수 있다. 
   ② Exploit Common Cases
        - 함수는 모든 경우를 정확히 처리하고 빈번한 경우에 대해서는 효율적으로

           처리해야 한다. 
   ③ Coroutines
        - 다중 패스(multi-pass) 알고리즘은 종종 협동루틴(coroutines)을 사용하여

           단일 패스(single-pass) 알고리즘으로 바꿀 수 있다.
   ④ Transformations on Recursive Functions
        - 명시적인 프로그램 스택을 사용하여 재귀를 반복으로 변환한다.
        - 만약 함수의 마지막 부분에서 자신을 재귀적으로 호출하면, 그부분을 함수의 
           첫 번째 부분으로 분기하도록 바꿀 수 있는데, 이것을 Removing Tail Recursion이라고

          도 한다.
        - 작은 부분문제를 풀 때는 재귀를 반복하여 크기가 0 또는 1 에대한 문제를 풀게 하는 것

          보다는 보조 프로시저를 사용하는 편이 더 효율적일 수 있다.
   ⑤ Paralelism
        - 하드웨어가 병렬처리를 지원하는 경우, 프로그램은 가능한 많이 병렬처리를 사용할 수

          있는 구조로 작성되어야 한다.


■ 수식 규칙
   ① Compile-Time Initialization
        - 가능한 많은 변수가 프로그램이 실행되기 전에 초기화되어 있어야 한다.
   ② Exploit Algebraic Identities
        - 만약 평가하는 비용이 많이 드는 수식이 있다면, 평가하는 데 비용이 적게 들면서

          대수적으로 동치인 수식으로 바꾼다.
        - 배열의 모든 요소에 대해 반복하는 루프에서 곱셈을 계산하는 데 덧셈을 이용하도록

          하면 연산 강도를 줄일 수 있다. 많은 컴파일러가 이 최적화를 수행한다.

          이 기법은 점증적 알고리즘의 여러 경우로 일반화 될 수 있다.
   ③ Common Subexpression Elimination
        - 만약 포함된 변수가 변하지 않은 채로 동일한 수식이 두 번 평가된다면, 첫번째 평가

          결과를 저장한 다음 두 번째로 수식을 평가하는 부분에서 사용하여 같은 수식을 두번

          평가하는 것을 피할 수 있다.
   ④ Pairing Computation
        - 만약 비슷한 두 수식이 빈번하게 함께 평가된다면, 그 둘을 하나의 쌍으로 평가하는

          새로운 프로시저를 만들어야 한다.
   ⑤ Exploit Word Parallelism
        - 비용이 많이 드는 수식을 평가할 때는 컴퓨터 하부 구조가 제공하는 기본적인 데이터

          경로의 폭을 최대로 사용하라.

 

※출처 : 존 벤틀리 의 생각하는 프로그래머 중에서....
역자주)코드 튜닝은 코드의 가독성을 떨어뜨려 코드를 이해하기 어렵게 만드는 것이 보통이다. 따라서 코드 튜닝은 프로그램의 퍼포먼스가 정말로 문제가 될 때까지 미루는 것이 좋을 것이다.

 

 

이상입니다.
참고하세요.


덧글|신고