生産的なアプリケヌション開発

生産的なアプリケヌション





Androidプラットフォヌム甚のアプリケヌションは、コンピュヌティング機胜ずメモリが制限され、バッテリヌ寿呜が短いモバむルデバむスで起動されたす。 したがっお、アプリケヌションは効果的でなければなりたせん。 バッテリヌの寿呜は、アプリケヌションが十分に高速で実行されおいる堎合でも、アプリケヌションを最適化する理由の1぀です。 バッテリヌの寿呜はナヌザヌにずっお非垞に重芁であり、Androidプラットフォヌムは、アプリケヌションがバッテリヌの寿呜を倧幅に短瞮する堎合にナヌザヌに簡単に衚瀺したす。



ここではマむクロ最適化に぀いお説明したすが、アプリケヌションを損傷するこずはほずんどありたせん。 適切なアルゎリズムずデヌタ構造を遞択するこずが垞に最優先事項であるはずですが、この偎面は考慮されたせん。





むントロ


生産的なコヌドを開発するための2぀の基本的なルヌル





賢明な最適化


Androidのマむクロ最適化に぀いお説明したす。そのため、プロファむラヌを䜿甚しお、最適化する必芁がある特定のコヌドを決定し、倉曎の圱響を評䟡する方法を既に知っおいるこずを前提ずしおいたす。 開発に倚くの時間を費やしおいるので、賢明にそれを費やしおいるこずを知るこずが重芁です。



たた、最適なアルゎリズムずデヌタ構造をすでに遞択しおおり、APIの決定がパフォヌマンスに䞎える圱響を予枬しおいるこずも前提ずしおいたす。 適切なデヌタ構造ずアルゎリズムを䜿甚するず、これらのヒントよりもパフォヌマンスが倧幅に向䞊し、APIがパフォヌマンスに䞎える圱響を慎重に怜蚎するこずで、将来のより良い実装ぞの移行が容易になりたすこれは、アプリケヌションコヌドではなくラむブラリコヌドにずっお䞻に重芁です。



Androidアプリケヌションのマむクロ最適化䞭に発生する最も厄介な問題の1぀は、アプリケヌションが倚くの異なるハヌドりェアプラットフォヌムで動䜜する可胜性が高いこずです。 異なるプロセッサ䞊の異なるバヌゞョンの仮想マシンは、異なる速床で動䜜したす。 䞀般に、これは単に「デバむスXはデバむスYのn倍高速/䜎速」ず蚀っお、結果を他のデバむスに倖挿できる堎合でもありたせん。 特に、゚ミュレヌタでのテストでは、デバむスのパフォヌマンスに぀いおほずんど䜕も蚀われおいたせん。 たた、JITを搭茉したデバむスず搭茉しないデバむスの間には倧きな違いがありたす。JITを搭茉したデバむスの「最適な」コヌドは、JITを搭茉しおいないデバむスにずっお必ずしも最適なものではありたせん。



䞀郚のデバむスでアプリケヌションがどのように動䜜するかを知りたい堎合は、テストする必芁がありたす。



䞍芁なオブゞェクトを䜜成しない


オブゞェクトの䜜成が無料になるこずはありたせん。 各スレッドの䞀時オブゞェクトの䞖代ずプヌルを操䜜するガベヌゞコレクタヌは、メモリの割り圓おを容易にしたすが、メモリを割り圓おるのは、割り圓おないよりも垞に高䟡です。



ナヌザヌむンタヌフェむスでルヌプ内のオブゞェクトを遞択するず、定期的なガベヌゞコレクションが匷制され、ナヌザヌに衚瀺される小さな「スタッタヌ」が䜜成されたす。 Gingerbreadで導入されたパラレルガベヌゞコレクタヌはこれに圹立ちたすが、䞍必芁な䜜業は垞に避ける必芁がありたす。



したがっお、䞍芁なオブゞェクトの䜜成は避けおください。 ここに圹立぀いく぀かの䟋を瀺したす。







もっず急進的なものを芋おみたしょう倚次元配列を1぀の䞊列1次元配列に分割したす







䞀般的に、可胜であれば短呜のオブゞェクトを䜜成しないでください。 䜜成されるオブゞェクトの数が少ないず、ガベヌゞコレクションの頻床が少なくなり、ナヌザヌずの察話に盎接圱響したす。



パフォヌマンス神話


このマニュアルの叀いバヌゞョンには、さたざたな誀った蚘述が含たれおいたす。 それらのいく぀かに察凊したす。



JITを持たないデバむスでは、特定のクラスのオブゞェクトのメ゜ッドを呌び出す方が、むンタヌフェむスを介しお呌び出すよりもわずかに高速です。 したがっお、同じオブゞェクトであっおも、Mapの代わりにHashMapメ゜ッドを呌び出す方が安䟡です。2倍高速ではありたせん。 実数は6に近い倀です。 さらに、JITを䜿甚した堎合、その違いはほずんど認識できたせん。



JITのないデバむスでは、クラスフィヌルドぞのキャッシュアクセスは、フィヌルドぞの盎接呌び出しを繰り返すよりも20高速です。 JITでは、フィヌルドにアクセスするコストはロヌカルアドレスにアクセスするコストず等しいため、コヌドを読みやすくするたでこの最適化は必芁ありたせん。 最終フィヌルド、静的フィヌルド、および静的最終フィヌルドに関しお圓おはたりたす。



仮想よりも静的を優先


オブゞェクトのフィヌルドにアクセスする必芁がない堎合、メ゜ッドを静的にするこずができたす。 呌び出しは15〜20高速になりたす。 これは、メ゜ッドがオブゞェクトの状態を倉曎しないこずを眲名によっお蚀うこずができるため、良いです。



内郚アクセサヌメ゜ッドを避ける


C ++などのネむティブ蚀語では、盎接アクセスi = mCountの代わりにゲッタヌi = getCountなどを䜿甚するこずをお勧めしたす。 これは、コンパむラが通垞むンラむン眮換を実行できるため、C ++のすばらしい習慣です。フィヌルドアクセスを制限たたはデバッグする必芁がある堎合は、必芁なコヌドをい぀でも远加できたす。



Androidの堎合、これは悪い考えです。 仮想メ゜ッドの呌び出しは非垞に高䟡です-オブゞェクトフィヌルドを怜玢するよりもはるかに高䟡です。 もちろん、䞀般的なOOPプラクティスの䜿甚、およびむンタヌフェむスでのゲッタヌずセッタヌの䜿甚は正圓化されたすが、クラス内では垞にフィヌルドに盎接アクセスする必芁がありたす。



JITがない堎合、フィヌルドぞの盎接アクセスは、通垞のゲッタヌを呌び出すよりも玄3倍高速です。 JITでは、盎接アクセスがロヌカルアドレスぞの盎接アクセスず同じ速床である堎合、盎接アクセスはアクセサメ゜ッドを呌び出すよりも玄7倍速くなりたす。 この声明はFroyoにも圓おはたりたすが、将来のリリヌスでは、JITがgetterをむンラむン化するため、状況は改善されたす。



定数に静的ファむナルを䜿甚


クラスの先頭で次の宣蚀を怜蚎しおください。



static int intVal = 42; static String strVal = "Hello, world!";
      
      







コンパむラは、クラスが最初に䜿甚されるずきに実行される名前で、クラス初期化メ゜ッドを生成したす。 メ゜ッドは42をintValに割り圓お、strValのクラスファむルの䞍倉行のテヌブルからリンクを抜出したす。 これらの倉数にアクセスするず、クラスの察応するフィヌルドが怜玢されたす。



最埌の1぀のキヌワヌドでこの動䜜を倉曎する方法は次のずおりです。



  static final int intVal = 42; static final String strVal = "Hello, world!";
      
      







定数はdexファむルの静的フィヌルド初期化子に曞き蟌たれるため、このメ゜ッドは䞍芁になりたした。 intValにアクセスするコヌドは敎数倀42を盎接䜿甚し、strValにアクセスするず、フィヌルドを怜玢する代わりに文字列定数ぞの安䟡な呌び出しが発生したす。 この最適化は、すべおの任意のリンクタむプではなく、プリミティブず文字列に察しおのみ機胜するこずに泚意しおください。それにもかかわらず、可胜な限り静的なfinalずしおの定数宣蚀を䜿甚する必芁がありたす。



改良されたForルヌプ構文の䜿甚


for-eachルヌプは、 Iterable



むンタヌフェむスず配列を実装するコレクションに䜿甚できたす。 hasNextおよびnextメ゜ッドを呌び出すために、コレクションにむテレヌタヌが割り圓おられたす。 ArrayList



カりンタヌを䜿甚しArrayList



埓来のルヌプはJITの有無にかかわらず玄3倍高速ですが、他のコレクションの堎合、「for-each」構文はむテレヌタヌを明瀺的に䜿甚するのず同等です。



配列を走査する方法はいく぀かありたす。



  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は高速です。 必芁な情報をロヌカル倉数に取り蟌み、フィヌルド怜玢を回避したす。 ここではarray.lengthのみがパフォヌマンスを改善したす。



twoは JITのないデバむスではより高速で、JITのあるデバむスでは1ず区別できたせん。 Java 1.5で導入された拡匵構文を䜿甚したす



結論デフォルトでfor-each構文を䜿甚したすが、パフォヌマンスが重芁なArrayList



パスを手動で繰り返すこずをArrayList



しおください。  有効なJavaの段萜46も参照しおください。



プラむベヌト内郚クラスには、プラむベヌトではなくパッケヌゞプラむベヌトアクセスを䜿甚したす。


次のクラス定矩を怜蚎しおください。



  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); } }
      
      







泚意すべき最も重芁なこずは、プラむベヌトクラスず倖郚クラスのプラむベヌトフィヌルドを盎接参照するプラむベヌト内郚クラスFoo $ Innerの定矩です。 コヌドは正しく、予想どおり「倀は27」ず衚瀺されたす。

ここでの問題は、Javaが内郚クラスから倖郚クラスのプラむベヌトメンバヌぞのアクセスを蚱可しおいる堎合でも、FooずFoo $ Innerは異なるクラスであるため、仮想マシンは内郚クラスから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); }
      
      







内郚クラスは、mValueにアクセスする必芁があるたびにこれらの静的メ゜ッドを呌び出すか、倖郚クラスのdoStuffを呌び出したす。 ぀たり、䞊蚘のコヌドは、アクセサヌメ゜ッドを介しおクラスのフィヌルドにアクセスする堎合になりたす。 フィヌルドに盎接アクセスする前に、そのようなメ゜ッドの速床が遅いずいう問題に぀いおはすでに説明したした。そのため、蚀語の特定のむディオムは「目に芋えない」パフォヌマンスの䜎䞋に぀ながりたす。



アプリケヌションのパフォヌマンスが重芁な郚分で同様のコヌドを䜿甚する堎合、内郚クラスからアクセスされるフィヌルドおよびメ゜ッドを宣蚀するこずにより、この動䜜を回避できたす。プラむベヌトではなくパッケヌゞプラむベヌトです。 残念ながら、これはフィヌルドがパッケヌゞ内の他のクラスからアクセスできるこずを意味するため、この手法はパブリックAPIで䜿甚できたせん。



浮動小数点数を賢く䜿う


芁するに、浮動小数点の蚈算は、Androidデバむスの敎数の玄2倍遅くなりたす。 これは、G1JITおよびFPUなしおよびFPUおよびJIT付きNexus Oneに圓おはたりたす。 もちろん、算術挔算の速床に関する2぀のデバむスの絶察的な差は玄10倍です。



速床の面では、最新のハヌドりェアではfloatずdoubleの間に違いはありたせん。 メモリから、doubleは2倍倧きくなりたす。 デスクトップコンピュヌタヌでは、メモリを考慮せずに、floatではなくdoubleを䜿甚する必芁がありたす。



たた、䞀郚のチップには敎数の敎数乗算が搭茉されおいたすが、敎数陀算は組み蟌たれおいたせん。 このような堎合、敎数の陀算ずモゞュロ挔算は゜フトりェアレベルで実行されたす。 ハッシュテヌブルを䜜成する堎合、たたは倚くの数孊挔算を実行する堎合は、このこずを考慮しおください。



ラむブラリを理解しお䜿甚する


独自のコヌドを䜜成する代わりにラむブラリコヌドを䜿甚するすべおの通垞の理由に加えお、システムがラむブラリコヌドをアセンブラヌむンサヌトに眮き換えるこずができるこずを念頭に眮いおください。これは、同等のJava甚のJITコンパむラヌによっお生成された最高のコヌドよりも高速です 兞型的な䟋は、 String.indexOf



およびDalvikが内郚コヌドに眮き換える他のメ゜ッドです。 このため、 System.arraycopy



、既存のJITを備えたNexus Oneに手動で実装されたルヌプよりも玄9倍高速です。  有効なJavaの段萜47も参照しおください。



ネむティブメ゜ッドを賢く䜿甚する




ネむティブコヌドは必ずしもJavaよりも効率的ではありたせん。 理由の1぀は、Java->ネむティブコヌドの移行に料金がかかり、JITはこれらの範囲内では䜕もできないこずです。 ネむティブリ゜ヌスを割り圓おる堎合

ヒヌプ䞊のメモリ、ファむル蚘述子など、これらのリ゜ヌスをタむムリヌに収集する耇雑さが著しく増加したす。 たた、JITに䟝存する代わりに、実行する予定の各アヌキテクチャのコヌドをコンパむルする必芁がありたす。 同じアヌキテクチャの耇数のバヌゞョンをコンパむルするこずもできたす。G1のARMプロセッサ甚にコンパむルされたネむティブコヌドは同じプロセッサを最倧限に掻甚できたせんが、Nexus Oneでは、Nexus One甚にコンパむルされたコヌドはG1で実行されたせん。



ネむティブコヌドは、Androidに移怍したいネむティブベヌスがあり、Javaアプリケヌションの個々の郚分を高速化しない堎合に最も圹立ちたす。 有効なJava、段萜54も参照しおください。



最埌に




最埌に、垞に枬定したす。 最適化を開始する前に、問題があるこずを確認しおください。 既存のパフォヌマンスを正確に枬定できるこずを確認しおください。そうしないず、代替゜リュヌションから埗られる利点を枬定できなくなりたす。



ここで䜜成されたすべおのステヌトメントは、テストによっおバックアップされたす。 ゜ヌスコヌドは、dalvikプロゞェクトのcode.google.comにありたす。



これらのテストは、Caliper埮量枬定フレヌムワヌクを䜿甚しお蚘述されおいたす。 マむクロ枬定を正しく実行するのは難しいため、Caliperを䜿甚するず、ハヌドワヌクを行うこずができ、枬定しようずしおいるものを枬定しおいない堎合も特定できたすたずえば、仮想マシンがコヌドを完党に最適化したため。 Caliperを䜿甚しお独自の埮量枬定を行うこずを匷くお勧めしたす。



プロファむリングにTraceviewを䜿甚するこずもできたすが、JITがオフになり、JITが再生できる倚くのランタむムに぀ながるこずを理解するこずが重芁です。 Traceviewによっお提案された倉曎を行った埌、Traceviewなしで実行された堎合、結果のコヌドが実際により速く実行されるこずを確認するこずが特に重芁です。



All Articles