코드를 읽거나, 코드를 작성하거나 할때, 일단 라인수가 큰 코드, 데이터와 코드가 분리되어 있지 않은 코드들을 보면 나도 모르게 불안해진다. 


마음속 깊은곳에서 "고쳐야돼.. 제거해야해... 줄여야해!!" 하는 소리가 들리는것 같은 기분으로 불안한 상태로 코드를 제대로 작성하거나 읽을수가 없다.


포인터가 사용가능한 C언어류를 사용할때에도 Switch나 if를 많이 쓰게 되면 함수포인터로 테이블을 생성하여 사용하거나 했는데, 자바로 넘어오니 어랏??? 없다. 포인터 같은게...


그래서 interface 와 map을 사용하여 switch - case 들을 제거하여 코딩하고 있다.


제거하면 나중에 좋을까?? 


상황에 따라 다르지만, 일반적으로 코드량을 줄일수 있다. 물론 switch - case로 가장 컴팩트하게 코딩을 할수 있는 경우도 많지만, 보통 case에서 구분되어야 하는 경우의 수를 구분짓기 위해 일단 int 형 구분 값이 고정적으로 들어간다.( case [constant 값] : 이런식으로 코딩을 하니까 )

map에서는 그 구분하는 것을 array의 index를 사용하던, 특정한 키 object를 사용하여 구분짓던 무엇이든지 map을 만들수 있는 방법이면 다 사용가능하다. 그래서 case에 대한 정의를 따로 할 필요가 없다. 키만 구분할수 있게 애초에 입력을 받으면 된다.


그리고, case가 추가되거나 제거될때는 map부분만 수정하고 추가하면 된다, switch를 구분하는 곳에서는 더이상수정할 필요가 없어지게 된다. 

이로 인해 logic을 관리하는 클래스와 case들을 관리하는 class로 구분이 가능하게 된다. Map들만 따로 클래스로 생성해서 나눈다음에 상황에 맞는 Map을 logic으로 올려주도록 하면 switch case가 portable하게 만들어지는 것이다.


학부시절 C언어로 뭔가를 만들어오라는 과제를 할때는 Switch Case를 당연히 사용하곤했었는데, 계속 프로그래밍을 하다보니, if,switch,case등을 계속 추가하게 되는 코드들은 나를 너무 불안하게 만들었다. ㅠ.ㅠ


요즘에는 참여하는 프로젝트에서 누군가가 Switch -case를 쓰면 나도 모르게 ctrl - a , delete를 누르게 된다. @.@


예제 코드는 정말 이런식으로 코딩할리 없겠지만, 어떻게 interface로 case를 구분하는냐에 대한 예제정도로만 봐주면 좋을것 같다.



package com.hopeisagoodthing.removeswitch;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class Main {

	/**
	 * @param args
	 */
	private interface IMethod{
		void method();
	}
	
	private static Map<Integer,IMethod> methodArray= new HashMap<Integer,IMethod>();
	static{
		methodArray.put(1, new IMethod(){
			@Override
			public void method() {
				System.out.println("1");				
			}});
		
		methodArray.put(2, new IMethod(){
			@Override
			public void method() {
				System.out.println("2");				
			}});
		
		methodArray.put(3, new IMethod(){
			@Override
			public void method() {
				System.out.println("3");				
			}});
		
		methodArray.put(4, new IMethod(){
			@Override
			public void method() {
				System.out.println("4");				
			}});
		
		methodArray.put(5, new IMethod(){
			@Override
			public void method() {
				System.out.println("5");				
			}});
		
		methodArray.put(6, new IMethod(){
			@Override
			public void method() {
				System.out.println("6");				
			}});
		
		methodArray.put(7, new IMethod(){
			@Override
			public void method() {
				System.out.println("7");				
			}});
		
		methodArray.put(8, new IMethod(){
			@Override
			public void method() {
				System.out.println("8");				
			}});
		
		methodArray.put(9, new IMethod(){
			@Override
			public void method() {
				System.out.println("9");				
			}});
		
		methodArray.put(10, new IMethod(){
			@Override
			public void method() {
				System.out.println("10");				
			}});
		
		methodArray.put(11, new IMethod(){
			@Override
			public void method() {
				System.out.println("11");				
			}});
		
		methodArray.put(12, new IMethod(){
			@Override
			public void method() {
				System.out.println("12");				
			}});
		
		methodArray.put(13, new IMethod(){
			@Override
			public void method() {
				System.out.println("13");				
			}});
		
		methodArray.put(14, new IMethod(){
			@Override
			public void method() {
				System.out.println("14");				
			}});
		
		methodArray.put(15, new IMethod(){
			@Override
			public void method() {
				System.out.println("15");				
			}});
		
		methodArray.put(16, new IMethod(){
			@Override
			public void method() {
				System.out.println("16");				
			}});
		
		methodArray.put(17, new IMethod(){
			@Override
			public void method() {
				System.out.println("17");				
			}});
		
		methodArray.put(18, new IMethod(){
			@Override
			public void method() {
				System.out.println("18");				
			}});
		
		methodArray.put(19, new IMethod(){
			@Override
			public void method() {
				System.out.println("19");				
			}});
		
		methodArray.put(20, new IMethod(){
			@Override
			public void method() {
				System.out.println("20");				
			}});
		
		methodArray.put(21, new IMethod(){
			@Override
			public void method() {
				System.out.println("21");				
			}});
		
		methodArray.put(22, new IMethod(){
			@Override
			public void method() {
				System.out.println("22");				
			}});
		
		methodArray.put(23, new IMethod(){
			@Override
			public void method() {
				System.out.println("23");				
			}});
		
		methodArray.put(24, new IMethod(){
			@Override
			public void method() {
				System.out.println("24");				
			}});
		
		methodArray.put(25, new IMethod(){
			@Override
			public void method() {
				System.out.println("25");				
			}});
		
		methodArray.put(26, new IMethod(){
			@Override
			public void method() {
				System.out.println("26");				
			}});
		
		methodArray.put(27, new IMethod(){
			@Override
			public void method() {
				System.out.println("27");				
			}});
		
		methodArray.put(28, new IMethod(){
			@Override
			public void method() {
				System.out.println("28");				
			}});
		
		methodArray.put(29, new IMethod(){
			@Override
			public void method() {
				System.out.println("29");				
			}});
		
		methodArray.put(30, new IMethod(){
			@Override
			public void method() {
				System.out.println("30");				
			}});
		
		methodArray.put(31, new IMethod(){
			@Override
			public void method() {
				System.out.println("31");				
			}});
		
		methodArray.put(32, new IMethod(){
			@Override
			public void method() {
				System.out.println("32");				
			}});
		
		methodArray.put(33, new IMethod(){
			@Override
			public void method() {
				System.out.println("33");				
			}});
		
		methodArray.put(34, new IMethod(){
			@Override
			public void method() {
				System.out.println("34");				
			}});
		
		methodArray.put(35, new IMethod(){
			@Override
			public void method() {
				System.out.println("35");				
			}});
		
		methodArray.put(36, new IMethod(){
			@Override
			public void method() {
				System.out.println("36");				
			}});
		
		methodArray.put(37, new IMethod(){
			@Override
			public void method() {
				System.out.println("37");				
			}});
		
		methodArray.put(38, new IMethod(){
			@Override
			public void method() {
				System.out.println("38");				
			}});
		
		methodArray.put(39, new IMethod(){
			@Override
			public void method() {
				System.out.println("39");				
			}});
		
		methodArray.put(40, new IMethod(){
			@Override
			public void method() {
				System.out.println("40");				
			}});
	}
	
	private static void withoutSwitch(int inA)
	{
		inA = inA%40+1;				
		final IMethod method = methodArray.get(inA);
		if(method!=null){
			method.method();
		}
		else
		{
			System.out.println("nothing");
		}		
	}
	
	private static void useSwitch(int inA)
	{
		inA = inA%40+1;
		switch(inA)
		{
		case 1:
			System.out.println("1");
			break;
		case 2:
			System.out.println("2");
			break;
		case 3:
			System.out.println("3");
			break;
		case 4:
			System.out.println("4");
			break;
		case 5:
			System.out.println("5");
			break;
		case 6:
			System.out.println("6");
			break;
		case 7:
			System.out.println("7");
			break;
		case 8:
			System.out.println("8");
			break;
		case 9:
			System.out.println("9");
			break;
		case 10:
			System.out.println("10");
			break;
		case 11:
			System.out.println("11");
			break;
		case 12:
			System.out.println("12");
			break;
		case 13:
			System.out.println("13");
			break;
		case 14:
			System.out.println("14");
			break;
		case 15:
			System.out.println("15");
			break;
		case 16:
			System.out.println("16");
			break;
		case 17:
			System.out.println("17");
			break;
		case 18:
			System.out.println("18");
			break;
		case 19:
			System.out.println("19");
			break;
		case 20:
			System.out.println("20");
			break;
		case 21:
			System.out.println("21");
			break;
		case 22:
			System.out.println("22");
			break;
		case 23:
			System.out.println("23");
			break;
		case 24:
			System.out.println("24");
			break;
		case 25:
			System.out.println("25");
			break;
		case 26:
			System.out.println("26");
			break;
		case 27:
			System.out.println("27");
			break;
		case 28:
			System.out.println("28");
			break;
		case 29:
			System.out.println("29");
			break;
		case 30:
			System.out.println("30");
			break;
		case 31:
			System.out.println("31");
			break;
		case 32:
			System.out.println("32");
			break;
		case 33:
			System.out.println("33");
			break;
		case 34:
			System.out.println("34");
			break;
		case 35:
			System.out.println("35");
			break;
		case 36:
			System.out.println("36");
			break;
		case 37:
			System.out.println("37");
			break;
		case 38:
			System.out.println("38");
			break;
		case 39:
			System.out.println("39");
			break;
		case 40:
			System.out.println("40");
			break;
			
		default:
			System.out.println("nothing");
			break;			
		}
		
	}
	private static final int TOTAL_TRIAL = 20;
	private static final int MAX_LOOP = 20000;
	public static void main(String[] args) {		
		Random urandom = new Random();
		int[] inputs = new int[MAX_LOOP];		
		int[] useSwitchResult = new int[TOTAL_TRIAL];
		int[] withoutSwitchResult = new int[TOTAL_TRIAL];
		
		for(int i=0;i<MAX_LOOP;i++){
			inputs[i] = urandom.nextInt();
		}
		
		
		for(int trial=0;trial<TOTAL_TRIAL;trial++)
		{
			System.gc();
			long start = System.currentTimeMillis();
			for(int a=0;a<500*trial;a++){						
				useSwitch(inputs[a]);
			}
			useSwitchResult[trial] = (int) (System.currentTimeMillis()-start);			
		}
		
				
		for(int trial=0;trial<TOTAL_TRIAL;trial++)
		{		
			System.gc();
			long start = System.currentTimeMillis();		
			for(int a=0;a<500*trial;a++){			
				withoutSwitch(inputs[a]);
			}
			withoutSwitchResult[trial] = (int) (System.currentTimeMillis()-start);
		}
		System.out.println("useSwitch :" );
		for(int trial=0;trial<TOTAL_TRIAL;trial++)
		{
			System.out.print(useSwitchResult[trial]+"\t");
		}
		
		System.out.println("\nwithoutSwitch :" );
		for(int trial=0;trial<TOTAL_TRIAL;trial++)
		{
			System.out.print(withoutSwitchResult[trial]+"\t");
		}				
	}
}



블로그 이미지

커뉴

이 세상에서 꿈 이상으로 확실한 것을, 인간은 가지고 있는 것일까?

,

블로그 정리중 발견한 글 옮김(2008년 8월 26일 글)

두개의 32비트 Register에서 상하위 의 16비트 값씩을 조합해서 새로운 값을 만들어 낼때 유용하게 사용할수 있는 명령어가 바로 PKHBT,TB 명령어 이다.

이 명령어는 ARM v6 ARCH에 추가된 명령어로  v6 아키텍쳐를 지원하는 모든 ARM cpu에서 사용할수 있다.

아래의 예제를 보면, 실제 사용예를 이해할수 있다.

참고: H : 상위 16비트
        L  : 하위 16비트

r9 == C(H)A(L)  : r9 레지스터의 상위 16비트에 C가, 하위 16비트에 A가 저장되어 있다.
r1 == D(H)B(L)  : r1 레지스터의 상위 16비트에 D가, 하위 16비트에 B가 저장되어 있다.
 
PKHTB  r12,r1,r9, ASR #16 ; 
PKHBT  r9,r9,r1, LSL #16 ; r9 = B ,A

결과 >
r12 = D(H)C(L) , 즉, r1의 TOP(상위16비트)와 r9를 16비트 ASR한 BOTTOM(하위 16비트)를 packaging!
r9 == B(H)A(L) , 즉, r9의 BOTTOM(하위 16비트)과 r1를 16비트 LSL한 TOP(상위16비트)을 packaging!

이런 방법을 사용하지 않고 다르게 할수 있는 방법은 Register를 하나 더 사용해서, AND연산과 ORR연산을 사용하는 방법이 있다. 그러나, 위의 두방법처럼 하나의 명령어로 처리되지는 못하므로, LOOP사용시 훨씬더 많은 비용을 소모하게 된다.

이런 방법을 그래픽 렌더링 엔진쪽에서 ASM 코딩으로 최적화 해주면, 지금 나오는 Embedded 단말보다 훨씬 빠른 UI를 경험할수 있을것이다. 

블로그 이미지

커뉴

이 세상에서 꿈 이상으로 확실한 것을, 인간은 가지고 있는 것일까?

,

아주 오래전에 적어두었던 글을 블로그 정리중 발견하여 옮겨적습니다.(2008년 8월 27일 글)

 

그래픽 처리하는 루틴이나 라이브러리의 경우 C언어나 CPP로 아무리 컴팩트하게 프로그래밍을 했다고 해도, 컴파일러가 사람만큼 스마트 하지 못하기 때문에 비효율적이게 되는 부분이 발생하게 된다. 

컴파일러의 첫 목적은 사용자가 의도한 대로 안전하게 어셈블링 해주는 것이기 때문에 이런 비효율은 피할 수 없는 것이다.

만약 사용자가 어셈블리에 관한 지식과 해당 H/W에 대한 지식이 조금이라도 있다면, 어셈블리 레벨에서 최적화를 시도해 볼 수 있는데, 그 실제적인 예를들어 설명하겠다.

<C CODE>

typedef _DWORD DWORD
struct _DWORD
{
    unsigned short  a ;  
    unsigned short  b ;  
    unsigned short  c ;  
    unsigned short  d ;  
};

typedef _DWORD2 DWORD2
struct _DWORD2
{
    unsigned int   A;  
    unsigned int   C ;  
};

void Loop(DWORD2 *dst,DWORD *src,int loopsize)
{
    while(loopsize--)
    {
         int d = 256 - src->d;
         dst->A=(DWORD2*)src->A+((dst->A*d)>>8)&0x1F001F00;
         dst->C=(DWORD2*)src->C+((dst->C*d)>>8)&0x1F001F00;
         dst++;
         src++;
   }       
}

위의 코드를 C언어 레벨에서 분석하였을때, 구조체가 사용이 덜 최적화 되었다는 것을 알수 있다.
즉, (DWORD2*)src->C값은 src->d 값을 포함하고 있기 때문에, C코드상에서도 메모리 Access를 1회 제거하는데 큰 공헌을 할수는 있다. 

CPU상에서 연산되는 것보다, 메모리 IO에 의한 성능저하가 상당하기 때문에 위와 같이 메모리 IO를 1회 줄이는것만으로도 성능향상을 확인할 수 있다.

앞으로, 우리가 튜닝을 할때에는 C언어로 무결한 코드를 완전히 컴팩트하게 구성한다음에 어셈블리 언어로 최적화 하면 된다.

위 C코드의 컴파일 결과 : ASM 코드(armcc 사용,LE, arm1136j-S)

Loop
     SUBS    R3,R2,#1
     BXCC   R14
     PUSH   {R14}
     LDRH    R2,[R1,#6]
     LDR      R4,[R0,#0]
     LDR      R12,[R1,#0]
     RSB      R2,R2,#0X100
     SUBS    R3,R3,#1
     MUL     R4,R2,R4
     ADD      R12,R12,R4,LSR #8
     BIC       R12,R12,#0x1F000000
     BIC       R12,R12,#0x1F00
     STR      R12,[R0,#0]
     LDR      R4,[R0,#4]
     LDR      R12,[R1,#4]
     ADD      R1,R1,#8
     MUL     R2,R4,R2
     ADD      R2,R12,R2,LSR #8
     BIC       R2,R2,#0x1F000000
     BIC       R2,R2,#0x1F00
     STR      R2,[R0,#4]!
     ADD      R0,R0,#4
     BCS      {PC} -0x4C
     POP      {R14}
     BX         R14

위의 ASM 코드를 보면 상당히 많은 부분이 개선가능하다는 것을 발견할 수 있다. 

두 말 할 것 업이 바로 최적화 작업에 돌입한다.!!!

개선된 코드

Loop_opt
    SUBS     R3,R2,#1
    BXCC     R14
    PUSH     {R4-R9}   ; 레지스터를 더 많이 쓰기 위해, 스택에 저장
    MOV      R8,#0x1F00
    ORR       R8,R8,R8, LSL #16
    MOV      R9,#0xFF
    ORR       R9,R9,R9 LSL #8
INLOOP
    LDM       R1!,{R6,R7}  ; 한번에 두개씩 읽어오자!!
    LDM       R0, {R4,R5}  ; 역시 한번에 두개 씩  읽어올것!!
    AND       R2,R9,R7, ASR #16   ;위에서 말했던, 메모리 Access 1회 줄이는 방법.!!!!
    RSB       R2,R2,#0x100 
    MUL      R4,R2,R4 ; STALL제거를 위해 위로 올라옴
    MUL      R5,R2,R5 ; STALL제거를 위해 위로 올라옴
    SUBS     R3,R3,#1
    ADD       R6,R6,R4, LSR #8 ;이놈은 줄이고 싶지만... 더 연구해봐야 됨.
    BIC        R6,R6,R8  ; 이런거 말고 방법이 있을거야~~
    ADD       R7,R7,R5, LSR #8 ;이놈도...
    BIC        R7,R7,R8 ; 이런거 말고 방법이 있을거야~~
    STM      R0!, {R6,R7} ; 두개 저장.
    BCS      INLOOP
    POP      {R4-R9}
    BX         R14

적용 테스트 결과 :루틴 자체의 성능이 약 3배 개선되었다. (시스템 점유율 12 % --> 4%로 개선)
최적화 방법 :
1. 메모리 접근 횟수를 줄임
2. LOOP내 인스트럭션의 수를 줄임
3. STALL 발생 횟수를 없앰.
4. BIT 연산을 한번에 가능하도록 함.

그러나, "나는 더 개선하고 싶다." 하는 사람은 아직 한가지 더 방법이 있다.
바로 Cache의 최대한 활용하는 방법을 연구하는 것과 SIMD(Single Instruction Multiple Data)를 사용하는 것이다.  ARM의 경우 Cache Line 사이즈가 32byte이므로 한 라인에 딱 맞는 Loop Code를 작성하면 캐시 Hit Ratio가 증가할것이고 이에 따라 성능향상도 기대할 수 있다.
그러기 위해서는 Thumb code는 16개, ARM code는 8개의 instruction으로 코드를 작성해야 한다. 

그리고 SIMD를 적용한다면, 아래 코드를 개선할수 있다.

MUL      R4,R2,R4 ; 
MUL      R5,R2,R5 ; 
ADD       R6,R6,R4, LSR #8 ;
BIC        R6,R6,R8  ;
ADD       R7,R7,R5, LSR #8 ;
BIC        R7,R7,R8 ;

구조체 구조가 32bit SIMD에서는 못써먹을 구조라서, 64bit SIMD가 되면 바로 적용해 볼 수 있을것 같다.

한줄요약 : RISC에서는 생각 보다 최적화는 어렵지 않다.

블로그 이미지

커뉴

이 세상에서 꿈 이상으로 확실한 것을, 인간은 가지고 있는 것일까?

,