7

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

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

別の質問を読んだ後、定数Release.DEBUGがfalseの場合、ifステートメントの内容がコンパイルされないことを学びました。

私が知りたいのは、この空のメソッドを実行することによって生成されるオーバーヘッドの量です。(if句が削除されると、メソッドにコードが残りません)アプリケーションに影響はありますか?明らかに、携帯電話用に書く場合、パフォーマンスは大きな問題です= P

ありがとう

ゲイリー

4

5 に答える 5

14

Android2.3.2を搭載したNexusSで行われた測定:

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. ネクサスのコストは70ns/呼び出し(つまり、約70 cpuサイクル)以下であり、ループ反復の1つの空のコスト(つまり、ローカル変数の1つのインクリメントと1つの条件チェック)に匹敵します。

あなたの場合、文字列引数が常に評価されることに注意してください。文字列の連結を行う場合、これには中間文字列の作成が含まれます。これは非常にコストがかかり、多くのgcが必要になります。たとえば、関数を実行します。

void empty(String string){
}

次のような引数で呼び出されます

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

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

于 2011-02-15T21:55:37.427 に答える
2

優れたコンパイラは空のメソッド全体を削除するため、オーバーヘッドはまったく発生しません。Dalvikコンパイラがすでにこれを行っているかどうかはわかりませんが、少なくともFroyoを使用したJust-in-timeコンパイラが登場して以来、おそらくそうなっていると思います。

参照:インライン展開

于 2010-12-03T13:21:07.043 に答える
2

深くネストされたループ内からこれを呼び出さない限り、私はそれについて心配しません。

于 2010-12-03T13:16:25.140 に答える
2

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

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

これは、メッセージがビニングされても発生します。残念ながら、目標がパフォーマンスである場合は、関数自体の内部ではなく、この関数の呼び出しの周りに「if(Release.DEBUG)」が本当に必要です。これは、多くのAndroidコードで確認できます。

于 2010-12-03T17:35:24.720 に答える
1

これは興味深い質問で、@ misiu_mp分析が好きなので、Android6.0.1を実行しているNexus7で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)静的または静的または空のメソッドを呼び出すにはまだコストがかかるようです。いいえ。

ただし、時間のヒストグラムを見ると、興味深いことがわかります。静的であるかどうかに関係なく、呼び出しの大部分は30〜40 nsかかり、データを注意深く見ると、事実上すべて30nsです。

ここに画像の説明を入力してください

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

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

于 2016-04-06T01:12:22.840 に答える