MAT(Memory Analysis Tool) 이란?




[SLIDE_01]





[SLIDE_02]





eclipse plug-in으로 MAT 설치하고 실행하기



[SLIDE_03]




[SLIDE_04]




[SLIDE_05]





분석결과 보기




[SLIDE_06]




[SLIDE_07]




[SLIDE_08]




[SLIDE_09]




[SLIDE_10]




[SLIDE_11]




[SLIDE_12]




[SLIDE_13]




[SLIDE_14]




[SLIDE_15]




[SLIDE_16]





[SLIDE_17]




[SLIDE_18]




[SLIDE_19]




[SLIDE_20]





[SLIDE_21]





HCGellary 앱을 이용한 메모리 누수 모니터링 해보기


안드로이SDK설치폴더/sample 폴더에 가면 "Honey Comb Gellary" 앱을 사용하여 메모리 누수 모니터링 작업을 수행해 본다.

sample프로젝트는 메모리릭이 발생되지 않도록 작성된 프로그램이다. 

우리는 여기서 메모리가 문제가 발생하도록 소스코드를 약간 수정한 뒤 테스트 해보도록 하겠다.

리소스로 부터 읽어온 Bitmap 객체를 ImageView에 붙여서 보여주는 작업을 수행하는데 우리는 이 Bitmap을 HashMap에 캐싱해서 한번 본 이미지에 대한 처리 속도를 높이도록 프로그램을 다음과 같이 수정한다.




[SLIDE_22]




[SLIDE_23]




[SLIDE_24]




[SLIDE_25]



LogCat에는 OOM(Out-of-Memory)에 어느 파일에 몇번째 라인에서 발생했는지에 에러 메시지를 확인할 수 있다. 우리는 좀더 자세한 OOM원인과 상태를 확인하기 위해 해당 프로세스에 대한 메모리 덤프를 확보하고 MAT로 분석 해보려고 한다.




[SLIDE_26]




[SLIDE_27]


Histogram View에 의하면 byte[] 클래스의 Shallow Heap과 Retained Heap이 앱이 사용할 수 있는 Heap 90% 이상을 차지하는 것을 확일 할 수 있다.




[SLIDE_28]

해당 클래스 객체를 참조하는 클래스 객체정보를 확인해 보니 SBitmapCache의 누적된 값이 무척이나 큰것을 확일할 수 있다. 이것은 메모리 누수의 원인으로 판단가능한 데이터이다.




[SLIDE_29]




위 예제의 해결책은?



위 예제의 문제점은 Bitmap 이미지를 캐시하기위해 사이즈제한이 없는 HashMap을 사용하여 캐싱하다 OOM이 발생한 경우이다. 이런 경우에는 LruCache 을 이용하여 이미지 캐시를 하도록 프로그램을 수정할 수 있다.


LruCache은 메모리캐시를 구현하는 API 로 지정된 사이즈만큼 캐시한다. 캐시 사이즈보다 더 많은 데이터를 저장해야 하는 경우 LRU 알고리즘에 따라 가장 최근에 사용하지 않은 데이터를 밀어내고 새로운 데이터를 저장하는 방식을 사용한다.



private static HashMap<String,Bitmap> sBitmapCache = new HashMap<String,Bitmap>();

==> private static LruCache  mMemoryCache; 


LruCache의 사이즈를 앱의 heap메모리 한계의 1/8 정도로 설정하려고 한다면 프래그먼트가 액티비티에 붙을 때 호출되느 onAttach() 메소드를 재정의하여 캐쉬의 사이즈를 설정하도록 한다.


  @Override
   public void onAttach(Activity activity) {

	super.onAttach(activity);
	
       // 앱이 사용할 수 있는 Heap Size 
	final int memClass =((ActivityManager)(activity.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE))).getMemoryClass();

      // 힙사이즈의 1/8 을 캐시영역으로 할당하기 위해 캐시크기 계산
       cacheSize=1024*1024*memClass/8;
	    
       mMemoryCache=new LruCache(cacheSize);
	   
  }



  
  // 캐시에 저장되어 있지 않으면 Bitmap을 메모리에 캐싱한다.
  public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    	if (getBitmapFromMemoryCache(key) == null) {
    		mMemoryCache.put(key,bitmap);
    	}
    }
    
    // 해당 키값으로 이미지가 캐시되어 있으면 Bitmap을 찾아서 반환한다.
    public Bitmap getBitmapFromMemoryCache(String key) {
            return (Bitmap)mMemoryCache.get(key);
   	}
  



   void updateContentAndRecycleBitmap(int category, int position) {
        mCategory = category;
        mCurPosition = position;

        if (mCurrentActionMode != null) {
            mCurrentActionMode.finish();
        }

        // 캐시에 키로 사용할 ID를 생성한다.
        String bitmapId=""+category+"."+position;

        // 캐시로 부터 Bitmap 이미지를 찾아온다.
        mBitmap=  getBitmapFromMemoryCache(bitmapId);

        // 캐시된 이미지가 없으면 Resource로 부터 이미지를 읽어서 Bitmap으로 반환하고, 캐시에 저장한다.
        if ( mBitmap == null ) {
               mBitmap = Directory.getCategory(category).getEntry(position)
                                  .getBitmap(getResources());
              addBitmapToMemoryCache(bitmapId, mBitmap);        
        }
        // 읽어온 Bitmap 이미지를 화면에 보여준다.
        ((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);
    }





블로그 이미지

오픈이지 제로킴

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

 

















참고자료: 안드로이드 앱 성능최적화 (프리렉출판사)

블로그 이미지

오픈이지 제로킴

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

 



























참고자료: 안드로이드 앱 성능최적화 (프리렉출판사)

블로그 이미지

오픈이지 제로킴

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

 






























참고자료: 안드로이드 앱 성능최적화 (프리렉출판사)

블로그 이미지

오픈이지 제로킴

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

 













참고자료: 안드로이드 앱 성능최적화 (프리렉출판사)

블로그 이미지

오픈이지 제로킴

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































참고자료: 안드로이드 앱 성능최적화 (프리렉출판사)

블로그 이미지

오픈이지 제로킴

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

 






















































참고자료: 안드로이드 앱 성능최적화 (프리렉출판사)

블로그 이미지

오픈이지 제로킴

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







































































































참고자료: 안드로이드 앱 성능최적화 (프리렉출판사)

블로그 이미지

오픈이지 제로킴

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











































참고자료: 안드로이드 앱 성능최적화 (프리렉출판사)

블로그 이미지

오픈이지 제로킴

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

 

renderscript.zip

블로그 이미지

오픈이지 제로킴

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

프리렉 안드로이드 앱 성능최적화 랩파일 다운로드 링크:

http://freelec.co.kr/bbs/read.asp?BBSName=BOOKPDS&page=1&id=202562

블로그 이미지

오픈이지 제로킴

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

Eclipse Memory Analyzer는 Java Heap 메모리 분석 도구이다.

메모리 릭을 발견하거나 분석을 통해 메모리 소비를 줄일 수 있도록 지원한다.

 

설치



STEP1.  MAT eclipse plug-in 설치

             Help > Install New Software ...

              work with [ http://download.eclipse.org/releases/juno ]

             검색창에 memory 입력

             [General Purpose Tools][Memory Analyzer]

             [Memory Analyzer(Charts) [optional] 선택

 

STEP2.  perspective 열기

              Windows > Open Perspective > Memory Analyzer

 

<참고> MAT는 자바공용 툴이다. 그래서 안드로이드의 경우 hprof 파일을 추출한후에 Conversion을 거쳐 MAT에서 그 파일을 로드하여 분석한다.

 

STEP3. 안드로이드에서 hprof 추출하기

             DDMS에서 [Dump HPROF file] 아이콘을 클릭하여 hprof파일을 추출한다.

 

 

 

사용가능한 메뉴



 

Action

- Histogram : 클래스당 인스턴스 수를 리스트로 출력

- Dominator Tree:  Biggest Object List와 그것을 keep alive하게 하는것에 대한 정보 출력

- Duplicate Classes: 다중 클래스 로더에 의해 중복적으로 로드된 클래스 정보 출력

 

Report

- Leak Suspects: 메모리 누수를 발생할 가능성이 있는 객체에 대한 정보와 시스템 개요 정보 출력

- Top Components: 힙사용량이 전체 힙사이즈의 1%이상인 컴퍼넌트 리스트 출력

 

Step by Step

- COmponent Report: common root package또는 class loader에 포함되는 오브젝트 분석 정보 출력

 

[참고용어]

결과물을 이해하기 위해 기본적으로 정리해야 하는 용어

Shallow Heap: 1개의 오브젝트가 소비하고 있는 메모리량. 한개의 참조에 대해 4바이트 사용

Retained Heap: GC에의해 제거되는 오브젝트가 사용한 Shallow Heap의 합계

 

블로그 이미지

오픈이지 제로킴

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

원본: http://android-developers.blogspot.kr/2011/03/memory-analysis-for-android.html

Memory Analysis for Android Applications

[This post is by Patrick Dubroy, an Android engineer who writes about programming, usability, and interaction on his personal blog. — Tim Bray]

The Dalvik runtime may be garbage-collected, but that doesn't mean you can ignore memory management. You should be especially mindful of memory usage on mobile devices, where memory is more constrained. In this article, we're going to take a look at some of the memory profiling tools in the Android SDK that can help you trim your application's memory usage.

Some memory usage problems are obvious. For example, if your app leaks memory every time the user touches the screen, it will probably trigger an OutOfMemoryError eventually and crash your app. Other problems are more subtle, and may just degrade the performance of both your app (as garbage collections are more frequent and take longer) and the entire system.

Tools of the trade

The Android SDK provides two main ways of profiling the memory usage of an app: the Allocation Tracker tab in DDMS, and heap dumps. The Allocation Tracker is useful when you want to get a sense of what kinds of allocation are happening over a given time period, but it doesn't give you any information about the overall state of your application's heap. For more information about the Allocation Tracker, see the article on Tracking Memory Allocations. The rest of this article will focus on heap dumps, which are a more powerful memory analysis tool.

A heap dump is a snapshot of an application's heap, which is stored in a binary format called HPROF. Dalvik uses a format that is similar, but not identical, to the HPROF tool in Java. There are a few ways to generate a heap dump of a running Android app. One way is to use the Dump HPROF file button in DDMS. If you need to be more precise about when the dump is created, you can also create a heap dump programmatically by using the android.os.Debug.dumpHprofData() function.

To analyze a heap dump, you can use a standard tool like jhat or the Eclipse Memory Analyzer (MAT). However, first you'll need to convert the .hprof file from the Dalvik format to the J2SE HPROF format. You can do this using the hprof-conv tool provided in the Android SDK. For example:

hprof-conv dump.hprof converted-dump.hprof

Example: Debugging a memory leak

In the Dalvik runtime, the programmer doesn't explicitly allocate and free memory, so you can't really leak memory like you can in languages like C and C++. A "memory leak" in your code is when you keep a reference to an object that is no longer needed. Sometimes a single reference can prevent a large set of objects from being garbage collected.

Let's walk through an example using the Honeycomb Gallery sample app from the Android SDK. It's a simple photo gallery application that demonstrates how to use some of the new Honeycomb APIs. (To build and download the sample code, see the instructions.) We're going to deliberately add a memory leak to this app in order to demonstrate how it could be debugged.

Imagine that we want to modify this app to pull images from the network. In order to make it more responsive, we might decide to implement a cache which holds recently-viewed images. We can do that by making a few small changes to ContentFragment.java. At the top of the class, let's add a new static variable:

private static HashMap<String,Bitmap> sBitmapCache = new HashMap<String,Bitmap>();

This is where we'll cache the Bitmaps that we load. Now we can change the updateContentAndRecycleBitmap() method to check the cache before loading, and to add Bitmaps to the cache after they're loaded.

void updateContentAndRecycleBitmap(int category, int position) {
   
if (mCurrentActionMode != null) {
        mCurrentActionMode
.finish();
   
}
 
   
// Get the bitmap that needs to be drawn and update the ImageView.
 
   
// Check if the Bitmap is already in the cache
   
String bitmapId = "" + category + "." + position;
    mBitmap
= sBitmapCache.get(bitmapId);
 
   
if (mBitmap == null) {
       
// It's not in the cache, so load the Bitmap and add it to the cache.
       
// DANGER! We add items to this cache without ever removing any.
        mBitmap
= Directory.getCategory(category).getEntry(position)
               
.getBitmap(getResources());
        sBitmapCache
.put(bitmapId, mBitmap);
   
}
   
((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);
}

I've deliberately introduced a memory leak here: we add Bitmaps to the cache without ever removing them. In a real app, we'd probably want to limit the size of the cache in some way.

Examining heap usage in DDMS

The Dalvik Debug Monitor Server (DDMS) is one of the primary Android debugging tools. DDMS is part of the ADT Eclipse plug-in, and a standalone version can also be found in the tools/ directory of the Android SDK. For more information on DDMS, see Using DDMS.

Let's use DDMS to examine the heap usage of this app. You can start up DDMS in one of two ways:

  • from Eclipse: click Window > Open Perspective > Other... > DDMS
  • or from the command line: run ddms (or ./ddms on Mac/Linux) in the tools/ directory

Select the process com.example.android.hcgallery in the left pane, and then click the Show heap updates button in the toolbar. Then, switch to the VM Heap tab in DDMS. It shows some basic stats about our heap memory usage, updated after every GC. To see the first update, click the Cause GC button.

We can see that our live set (the Allocated column) is a little over 8MB. Now flip through the photos, and watch that number go up. Since there are only 13 photos in this app, the amount of memory we leak is bounded. In some ways, this is the worst kind of leak to have, because we never get an OutOfMemoryError indicating that we are leaking.

Creating a heap dump

Let's use a heap dump to track down the problem. Click the Dump HPROF file button in the DDMS toolbar, choose where you want to save the file, and then run hprof-conv on it. In this example, I'll be using the standalone version of MAT (version 1.0.1), available from the MAT download site.

If you're running ADT (which includes a plug-in version of DDMS) and have MAT installed in Eclipse as well, clicking the “dump HPROF” button will automatically do the conversion (using hprof-conv) and open the converted hprof file into Eclipse (which will be opened by MAT).

Analyzing heap dumps using MAT

Start up MAT and load the converted HPROF file we just created. MAT is a powerful tool, and it's beyond the scope of this article to explain all it's features, so I'm just going to show you one way you can use it to detect a leak: the Histogram view. The Histogram view shows a list of classes sortable by the number of instances, the shallow heap (total amount of memory used by all instances), or the retained heap (total amount of memory kept alive by all instances, including other objects that they have references to).

If we sort by shallow heap, we can see that instances of byte[] are at the top. As of Android 3.0 (Honeycomb), the pixel data for Bitmap objects is stored in byte arrays (previously it was not stored in the Dalvik heap), and based on the size of these objects, it's a safe bet that they are the backing memory for our leaked bitmaps.

Right-click on the byte[] class and select List Objects > with incoming references. This produces a list of all byte arrays in the heap, which we can sort based on Shallow Heap usage.

Pick one of the big objects, and drill down on it. This will show you the path from the root set to the object -- the chain of references that keeps this object alive. Lo and behold, there's our bitmap cache!

MAT can't tell us for sure that this is a leak, because it doesn't know whether these objects are needed or not -- only the programmer can do that. In this case, the cache is using a large amount of memory relative to the rest of the application, so we might consider limiting the size of the cache.

Comparing heap dumps with MAT

When debugging memory leaks, sometimes it's useful to compare the heap state at two different points in time. To do this, you'll need to create two separate HPROF files (don't forget to convert them using hprof-conv).

Here's how you can compare two heap dumps in MAT (it's a little complicated):

  1. Open the first HPROF file (using File > Open Heap Dump).
  2. Open the Histogram view.
  3. In the Navigation History view (use Window > Navigation History if it's not visible), right click on histogram and select Add to Compare Basket.
  4. Open the second HPROF file and repeat steps 2 and 3.
  5. Switch to the Compare Basket view, and click Compare the Results (the red "!" icon in the top right corner of the view).

Conclusion

In this article, I've shown how the Allocation Tracker and heap dumps can give you get a better sense of your application's memory usage. I also showed how The Eclipse Memory Analyzer (MAT) can help you track down memory leaks in your app. MAT is a powerful tool, and I've only scratched the surface of what you can do with it. If you'd like to learn more, I recommend reading some of these articles:

블로그 이미지

오픈이지 제로킴

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

==> 젤리빈에서는 .... RenderScript를 버렸다네요.

그래서 SurfaceView의 대안으로 TextureView를 사용하도록 권고하네요. ^^


렌더스크립트는



아키텍처 종속성에 대한 문제(안드로이드는 ARM, MIPS, X86 과 같은 다양한 플랫폼에서 동작한다.) 및 성능과 사용성 모두를 유연하게 해결하기 위한 구글의 솔루션이다.


렌더스크립트는 기기 아키텍처에 종속적이지 않으며, 네이티브 코드로 작성한것과 같이 성능은 좋다.


또한 최근 개발자의 관심사인 병렬CPU사용도 용이하다.


하지만 렌더스크립트는 C/C++로 작성된 코드와 달리 안드로이드 환경에서만 이용할 수 있다는 단점도 있다.


다시 정리해 보면,


   렌더스크립트는 고성능 3D렌더링 컴퓨팅 연산에 맞춘 새 API 이다.
   렌더스크립트의 목적은 저수준에서 얻은 고성능의 API앱성능을 최적화 하기위해 기계수준까지 작업
     하는것이
익숙하지 않은 안드로이드 개발자에게 주는것이다.
   렌더스크립트는 하드웨어 가속되는 간단한 3D 렌더링 API, CUDA와 유사한 개발자 친화적인 연산 API,
     C99
와 유사한 언어이다.
   간단한 스크립트는 작업을 GPU에서 연산하고, 더 복잡한 스크립트는 CPU에서 수행한다.


렌더스크립트의 동작방식


렌더스크립트를 사용할때 스크립트 자체는 호스트머신에서 LLVM(Low-Level  Virtual Machine)의 비트코드로 컴파일되며 LLVM 비트코드는 안드로이드 디바이스에서 네이티브 코드로 컴파일 된다.


디바이스에 있는 네이티브 코드는 캐시되고, 그다음에 실행될 스크립트는 스크립트 자체가 이미 네이티브로 컴파일 되어 있기 때문에 실행속도가 빠르다.


비트코드를 네이티브 코드로 컴파일 하는 작업은 디바이스에서 일어나기 때문에 네이티브 코드는 디바이스에 최적화되거나 GPU와 같은 하드웨어 모듈에 최적화 된다.


그러므로 개발자는 아키텍처와 각각의 기능을 어떻게 고려해야 할지 고민할 필요가 없다. 이런면에서 보면 NDK와 가장 큰 차이점이라고 볼 수 있다.


즉 어떤 디바이스를 사용하느냐에 따라 개발하는 애플리케이션에서 어떤 코드를 사용할지 결정하게 되므로 SIMD 명령어를 사용하려고 한다면 NEON을 사용할 수 있는지 미리 체크해야 한다. 


렌더스크립트는 네이티브 레벨로 동작하지만 작동은 안드로이드 VM이 관할한다. 


다음은 렌더스크립트의 시스템 구조이다.




<참고> 

SIMD?

 Single Instruction Multiple Data. 병렬 프로세스의 한 종류로, 하나의 명령어로 여러개의 값을 동시에 계산하는 방식이다. 벡터 프로세스에서 많이 사용되는 방식으로 비디오 게임콘솔이나 그래픽카드와 같은 멀티미디어 분야에 자주 사용된다.

 

NEON이란?

  ARM 프로세서에서 사용하는 Advanced SIMD 확장 도구를 NEON이라 부르며 미디어와 신호처리를 위한 명령어집합을 추가지원한다.래서 오디오, 비디오, 3D, 이미지, 음성 인식등의 처리를 프로세서 수준에서 빠ㅡ게 수행해준다. 

 

VFP?

  Vector Floating Point는 ARM에서 IEEE부동소수점 연산 표준을 만족하는 단정도(single-precision)과 배정도(double-precision) 부동 소수점 연산을 제공하는 보조 연산장치이다.  Vector라는 단어에도 불구하고 진정한 SIMD 를 제공한다고 보기는 힘들지만 ARM에서 실수 연산의 성능 향상에 기여하고 있다.



자 그럼 프로젝트에서 기본적인 렌더스크립트를 어떻게 생성하는지 같이 해 보자.

에뮬레이터는 렌더스크립트에서 요구하는 모든 기능을 제공하고 있지 않기 때문에 렌더스크립트를 테스트하려면 실제 안드로이드 디바이스가 필요하다.


언제나 시작은 "안녕 세상아 !!"  ㅎㅎ

렌더스크립트를 사용해서 출력해보도록 하자.


STEP1. 안드로이드 프로젝트를 생성한다. (허니콤도 상관없지만 ICS로 타켓환경을 정하자.)

STEP2. src폴더에 helloworld.rs 파일을 하나 생성한다.

   #pragma version(1);
   #pragma rs java_package_name(kr.co.openeg.render)
   void hello_world() {
         reDebug("hello world!!",0);
   }


1번라인은 렌더스크립트의 버전

2번라인은 이스크립트가 사용할 자바의 패키지명 선언

4번라인은 로그캣엔 출력할 문자열


안드로이드 프로젝트를 빌드하면 다음의 세개 파일이 생성된다.

- gen/ScriptC_helloworld.java

- gen/helloworld.d

- res/raw/helloworld.bc



먼저 gen폴더에 생성된 ScriptC_helloworld.java  파일을 보면,  #pragma 로 정의한 패키지이름이 정의되어 있고, 디폴트 생성자가 만들어져 있으며, invoke_hello_world함수가 생성된것을 볼 수 있다.

/*
 * Copyright (C) 2011-2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * This file is auto-generated. DO NOT MODIFY!
 * The source Renderscript file: C:\Users\young0\workspace_android\renderscript\src\kr\co\openeg\render\helloworld.rs
 */
package kr.co.openeg.render;

import android.renderscript.*;
import android.content.res.Resources;

/**
 * @hide
 */
public class ScriptC_helloworld extends ScriptC {
    private static final String __rs_resource_name = "helloworld";
    // Constructor
    public  ScriptC_helloworld(RenderScript rs) {
        this(rs,
             rs.getApplicationContext().getResources(),
             rs.getApplicationContext().getResources().getIdentifier(
                 __rs_resource_name, "raw",
                 rs.getApplicationContext().getPackageName()));
    }

    public  ScriptC_helloworld(RenderScript rs, Resources resources, int id) {
        super(rs, resources, id);
    }

    private final static int mExportFuncIdx_hello_world = 0;
    public void invoke_hello_world() {
        invoke(mExportFuncIdx_hello_world);
    }

}



STEP3. 생성한 스크립트를 사용하는 액티비티를 생성해 보자.

자바코드에서 ScriptC_helloworld 인스턴스를 생성하여 invoke_hello_world메서드를 통해 스크립트의 hello_world함수를 호출한다.



package kr.co.openeg.render;

import android.os.Bundle;
import android.renderscript.RenderScript;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		RenderScript rs=RenderScript.create(this);
		ScriptC_helloworld hws=new ScriptC_helloworld(rs, getResources(), R.raw.helloworld);
		hws.invoke_hello_world();
	}
}


STEP4. 실행결과를 로그캣에서 확인한다.

이 앱을 실행하면  로그캣에 다음과 같은 메시지가 출력되는것을 확인할 수 있다.

02-20 14:50:34.444: D/RenderScript(592): hello world!! 0  0x0




이번에는 실제 렌더스크립트를 이용하여 렌더링 작업을 수행해 보자



STEP1. 렌더링을 하는 스크립트를 생성한다.

STEP2. RenderScriptGL 컨텍스트 객체 생성

STEP3. RSSurfaceView를 확장한 클래스 생성

STEP4. 액티비티에서 컨텐트 뷰 역할을 하기위한 RSSurfaceView 설정



STEP1. 렌더링 스크립트의 생성

여기서는 백그라운드 컬러를 변경하는 스크립트를 작성해보자.

regClearColor()함수를 사용하기위해 rs_graphics.rsh를 include한다..

init()함수는 스크립트가 생성될때 자동으로 호출되며, 다른 루틴이 실행되기전에 초기화 작업이 필요하다면 이 함수에 정의한다.

root()함수는 실제 렌더링을 하는 함수이다. 이 예제에서는 랜덤컬러로 백그라운드 색상을 변경하는 간단한 코드이다. return되는 값은 얼마나 자주 렌더링이 발생할지를 설정하는 값이다. 이예제에서는 20밀리초마다 프레임이 업데이트 된다.


#pragma version(1)
#pragma rs java_package_name(kr.co.openeg.render)
#include "rs_graphics.rsh"

void init() {
   // 초기화에 필요한 작업을 수행한다.
}

int root() {
    float red=rsRand(1.0f);
    float green=rsRand(1.0f);
    float blue=rsRand(1.0f);
    rsgClearColor(red,green,blue,1.0f);
    return 20;
}


STEP2. RenderScriptGL 컨텍스트 객체 생성

렌더링작업을 수행하는 기능을 가진 HelloRenderingRS 클래스를 생성한다.  예제 코드에서는 실제 ScriptC_changeBackgroundColor 스크립트를 생성하여 RenderScriptGL 객체와 연결하는 작업을 수행한다. bindRootScript()함ㅁ수를 호출하면 스크립트의 root()함수가 호출되어 렌더링 작업을 수행하게 된다.


package kr.co.openeg.render;

import android.content.res.Resources;
import android.renderscript.RenderScriptGL;

public class HelloRenderingRS {
	
	private Resources mResource;
	private RenderScriptGL mRS;
	private ScriptC_changeBackgroundColor mScript;
	
     public HelloRenderingRS() {}
     
     public void init(RenderScriptGL rs, Resources res){
    	 mRS=rs;
    	 mResource = res;
    	 mScript = new ScriptC_changeBackgroundColor(mRS, mResource, R.raw.changebackgroundcolor);
    	 mRS.bindRootScript(mScript);
     }
     
}

STEP3. RSSurfaceView를 확장한 클래스 생성

RenderScriptGL 컨텍스트 객체를 생성하기 위한 RSSurfaceView를 상속받은 HelloRenderView클래스를 생성한다.


package kr.co.openeg.render;

import android.content.Context;
import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScriptGL;
import android.view.SurfaceHolder;

public class HelloRenderView extends RSSurfaceView {
      public HelloRenderView(Context context) {
    	  super(context);
      }
      
      private RenderScriptGL mRS;
      private HelloRenderingRS mRender;
      public void surfaceChanged(SurfaceHolder holder, int format, int w, int h){
    	  super.surfaceChanged(holder, format, w, h);
    	  if(mRS == null) {
    		  RenderScriptGL.SurfaceConfig sc=new RenderScriptGL.SurfaceConfig();
    		  mRS=createRenderScriptGL(sc);
    		  mRender=new HelloRenderingRS();
    		  mRender.init(mRS, getResources());    		  
    	  }
    	  mRS.setSurface(holder, w, h);
      }
	@Override
	protected void onDetachedFromWindow() {
		// TODO Auto-generated method stub
		if(mRS != null) {
			mRS=null;
			destroyRenderScriptGL();
		}
	}
      
      
}


STEP4. 액티비티에서 컨텐트 뷰 역할을 하기위한 RSSurfaceView 설정

마지막으로 렌더링 작업을 수행하는 액티비티를 작성한다. 이 예제에서는 화면이 포커스를 얻게 되는 시점에 렌더링 작업을 시작하고 포커스를 잃게 되는 시점에 렌더링 작업을 잠시 멈춘다.


package kr.co.openeg.render;

import android.os.Bundle;
import android.renderscript.RenderScript;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

	HelloRenderView helloView;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
			
		RenderScript rs=RenderScript.create(this);
		ScriptC_helloworld hws=new ScriptC_helloworld(rs, getResources(), R.raw.helloworld);
		hws.invoke_hello_world();
		
		helloView = new HelloRenderView(this);
		setContentView(helloView);
	}
	
	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		
	}

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}
}


STEP5. 실행결과 확인

실제 실행한 결과를 보면 20밀리초마다 화면의 백그라운드 색상이 빨강,초록,파랑색중 랜덤하게 업데이트 된다.



안드로이드 4.0에서는 두가지 중요한 기능이 추가되었다.

- 애플리케이션은 오프스크린 버퍼로 메모리를 사용하고 싶을 때 Allocation.USAGE_GRAPHICS_RENDER_TARGET 플래그를 설정하여 오프스크린 버퍼로 메모리를 사용할 수 있다.

- 레이아웃에서 RSTextureView를 사용할 수 있다. RSSurfaceView를 사용하면 분리된 윈도우를 생성하지만 RSTextureView를 사용하면 그렇지 않다.





렌더스크립트에 대한 또다른 예제는 다음 링크에서 더 테스트해 볼수 있을것 같다.

http://blog.naver.com/PostView.nhn?blogId=719121812&logNo=20171662334



네이티브 렌더스크립트 API


렌더스크립트는 API사용이 제한되어 있어서 NDK나 C라이브러리의 API를 호출할 수 없다. 렌더스크립트의 API는 6개의 헤더파일에 정의 되어있는데 SDK의 platform 폴더에 각 안드로이드 버전별로 rendscript 폴더에 저장되어 있다.


허니콤에서는 6개의 헤더파일이 제공된다.

rs_type.rsh               

rs_core.rsh              

rs_cl.rsh                  

rs_math.rsh

rs_graphics.rsh

rs_time.rsh 


여기에 ICS는 아래  6개의  헤더파일이 더 추가되어 12개의 헤더파일을 제공한다.

rs_allocation.rsh

rs_atomic.rsh

rs_debug.rsh

rs_matrix.rsh

rs_object.rsh

rs_quaternion.rsh


블로그 이미지

오픈이지 제로킴

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

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

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

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

 

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

안드로이드는 기본 자바와 달리 자바 코드를 실행하기 위해 자바가상머신(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는 실행에 소요된 밀리세컨값이다.

 

블로그 이미지

오픈이지 제로킴

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

티스토리 툴바