[본 글은 Naver 맥부기 Cafe의 리겔님의 글을 정리한 것입니다.]
요점정리 이전에 예제 함수의 정확한 성능차이부터 공개할까 한다.
안쪽 (x루프) 에서만 정확히 4배의 속도가 나온다.
상기 사이클은 goto가 소요하는 3사이클을 제외한 것이다. 그러므로 실질적으로는 19: 7로, 2.7배 정도의 속오디긴 하지만, 좀 더 복잡한 함수를 구현하는 경우 기본 실행사이클 자체도 큰 폭으로 늘어나게 되므로 3사이클을 비중은 무의미해 진다 할 수 있다.
이렇듯이 기본만 준수해도 3~4배의 정도의 속도를 달성할 수 있다.
그럼 준수를 요하는 기본을 정리하도록 하겠다.
1. 최대한 지역변수를 사용한다.
전역변수나 인자로 받은 (구조체)포인터 내 데이터는 전부 메모리 억세스를 뜻한다.
그리고 메모리 억세스는 느릴뿐만 아니라 전력소모 또한 가중시킨다.
꼭 필요한 전역변수나 구조체 멤버를 2회 이상 사용하는 경우 반드시 지역변수에 복사 한 후 그 지역변수만을 사용하는 습관을 길러야 한다.
2. 최대한 32비트 (지역)변수를 사용한다.
CISC 코어와 달리 RISC 코어는 모든 연산을 레지스터 전체에서 수행한다. (SIMD 명령어 제외)
그리고 ARM의 레지스터는 32비트 이다.
즉, 16비트나 8비트 변수로 연산을 수행하는 경우 반드시 표현가능 영역을 유지하고자 후처리가 들어가게 된다. 따라서 이러한 표현가능 영역을 보장받길 원하는 경우가 아니라면 모든 변수 및 인자를 32비트로 설정하는 것이 크게 유리하다.
3. 곱셉과 나눗셈을 가급적 사용하지 않는다.
ARM은 나눗셈을 지원하지 않는다. 고급언어에서 나눗셈을 사용하는 경우 컴파일시 내부적으로 나눗셈을 해 주는 함수를 호출하게 되는데, 이게 20사이클 이상을 소요한다.
나눗셈을 피하는 방법으로는 고정소수점 기법이 있는데, 이는 너무 방대한 주제이므로 깊이 들어가지 않겠다. 곱셈은 나눗셈만큼 치명적이지는 않지만 덧셈, 뺄셈 등 ALU명령어보다 확실히 느리다.
꼭 필요한 경우가 아니면 사용하지 않는 것이 좋다.
4. 루프는 가급적 0까지 카운트 다운하는 do... while 을 사용한다.,
순차적으로 상승하는 카운터 값을 루프 내부에서 사용해야만 하는 경우가 아니면 무조건 카운트 다운 do ... while 루프만이 진리이다.
5. 가급적 sequential access를 사용한다
매트릭스 등에서 좌표를 지정해서 몇몇개의 데이터만 취하는 경우가 아니라면 sequential access가 모든 면에서 유리하다. random access를 사용하는 경우 추가적인 연산이 필요한 경우가 대부분이며, 인덱스 값으로 들어갈 레지스터가 하나 더 필요한데, 이 하나의 레지스터 추가 소요가 성능면에서 치명적인 결과를 초래하는 경우가 종종 있기때문이다. (stack swap)
6. 루프 내부의 연산을 최소화한다.
너무나도 당연한 얘기지만 루프 내에서는 필수불가결한 기능만을 수행하는지 반드시 점검해야 한다.
예를 들자면 최적화된 예제 함수에서 gap을 미리 계산 한 후 루프에 진입하는 식이다.
7. 루프 내부에서 사용되는 변수를 12개 이하로 유지한다.
ARM에는 총 16개의 32비트 다목적 레지스터가 있다. (r0-r15)
이 중 r13은 sp(stack pointer), r14는 lr(link register), r15는 pc( program counter) 인데, sp와 pc는 사용자 레벨에서 절대 사용이 불가능 하므로 14개의 레지스터 많이 사용 가능하다.
그리고 lr은 제약적으로만 사용 가능하고, 일종의 temop 레지스터가 필요한 경우가 많으므로 사용자가 무리 없이 사용 가능한 레지스터의 수는 12개이다.
최적화된 예제함수에서 루프 내부에서 사용되는 변수들을 열거하자면 :
x, y
srcheight, srcwidth
pDstBuf, pSrcBuf
gap
총 7개이다.
위에 열거한 7가지가 바로 최적화에 있어서 기본중의 기본이다.
이 기본만 준수해도 체감적인 성능향상을 느낄 수 있을 것이다.
요점정리 이전에 예제 함수의 정확한 성능차이부터 공개할까 한다.
before | after | |
바이너리 사이즈 | 136 | 84 |
x루프 1회 실행 사이클 | 16 | 4 |
안쪽 (x루프) 에서만 정확히 4배의 속도가 나온다.
상기 사이클은 goto가 소요하는 3사이클을 제외한 것이다. 그러므로 실질적으로는 19: 7로, 2.7배 정도의 속오디긴 하지만, 좀 더 복잡한 함수를 구현하는 경우 기본 실행사이클 자체도 큰 폭으로 늘어나게 되므로 3사이클을 비중은 무의미해 진다 할 수 있다.
이렇듯이 기본만 준수해도 3~4배의 정도의 속도를 달성할 수 있다.
그럼 준수를 요하는 기본을 정리하도록 하겠다.
1. 최대한 지역변수를 사용한다.
전역변수나 인자로 받은 (구조체)포인터 내 데이터는 전부 메모리 억세스를 뜻한다.
그리고 메모리 억세스는 느릴뿐만 아니라 전력소모 또한 가중시킨다.
꼭 필요한 전역변수나 구조체 멤버를 2회 이상 사용하는 경우 반드시 지역변수에 복사 한 후 그 지역변수만을 사용하는 습관을 길러야 한다.
2. 최대한 32비트 (지역)변수를 사용한다.
CISC 코어와 달리 RISC 코어는 모든 연산을 레지스터 전체에서 수행한다. (SIMD 명령어 제외)
그리고 ARM의 레지스터는 32비트 이다.
즉, 16비트나 8비트 변수로 연산을 수행하는 경우 반드시 표현가능 영역을 유지하고자 후처리가 들어가게 된다. 따라서 이러한 표현가능 영역을 보장받길 원하는 경우가 아니라면 모든 변수 및 인자를 32비트로 설정하는 것이 크게 유리하다.
3. 곱셉과 나눗셈을 가급적 사용하지 않는다.
ARM은 나눗셈을 지원하지 않는다. 고급언어에서 나눗셈을 사용하는 경우 컴파일시 내부적으로 나눗셈을 해 주는 함수를 호출하게 되는데, 이게 20사이클 이상을 소요한다.
나눗셈을 피하는 방법으로는 고정소수점 기법이 있는데, 이는 너무 방대한 주제이므로 깊이 들어가지 않겠다. 곱셈은 나눗셈만큼 치명적이지는 않지만 덧셈, 뺄셈 등 ALU명령어보다 확실히 느리다.
꼭 필요한 경우가 아니면 사용하지 않는 것이 좋다.
4. 루프는 가급적 0까지 카운트 다운하는 do... while 을 사용한다.,
순차적으로 상승하는 카운터 값을 루프 내부에서 사용해야만 하는 경우가 아니면 무조건 카운트 다운 do ... while 루프만이 진리이다.
5. 가급적 sequential access를 사용한다
매트릭스 등에서 좌표를 지정해서 몇몇개의 데이터만 취하는 경우가 아니라면 sequential access가 모든 면에서 유리하다. random access를 사용하는 경우 추가적인 연산이 필요한 경우가 대부분이며, 인덱스 값으로 들어갈 레지스터가 하나 더 필요한데, 이 하나의 레지스터 추가 소요가 성능면에서 치명적인 결과를 초래하는 경우가 종종 있기때문이다. (stack swap)
6. 루프 내부의 연산을 최소화한다.
너무나도 당연한 얘기지만 루프 내에서는 필수불가결한 기능만을 수행하는지 반드시 점검해야 한다.
예를 들자면 최적화된 예제 함수에서 gap을 미리 계산 한 후 루프에 진입하는 식이다.
7. 루프 내부에서 사용되는 변수를 12개 이하로 유지한다.
ARM에는 총 16개의 32비트 다목적 레지스터가 있다. (r0-r15)
이 중 r13은 sp(stack pointer), r14는 lr(link register), r15는 pc( program counter) 인데, sp와 pc는 사용자 레벨에서 절대 사용이 불가능 하므로 14개의 레지스터 많이 사용 가능하다.
그리고 lr은 제약적으로만 사용 가능하고, 일종의 temop 레지스터가 필요한 경우가 많으므로 사용자가 무리 없이 사용 가능한 레지스터의 수는 12개이다.
최적화된 예제함수에서 루프 내부에서 사용되는 변수들을 열거하자면 :
x, y
srcheight, srcwidth
pDstBuf, pSrcBuf
gap
총 7개이다.
위에 열거한 7가지가 바로 최적화에 있어서 기본중의 기본이다.
이 기본만 준수해도 체감적인 성능향상을 느낄 수 있을 것이다.
반응형
'Miscellaneous > Design Pattern' 카테고리의 다른 글
ISO/IEC15504 - SPICE(Software Process Improvement and Capability dEtermination) (0) | 2016.01.29 |
---|---|
SW품질 평가 기준 CMMI, 프로세스 개선 효과 (0) | 2016.01.29 |
퍼포먼스 최적화의 기본 (3편) (0) | 2012.01.04 |
퍼포먼스 최적화의 기본 (2편) (0) | 2012.01.03 |
퍼포먼스 최적화의 기본 (1편) (0) | 2012.01.03 |