Android 고성능 인코딩 모범 사례
9122 단어 AndroidPerformance
효율적인 코드를 작성하는 두 가지 기본 원칙:
앱을 최적화할 때 가장 까다로운 문제는 앱이 서로 다른 하드웨어 장치에서 실행될 수 있고 서로 다른 가상 기기 버전, 서로 다른 프로세서로 인해 서로 다른 운행 속도를 초래할 수 있다는 것이다.설비의 JIT 유무도 서로 다른 성능을 초래할 수 있다.서로 다른 설비에서 비교적 좋은 성능을 확보하기 위해서 우리는 코드 차원에서 최적화를 해서 코드가 효율적으로 집행될 수 있도록 확보해야 한다.
불필요한 객체 작성 방지
대상 창설은 비용이 있기 때문에 우리는 가능한 한 임시적인 대상을 많이 만드는 것을 피해야 한다. 만약에 앱을 위해 너무 많은 대상을 만들면 빈번한 쓰레기 수거를 의미한다.잦은 쓰레기 수거는 사용자 체험에 좋지 않은 영향을 미칠 수 있다. 안드로이드 2.3 이후 쓰레기 수거는 더 이상'Stop-The-World'가 아니라 동시에 실행할 수 있지만 불필요한 대상 창설을 피해야 한다.다음 예제를 참조로 사용할 수 있습니다.
비교적 급진적인 방법은 다차원 그룹을 여러 개의 평행 1차원 그룹으로 나누는 것이다.
전반적으로 말하면 불필요한 대상의 창설을 최대한 피하고 창설된 대상이 적을수록 쓰레기 회수 빈도가 낮다는 것을 의미하며 이것은 사용자의 체험에 직접적인 영향을 줄 것이다.
Static 메서드 우선 사용
대상의 속성에 접근할 필요가 없다면, 방법을static로 설명할 수 있습니다. 이렇게 하면 이 방법의 호출 속도를 15%-20% 높일 수 있습니다.이것은 좋은 방법으로 서명 호출이 대상의 속성 상태를 바꾸지 않는다는 것을 알릴 수 있다.
static와final을 사용하여 상수를 수식합니다
클래스의 다음 설명을 보십시오.
static int intVal = 42;
static String strVal = "Hello, world!";
이 클래스가 처음 사용될 때 컴파일러는
이라는 클래스 초기화 방법을 생성합니다. 이 방법은 42를 intVal에 저장하고strVal에 클래스 파일 문자열 상수표의 인용을 되돌려줍니다.이 변수들이 호출되었을 때, 필드를 통해 찾는 방식으로 접근합니다.final 키워드를 사용하여 최적화할 수 있습니다.
static final int intVal = 42;
static final String strVal = "Hello, world!";
이렇게 되면 이 종류는 더 이상
방법이 필요하지 않다. 왜냐하면 상기 상수는 정적 필드를 사용하여 초기화되기 때문이다.intVal을 호출할 때 정형값 42를 직접 사용하고strVal에 접근할 때 필드 찾기 대신 비용이 더 적은 문자열 상수를 사용합니다.참고: 이 최적화는 원래 유형과 String 상수에만 적용됩니다.
내부 Getters/Setters 방지
C++와 같은 언어에서는 직접적인 필드 접근 (예: i=mCount) 대신 getters (예: i=getCount) 를 자주 사용하는데, 이 기능은 C#, 자바 등 대상을 대상으로 하는 언어에도 사용된다.
그러나 안드로이드에서는 이렇게 사용하는 것이 좋은 습관이 아니다.방법은 필드 검색보다 비용이 더 많이 든다. 대상을 대상으로 프로그래밍하는 측면에서 getters와setters를 사용해야 하지만, 같은 종류의 내부에서는 가능한 한 필드에 직접 접근해야 한다.
JIT가 없는 상황에서 필드에 직접 접근하는 것은 Getter를 호출하는 방법보다 약 3배 빠르다.JIT가 있는 경우 필드에 직접 접근하는 것이 Getter보다 약 7배 빠르다.
증강된 for 순환 문법 사용하기
for-each와 같은 증강된 for 순환은 Iterable 인터페이스의 집합, 그룹을 실현하는 데 사용할 수 있습니다.다음 예제에서는 배열을 반복하는 몇 가지 스키마를 보여 줍니다.
static class Foo {
int mSplat;
}
Foo[] mArray = ...
public void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray[i].mSplat;
}
}
public void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
}
public void two() {
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}
방법zero()가 가장 느립니다. 순환할 때마다 그룹 길이를 가져와야 하기 때문에 이 비용은 JIT가 최적화할 수 없습니다.
방법one()는zero()보다 빠르고 모든 변수를 국부 변수로 저장하여 검색을 피하고 수조 길이의 성능 비용을 최적화합니다.
방법two()는 증강된 for 순환 문법을 사용하여 JIT가 없을 때 가장 빠르다.JIT가 있는 경우 메소드 원(one)의 성능과 거의 유사합니다.
개인 내부 클래스에 대해 패키지 접근 권한을 개인 접근 권한으로 대체합니다
다음 클래스의 정의를 보십시오.
public class Foo {
private class Inner {
void stuff() {
Foo.this.doStuff(Foo.this.mValue);
}
}
private int mValue;
public void run() {
Inner in = new Inner();
mValue = 27;
in.stuff();
}
private void doStuff(int value) {
System.out.println("Value is " + value);
}
}
상기 코드에서Foo클래스에 내부 클래스 인너가 정의되었는데 이 내부 클래스는 외부 클래스의 개인 방법과 개인 구성원 필드에 직접 접근했다.이는 합법적이며 최종 코드는 "Value is 27"을 출력합니다.
그러나 가상 기기의 측면에서 볼 때 내부 클래스 인너가 외부 클래스Foo에 직접 접근하는 개인적인 방법과 구성원은 비합법적이다. 왜냐하면 그들은 두 가지 다른 클래스이기 때문이다.컴파일러는 직접 액세스할 수 있도록 다음과 같은 몇 가지 작성 방법을 생성합니다.
/*package*/ static int Foo.access$100(Foo foo) {
return foo.mValue;
}
/*package*/ static void Foo.access$200(Foo foo, int value) {
foo.doStuff(value);
}
인너가 외부 클래스에 접근할 수 있는 개인적인 방법과 구성원 변수를 필요로 할 때 상기 정적 방법을 사용합니다.이것은 합성된 액세서리 방법을 통해 변수에 접근해야 한다는 것을 의미한다. 왜냐하면 직접 접근보다 느리기 때문이다. 이것은 예를 들어 보이지 않는 성능 손실을 설명할 수 있다.
상기 상황의 성능 비용을 피하기 위해 내부 클래스에 대한 접근 방법과 변수를 패키지 접근 권한이 아니라 패키지 접근 권한으로 성명할 수 있습니다.그러나 이는 같은 패키지의 다른 클래스에 직접 접근할 수 있다는 뜻이므로 공유 API에 나타나지 않는 것이 좋다.
부동 소수점 유형 사용 방지
안드로이드 기기에서 부동점형은 정형보다 2배 정도 느린 것으로 알려져 있다.
속도 면에서 볼 때 플로트와 더블은 현대 하드웨어 설비에서 거의 차이가 없다.공간 비용 측면에서 볼 때 더블은 플로트의 2배다.데스크톱의 경우 공간이 문제가 아니라면 더블을 우선선택해야 한다.
정형에 대해 말하자면 일부 프로세서는 하드웨어 곱셈이 있고 하드웨어 제법 지원이 부족하다. 이런 상황에서 정형 제법과 모형 연산은 소프트웨어 차원에서 처리해야 한다. 예를 들어 해시표를 설계하거나 대량의 수학 연산을 해야 한다.
Library 이해 및 사용
In addition to all the usual reasons to prefer library code over rolling your own, bear in mind that the system is at liberty to replace calls to library methods with hand-coded assembler, which may be better than the best code the JIT can produce for the equivalent Java. The typical example here is
String.indexOf()
and related APIs, which Dalvik replaces with an inlined intrinsic. Similarly, the System.arraycopy()
method is about 9x faster than a hand-coded loop on a Nexus One with the JIT. Native 메서드 사용
Android NDK를 사용하여 Native 코드를 작성하여 App 기능을 구현하는 것이 Java를 사용하는 것보다 효율적인 것은 아닙니다.예를 들어 자바와 Native의 상호작용은 비용이 필요하고 JIT는 이를 최적화할 수 없다.일단 Native 자원에 메모리(예를 들어 Native heap에서)를 분배하면 이러한 자원을 수집하기 어렵고 서로 다른 CPU 구조를 위해 서로 다른 버전을 컴파일해야 하며 심지어 같은 구조를 위해 여러 버전을 컴파일해야 한다. 예를 들어 G1의 ARM 프로세서를 위해 컴파일한 Native 코드는 Nexus One에서 성능을 충분히 발휘할 수 없다.Nexus One의 ARM에 컴파일된 코드는 G1의 ARM에서 실행할 수 없습니다.
기존의 Native 코드 라이브러리를 안드로이드에 사용하고 싶을 때, 자바 코드 모듈의 속도를 높이기 위해 Native 코드를 사용하는 것이 아니라, Native 코드를 사용해야 한다.
Native 코드를 사용하려면 JNI에 대한 지식이 필요합니다.
성능 신화
JIT가 없는 상황에서 명확한 유형의 대상을 통해 호출하는 방법은 인터페이스 호출보다 빠르다. 예를 들어HashMap 대상의 방법은 Map 인터페이스보다 호출이 빠르다. 이들의 효율은 약 6% 정도 차이가 난다. 만약에 JIT가 있다면 이들의 효율은 거의 같다.
JIT가 없는 경우 캐시 필드의 접근이 중복된 필드보다 20% 빠르다.JIT가 있을 때 필드 접근 비용은 로컬 접근과 같기 때문에 코드를 쉽게 읽을 수 있다고 생각하지 않으면 최적화할 필요가 없습니다.
지속적인 평가
최적화를 진행하기 전에 당신이 해결해야 할 문제를 확인하고 현재의 성능 상태를 정확하게 평가할 수 있도록 해야 한다. 그렇지 않으면 최적화를 한 후에도 최적화가 가져온 성능 향상을 평가할 수 없다.
Traceview를 사용해서 분석을 할 수 있지만 Traceview를 사용할 때 JIT가 비활성화되어 있기 때문에 성능이 좋지 않다고 느낄 수 있지만 JIT를 사용하면 성능이 현저히 향상될 수 있다는 것을 깨달아야 한다.Traceview의 건의에 따라 수정한 후에 Traceview가 없는 상황에서 최적화된 코드가 이전보다 속도가 더 빠르도록 반드시 확보해야 한다.
참조 자료: Performance Tips
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.