Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- cpu scheduling
- 그리디
- KAKAO
- BubbleSort
- 피보나치
- kubernetes
- 백준
- mobaXTerm
- LeetCode
- Codility
- docker
- k8s
- easy
- Backjoon
- GCP
- GKE
- Python
- golang
- 알고리즘
- Dynamic Programming
- Singleton Pattern
- Observer Pattern
- Kotlin
- Programmers
- go
- github
- 파이썬
- Top-down
- java
Archives
- Today
- Total
To Be Developer
[Code Complete] 코드 최적화 기법 본문
코드 최적화 기법
- 속도를 향상, 코드를 작게
논리구조
답을 알고 있을 때에는 테스트 하지마라
if 5 < x and x < 10: ~~~ # 중복된 논리식이면 굳이 다시 검증할 필요가 없다
빈도에 따른 테스트 정렬
// 가장 빈번히 나오는 경우를 고의적으로 먼저 필터링 시켜 검증 수를 줄인다 switch(num){ case "+" || "=" : /* ~~~ */ break; case "0" To "9": /* ~~~ */ break; case ",",".",";": /* ~~~ */ break; }
if
thenelse 와 switch~case 문의 실행속도를 비교해서 더빠른 것을 사용하도록 한다
복잡한 표현식을 테이블 참조로 대체
if (a and not c) or (a and b and c): category = 1 elif (b and not a) or (a and c and not b): category = 2
categoryTable[2][2][2] = [ # !a [ [0,3], # !b!c [2,2] # !bc ], # a [ [1,2], # b!c [1,1] # bc ] ] # 복잡한 논리 구조를 대체하기 위해서 테이블 참조를 사용 category = categoryTable[a][b][c]
루프
- 루프는 여러 번 수행되기 때문에, 프로그램에서 분쟁 지역(hot spot)은 종종 루프 내에 있다. 루프 자체를 보다 빠르게 만드는 법
- 언스위칭(Unswitching)
// 스위칭으로 작성한 자바 코드
for (i=0; i < count; i++){
if (sumType == SUMTYPE_NET){ // 이 코드는 결과가 항상 같음에도 불구하고 루프가 반복될 때마다 반복되서 테스트함
netSum += amount[i];
}else{
grossSum+=aumout[i];
}
}
// 언스위칭으로 변환한 자바 코드
if (sumType == SUMTYPE_NET){ // 언스위칭으로 해서 프로그램 실행속도를 높일 수 있다
for(i=0; i<count; i++){
netSum += amount[i];
}
}else{
for(i=0; i<count; i++){
grossSum+=amount[i];
}
}
/*
언스위칭의 단점으로는 루프가 병렬로 함께 유지 보수 되어야 한다는 것이다.
변경할 점이 발생된다면 모두 변경을 해야된다는 점에서 힘들 수가 있다
이 경우 로직이 같다면 funtion으로 해결하면 될 듯 하다.
*/
- 재밍(jamming)(연합) - 동일한 요소의 집합을 다루는 두 개의 루프를 결합한 결과, 두 개의 루프를 하나로 줄임으로써 루프의 오버헤드를 줄이는 이득을 볼 수 있다.
# 재밍 가능한 분리되어 있는 루프
for i in range(0, employeeCount-1):
employeeName[i] = ""
for i in range(0, employeeCount-1):
employeeEarnings[i] = 0
# 재밍된 루프
for i in range(0, employeeCount-1):
employeeName[i] = ""
employeeEarning[i] = 0
- 언롤링(unrolling) - 언롤링의 목표는 루프의 보조 수단 코드(HouseKeeping)의 양을 줄이는 것
// 언롤링 될 수 있는 루프를 자바로 작성한 예제
i = 0;
while (i < count){
a[i] = i;
i = i + 1;
}
// 한 번 언롤링된 루프를 자바로 작성한 예제
i= 0;
while( i < count - 1){
a[i] = i;
a[i+1] = i+1;
i = i+2;
}
if ( i == count){
a[count-1] = count-1; // 루프가 2씩 증가할 때 놓칠 수 있는 경우를 처리
}
// 두 번 언롤링된 루프를 자바로 작성한 예제
i= 0;
while( i < count - 2){
a[i] = i;
a[i+1] = i+1;
a[i+2] = i+2;
i = i+3;
}
if ( i == count - 1){
a[count-1] = count-1;
}
if ( i == count - 2){
a[count-2] = count-2;
}
// 파이썬 같은 경우는 성능이 떨어지지만
// 다른 언어인 경우 16~43% 정도 시간을 절약할 수 있음
- 루프 내부 작업의 최소화
// 루프 내부에 복잡한 포인터 표현식이 있는 C++ 예제
for (i = 0; i<rateCount; i++){
netRate[i] = baseRate[i] * rates->discounts->factors->net;
}
// 복잡한 포인터 표현식을 단순화한 C++ 예제
quantityDiscount = rates->discounts->factors->net;
for(i=0; i<rateCount; i++){
netRate[i] = baseRate[i] * quantityDiscount;
}
데이터변환
- 가능한 가장 적은 차수의 배열을 사용하라
// 2차원 배열을 초기화하는 자바 예제
for (row=0; row < numRows; rot++){
for(column=0; column<numColumns; column++){
matrix[row][column] = 0;
}
}
// 1차원 배열로 표현한 자바 예제
for ( entry=0; entry < numRows * numColumns; entry++){
matrix[ entry ] = 0;
}
- 배열에 대한 참조를 최소화 하라
// 루프 내부에서 불필요하게 배열을 참조하고 있는 Java 예제
for ( discountType = 0; discountType < typeCount; discountType++ ){
for (discountLevel = 0; discountLevel < levelCount; discountLevel++){
rate[discountLevel] = rate[discountLevel] * discount[discountType];
}
}
// 배열에 대한 참조를 루프의 외부로 이동시킨 Java 예제
for ( discountType = 0; discountType < typeCount; discountType++ ){
thisDiscount = discount[discountType];
for (discountLevel = 0; discountLevel < levelCount; discountLevel++){
rate[discountLevel] = rate[discountLevel] * thisDiscount;
}
}
- 캐싱(caching)을 사용하라 - 캐싱은 자주 사용되지 않는 값보다 자주 사용되는 값을 보다 쉽게 가져올 수 있는 방법으로 값을 저장하는 것
// 캐싱을 하면 도움을 얻을 수 있는 루틴을 자바로 작성한 예제
double Hypotenuse(double sideA, double sideB){
return Math.sqrt( (sideA * sideA) + ( sideB * sideB) );
}
// 시간이 많이 소요되는 계산을 피하기 위해서 캐시를 사용하고 있는 자바 예제
private double cachedHypotenuse = 0;
private double cachedSideA = 0;
private double cachedSideB = 0;
public double Hypotenuse( double sideA, double sideB ){
// 삼각형이 이미 캐시에 있는지 확인한다.
if ( (sideA == cachedSideA) && (sideB == cachedSideB)){
return cachedHypotenuse;
}
// 새로운 빗변의 길이를 구하고 캐시한다
cachedHypotenuse = Math.sqrt( (sideA * sideA) + (sideB * sideB));
cachedSideA = sideA;
cachedSideB = sideB;
return cachedHypotenuse;
}