안드로이드 어플리케이션을 개발할때 코드 최적화는 가장 먼저 고려해야할 대상이 아니다.

즉 이말은 어플리케이션 개발에 있어서 가장 우선적인것은 완전한 기능을 구현하는것인가 이다.

즉 코드 최적화는 때에 따라 가장 나중에 신경써야 할 부분인지도 모른다. 하지만 코드 최적화 습관을 들여 놓는다면 나중에 다시 코드를 수정하는 수고와 시간을 들이지 않고 충분한 성능을 이끌어 낼 수 있을것이다.

 

우선 안드로이드 코드 실행 환경을 이해하자.

안드로이드는 기본 자바와 달리 자바 코드를 실행하기 위해 자바가상머신(JVM)을 사용하지 않고 달빅(Dalvik)가상 머신을 이용한다. 즉 달빅에서 자바 어플리케이션이 실행되기 위해서는 자바의 바이트코드가 아닌 dex컴파일러인 dx에 의해 생성된 달빅바이트 코드를 실행한다.

 

다음 예제 코드는 재귀적 방법을 사용한 일반적인 피보나치 수열을 계산하는 메서드이다.

   public long computeRecursively(int n){
    	if ( n>1) return computeRecursively(n-2) + computeRecursively(n-1);
    	return n;
    }
    
  

 

<참고> 피보나치수열

 F0 = 0

 F1 = 1

 Fn = Fn-2 +Fn-1  ( n > 1  인동안)

 

안드로이드 2.2(프로요)이후 버전은 JIT(Just-In-Time)컴파일러를 도입하여 달빅바이트코드를 네이티브코드로 컴파일하여 실행하므로 실행속도가 2~5배까지 빨라졌다. 이렇게 빨라진 이유는

- 네이티브코드는 가상 머신을 사용하지 않고 직접 CPU에 의해 실행된다.

- 네이티브코드는 특정 아키텍쳐에 맞게 최적화될 수 있다

 

피보나치수열함수 최적화

  public long computeRecursivelyWithLoop(int n){
    	if ( n> 1) {
    		long result=1;
    		do {
    			result += computeRecursivelyWithLoop(n-2);
    			n--;
    		}while(n>1);
    		return result;
    	}
    	return n;
    }
    
   

 

위의 예제 코드는 재귀적으로 구현되었기 때문에 메서드 호출을 없애는것이 전체 호출횟수를 상당히 줄여준다.

예를 들어 computeRecursively(30)은 2,692,537번 호출을 생성하는 반면 다음 computeRecursivelyWithLoop(30)은 1,346,269번 호출된다.

 

재귀적호출방법은 메모리가 충분하지 않은 디바이스에서는 너무 많은 스택공간을 요구하게 되므로 스택오버플로를 발생할 가능성도 있다. 따라서 가능하면 재귀적 호출방법 대신에 순환 호출 방법을 사용하면 알고리즘도 명확하고 간결하며 성능 또한 개선되는것을 확인할 수 있다.

 

   public long computeRecursivelyFaster(int n){
    	if ( n> 1) {
    		long a, b=1;
    	    n--;
    	    a=n&1;
    	    n /= 2;
    	    while ( n-- > 0 ){
    	    	a+=b;
    	    	b+=a;
    	    }
    	    return b;
    	}
    	return n;
    }

 

<실행결과>

다음 실행결과는 n=30인 피노나치수열을 계산하는 메서드를 재귀적호출방식, 재귀최적화방식, 순환 호출방식으로 코딩하여 실행한 결과이다. Times는 실행에 소요된 밀리세컨값이다.

 

블로그 이미지

오픈이지

시큐어코딩 교육/컨설팅 전문가 그룹

댓글을 달아 주세요