이 글은 http://developer.android.com/training/articles/perf-tips.html 의 내용을 참고하여 작성되었습니다.

# Avoid Internal Getters/Setters.

C++과 같은 native language에서는 internal getter / setter에 대해 Compiler가 inline으로 자동으로 수정을 해주기 때문에, internal getter / setter의 사용은 매우 좋은 습관이다.

eg) i = getCount(); 와 같은 getter는 Compiler가 inline으로 수정해준다.   i = mCount;

Compiler는 친절하다.

하지만 Android에서는 Virtual method를 호출하는 비용이 비싸기 때문에, public interface에 대해서는 기존 object-oriented programming의 관습을 따르지만, private access에 대해서는 field를 직접 access하는 것을 권장한다.

field에 직접 접근하는 것은 getter를 통해 접근하는 것보다, JIT이 미적용되었을 때 3배, JIT이 적용된 이후에는 7배 가량 빠르다.

100만번 access하도록 테스트 한 결과는 아래와 같다. 약 4배 정도 성능 차이가 있다.

 

Profiling inline vs getter

inline (ns) getter (ns)
1 6,683,350 36,254,881
2 39,184,568 81,085,207
3 6,713,868 70,892,334
4 8,178,711 38,879,393
5 6,530,762 55,084,228
AVG 13,458,252 56,439,209

Note : ProGuard를 적용하면 Native language의 compiler와 같이 자동으로 inline accessor를 생성해주기 때문에, getter / setter 사용과 성능 2마리 토끼를 다 잡을 수 있다.

 

# Use Enhanced For Loop Syntax.

Iterable interface를 구현한 collection이나 Array에 대해서 Enhanced For Loop(“for-each” loop 이라고 알려져 있다.)을 사용할 수 있다. Collection의 경우 iterator는 hasNext()next()를 호출할 수 있다.

ArrayList의 경우 hand-written counted loop가 3배 빠르지만, 다른 Collection들의 경우 enhanced for loop와 완벽하게 동일한 속도를 보여준다.

 

Profiling Loop syntax for Array.

normal loop (ns) cache size (ns) for-each (ns)
1 6,561,280 5,676,270 4,547,119
2 6,835,937 5,340,576 4,577,637
3 2,349,855 2,044,677 2,105,713
4 1,922,607 1,708,984 2,166,749
5 1,831,054 1,983,642 1,770,020
AVG 3,900,147 3,350,830 3,033,448

Profiling Loop syntax for ArrayList.

normal loop (ns) cache size (ns) for-each (ns)
1 9,521,484 7,659,912 12,939,453
2 8,666,991 6,103,515 12,451,171
3 8,361,816 6,042,480 12,329,101
4 7,537,841 7,232,666 11,199,952
5 7,720,946 7,324,218 10,375,976
AVG 8,361,816 6,872,558 11,859,131

 

NOTE

1. loop size를 cache하는 것은 성능 향상에 도움이 된다. 반복문의 조건절에는 List의 size() 함수나 Array의 length field를 access하는 대신 미리 계산한 길이를 사용하자.

2. ArrayList의 경우 Enhanced for loop(for-each loop)이 기존 방식보다 약 30% 정도 느리다.