Android:空のメソッドを実行することで、どのくらいの間接型が生成されますか?

StackOverflow https://stackoverflow.com/questions/4345845

質問

リリース前にすべてのログ出力を削除する必要がないように、デバッグ出力を処理するクラスを作成しました。

public class Debug {
    public static void debug( String module, String message) {
        if( Release.DEBUG )
            Log.d(module, message);
    }
}

別の質問を読んだ後、私はIFステートメントの内容が一定のリリースである場合にコンパイルされていないことを知りました。

私が知りたいのは、この空の方法を実行することでどのくらいの間接関係が生成されるかということです。 (IF句が削除されたら、メソッドにコードが残っていません)それは私のアプリケーションに影響を与えるでしょうか?明らかに、モバイルハンドセットのために書くときのパフォーマンスは大きな問題です= P

ありがとう

ゲイリー

役に立ちましたか?

解決

Android 2.3.2でNexus Sで行われた測定:

10^6 iterations of 1000 calls to an empty static void function: 21s  <==> 21ns/call
10^6 iterations of 1000 calls to an empty non-static void function: 65s  <==> 65ns/call

10^6 iterations of 500 calls to an empty static void function: 3.5s  <==> 7ns/call
10^6 iterations of 500 calls to an empty non-static void function: 28s  <==> 56ns/call

10^6 iterations of 100 calls to an empty static void function: 2.4s  <==> 24ns/call
10^6 iterations of 100 calls to an empty non-static void function: 2.9s  <==> 29ns/call

コントロール:

10^6 iterations of an empty loop: 41ms <==> 41ns/iteration
10^7 iterations of an empty loop: 560ms <==> 56ns/iteration
10^9 iterations of an empty loop: 9300ms <==> 9.3ns/iteration

測定値を数回繰り返しました。重要な逸脱は見つかりませんでした。コールあたりのコストは、ワークロードによって大きく異なる可能性があることがわかります(おそらくJITコンパイルによるものです)が、3つの結論を導き出すことができます。

  1. Dalvik/Javaは、死んだコードの最適化を吸います

  2. 静的関数呼び出しは、非静的な関数よりもはるかに優れていることができます(非静的関数は仮想であり、仮想テーブルで調べる必要があります)

  3. Nexus Sのコストは70ns/sall(〜70 cpuサイクル)以下であり、ループイテレーションの1つの空のコストに匹敵します(つまり、ローカル変数の1つの増分と1つの条件チェック)

あなたの場合、文字列引数が常に評価されることを観察してください。文字列連結を行うと、中間文字列の作成が含まれます。これは非常に費用がかかり、多くのGCが含まれます。たとえば、関数の実行:

void empty(String string){
}

などの引数で呼び出されます

empty("Hello " + 42 + " this is a string " + count );

10^4そのような呼び出しの100回の反復には10秒かかります。それは10us/sallです。つまり、空の呼び出しよりも1000倍遅いです。また、膨大な量のGCアクティビティを生成します。これを回避する唯一の方法は、関数を手動でインライン化することです。つまり、デバッグ関数呼び出しの代わりに>> if <<ステートメントを使用します。それは醜いですが、それを機能させる唯一の方法です。

他のヒント

あなたが深くネストされたループ内からこれを呼ぶのでなければ、私はそれについて心配しません。

優れたコンパイラは空のメソッド全体を削除し、オーバーヘッドはまったくありません。 Dalvikコンパイラがすでにこれを行っているかどうかはわかりませんが、少なくともFroyoにJust-in-Timeコンパイラが到着してから、おそらくそうだと思います。

参照: インライン拡張

パフォーマンスの観点から、デバッグ関数に渡されるメッセージを生成するオーバーヘッドは、メモリ割り当てを行う可能性があるため、はるかに深刻になるでしょう。

Debug.debug(mymodule, "My error message" + myerrorcode);

メッセージを通しても発生します。残念ながら、目標がパフォーマンスの場合は、関数自体ではなく、この関数への呼び出しの周りに「if(release.debug)」が本当に必要です。これは多くのAndroidコードで表示されます。

これは興味深い質問であり、@misiu_mp分析が好きなので、Android 6.0.1を実行しているNexus 7で2016年のテストで更新すると思いました。これがテストコードです:

public void runSpeedTest() {
    long startTime;
    long[] times = new long[100000];
    long[] staticTimes = new long[100000];
    for (int i = 0; i < times.length; i++) {
        startTime = System.nanoTime();
        for (int j = 0; j < 1000; j++) {
            emptyMethod();
        }
        times[i] = (System.nanoTime() - startTime) / 1000;
        startTime = System.nanoTime();
        for (int j = 0; j < 1000; j++) {
            emptyStaticMethod();
        }
        staticTimes[i] = (System.nanoTime() - startTime) / 1000;
    }
    int timesSum = 0;
    for (int i = 0; i < times.length; i++) { timesSum += times[i]; Log.d("status", "time," + times[i]); sleep(); }
    int timesStaticSum = 0;
    for (int i = 0; i < times.length; i++) { timesStaticSum += staticTimes[i]; Log.d("status", "statictime," + staticTimes[i]); sleep(); }
    sleep();
    Log.d("status", "final speed = " + (timesSum / times.length));
    Log.d("status", "final static speed = " + (timesStaticSum / times.length));
}

private void sleep() {
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

private void emptyMethod() { }
private static void emptyStaticMethod() { }

sleep() オーバーフローを防ぐために追加されました Log.d バッファ。

私は何度も遊んでいましたが、結果は@misiu_mpとかなり一致していました。

10^5 iterations of 1000 calls to an empty static void function: 29ns/call
10^5 iterations of 1000 calls to an empty non-static void function: 34ns/call

静的メソッド呼び出しは、非静的メソッド呼び出しよりも常にわずかに高速でしたが、a)Android 2.3.2およびb)空のメソッド、静的、またはstatic、またはb)のコストがかかるため、ギャップは大幅に閉じられているように見えます。いいえ。

しかし、時間のヒストグラムを見ると、何か面白いことが明らかになります。コールの大部分は、静的であろうとなかろうと、30〜40nsを摂取し、データをよく見ると、実質的にすべて30nsです。

enter image description here

空のループを使用して同じコードを実行する(メソッドコールをコメントアウト)すると、平均速度は8nsを生成しますが、測定時間の約3/4は0NSですが、残りは正確に30nsです。

このデータを説明する方法はわかりませんが、 @Misiu_MPの結論がまだ続いているかどうかはわかりません。空の静的方法と非静的な方法の違いは無視でき、測定の優勢は正確に30nsです。そうは言っても、空のメソッドを実行するにはまだゼロ以外のコストがあるように思われます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top