brunch

You can make anything
by writing

C.S.Lewis

by 이승현 Mar 06. 2017

Threading Performance #05

ThreadPoolExecutor

#01 Dedicated thread model


일반적으로 응용 프로그램은 전용 스레드 모델(dedicated thread model)을 이용합니다.


위 그림처럼 첫 번째 스레드는 오디오 스트리밍만을 처리하고, 두 번째 스레드는 데이터베이스 권한만 처리하고, 세 번째 스레드는 네트워크만 처리합니다.

이 구성은 스레드 당 작업량이 많지 않으므로, 순차적으로 처리하는 것이 적합합니다.



#02 parallel work


하지만 만약 스레드당 처리해야 할 작업량이 많아지면 순차적으로 처리하는 것이 적합하지 않습니다.

예를 들어, 디코딩할 40개의 비트맵이 있고, 각 디코딩은 4ms 정도 소요될 때, 모든 작업을 순차적으로 처리하려면 총 160ms [40 * 4ms]가 걸립니다.

따라서 모든 작업을 하나의 전용 스레드에 넣는 것은 좋지 않습니다. 


반면에 10개의 스레드를 생성하고 각각 4개의 비트맵만 병렬로 디코딩하면 16ms[(40 / 10) * 4] 밖에 걸리지 않습니다.

처리 시간이 10배로 줄어들기 때문에 당장 좋아 보이지만, 스레드들 사이에 적절한 방법으로 작업을 전달하고, 우선순위도 지정해야 하고, 이를 관리해야 하는 문제도 발생합니다.




ThreadPoolExecutor


#03 ThreadPoolExecutor


다행히 안드로이드는 ThreadPoolExecuter Helper 클래스를 제공하고 있습니다.


ThreadPoolExecutor는 스레드 그룹 생성을 관리하고 우선순위를 설정하며 해당 스레드 간에 작업이 분산되는 방식을 관리합니다.




How many threads should you create?


#04 MultiThread with CPU


코드상 원하는 만큼의 스레드를 생성할 수 있습니다.

하지만 CPU는 제한된 개수의 스레드 만 병렬로 실행할 수 있습니다.

만약 스레드가 제한된 개수보다 더 많아지게 된다면, 내부적으로 우선순위에 따라 스케줄링이 일어나고, 스레딩 성능 이슈가 발생할 수 있습니다.

즉, 스레드의 수가 계속 증가하더라도 작업 처리 속도는 그에 비례하여 빨라지지는 않습니다.




#05 MultiThread with Memory


스레드 수를 결정할 때 메모리 문제도 고려해야 합니다.

각 스레드는 최소한 64K의 메모리를 필요로 하며, 특히 기기에 앱이 많이 설치 되어 있고 호출 스택(call stack)이 많아질 수 있는 상황에서는 메모리가 빠르게 증가합니다.



#06 Systrace


Systrace를 사용하면 Android 장치의 많은 실행 중인 시스템에 응용 프로그램 실행을 적용하는 방법을 분석할 수 있고, 이를 바탕으로 문제없이 실행될 수 있는 최소 스레드 수를 발견할 수 있습니다.


https://developer.android.com/studio/profile/systrace-commandline.html

https://developer.android.com/studio/profile/systrace.html




Instantiate a ThreadPoolExecutor object


#07 Constructor of ThreadPoolExecutor


스레드 풀을 만들 때, 초기 스레드 수와 최대 스레드 수를 지정할 수 있습니다.

이 외에도 대기 시간, 작업 큐 등을 설정할 수 있습니다.


구현과 관련된 자세한 내용은 아래 링크들을 참조해 주시기 바랍니다.


http://128.199.231.48/threadpoolexecutor/

https://blog.mindorks.com/threadpoolexecutor-in-android-8e9d22330ee3#.mnsoheyvq




#08 Instantiate a ThreadPoolExecutor object


스레드 수 를 지정하기 위해 아래와 같이 사용 가능한 코어 개수를 얻어 오는데, 이때 반환되는 값이 장치의 물리적 코어 수를 반영하지 않을 수도 있습니다.

int NUMBER_OF_CORES = Runtime.getRuntime(). availableProcessors();


일부 기기에는 시스템 부하에 따라 하나 이상의 코어를 비활성화하는 CPU가 있기 때문에,  실제로 두 개의 CPU가 있지만 그중 하나가 잠들어 있으면 하나만을 반환할 수 있습니다.




스레드 풀을 지원해주니 좋네요.

하지만 초기 스레드 개수와 최대 개수 등 상황에 맞게 구현해야 하기 때문에 마냥 쉽지는 않네요.


https://developer.android.com/training/multiple-threads/create-threadpool.html

https://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html

https://www.youtube.com/watch?v=uCmHoEY1iTM&t=118s&index=6&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari