2016-05-20 19 views
1

私は自分のアプリケーションのパフォーマンスに敏感な部分を書いていますし、私はJITコンパイラ(すべての場合)は、以下の方法を最適化する方法をについて好奇心:JavaのJITコンパイラは簡単なループを "拡張"しますか?

private static int alphaBlend(int foreground, int background) { 
    int alpha = (foreground >> 24) & 0xFF; 
    int subAlpha = 0xFF - alpha; 
    return ((((((foreground >> 16) & 0xFF) * alpha) + (((background >> 16) & 0xFF)) * subAlpha) >> 8) << 16) 
      | ((((((foreground >> 8) & 0xFF) * alpha) + (((background >> 8) & 0xFF)) * subAlpha) >> 8) << 8) 
      | ((((foreground & 0xFF) * alpha) + ((background & 0xFF)) * subAlpha) >> 8); 
} 

private static int alphaBlendLoop(int foreground, int background) { 
    int alpha = (foreground >> 24) & 0xFF; 
    int subAlpha = 0xFF - alpha; 
    int blended = 0; 
    for (int shift = 16; shift >= 0; shift -= 8) { 
     blended |= (((((foreground >> shift) & 0xFF) * alpha) + (((background >> shift) & 0xFF)) * subAlpha) >> 8) << shift; 
    } 
    return blended; 
} 

は、これらのメソッドはをブレンドアルファを行います。基本的には、前景のRGBAピクセルと、RGBコンポーネントの値にアルファ値をあらかじめ乗算した背景のRG​​Bピクセルを結合します。

これらのメソッドはどちらも同じ入力に対して同じ値を返しますが、その実装は異なります。 個人的にはですが、私は後者の実装を読みやすくしていますが、パフォーマンスが低下する可能性があることを心配しています。両方の実装のためのバイトコードは、興味のある人のために、以下に含まれている(それはのIntelliJの「ショーバイトコード」ビューを使用して生成された):

private static alphaBlend(II)I 
    L0 
    LINENUMBER 95 L0 
    ILOAD 0 
    BIPUSH 24 
    ISHR 
    SIPUSH 255 
    IAND 
    ISTORE 2 
    L1 
    LINENUMBER 96 L1 
    SIPUSH 255 
    ILOAD 2 
    ISUB 
    ISTORE 3 
    L2 
    LINENUMBER 97 L2 
    ILOAD 0 
    BIPUSH 16 
    ISHR 
    SIPUSH 255 
    IAND 
    ILOAD 2 
    IMUL 
    ILOAD 1 
    BIPUSH 16 
    ISHR 
    SIPUSH 255 
    IAND 
    ILOAD 3 
    IMUL 
    IADD 
    BIPUSH 8 
    ISHR 
    BIPUSH 16 
    ISHL 
    ILOAD 0 
    BIPUSH 8 
    ISHR 
    SIPUSH 255 
    IAND 
    ILOAD 2 
    IMUL 
    ILOAD 1 
    BIPUSH 8 
    ISHR 
    SIPUSH 255 
    IAND 
    ILOAD 3 
    IMUL 
    IADD 
    BIPUSH 8 
    ISHR 
    BIPUSH 8 
    ISHL 
    IOR 
    ILOAD 0 
    SIPUSH 255 
    IAND 
    ILOAD 2 
    IMUL 
    ILOAD 1 
    SIPUSH 255 
    IAND 
    ILOAD 3 
    IMUL 
    IADD 
    BIPUSH 8 
    ISHR 
    IOR 
    IRETURN 
    L3 
    LOCALVARIABLE foreground I L0 L3 0 
    LOCALVARIABLE background I L0 L3 1 
    LOCALVARIABLE alpha I L1 L3 2 
    LOCALVARIABLE subAlpha I L2 L3 3 
    MAXSTACK = 4 
    MAXLOCALS = 4 

    private static alphaBlendLoop(II)I 
    L0 
    LINENUMBER 103 L0 
    ILOAD 0 
    BIPUSH 24 
    ISHR 
    SIPUSH 255 
    IAND 
    ISTORE 2 
    L1 
    LINENUMBER 104 L1 
    SIPUSH 255 
    ILOAD 2 
    ISUB 
    ISTORE 3 
    L2 
    LINENUMBER 105 L2 
    ICONST_0 
    ISTORE 4 
    L3 
    LINENUMBER 106 L3 
    BIPUSH 16 
    ISTORE 5 
    L4 
    FRAME FULL [I I I I I I] [] 
    ILOAD 5 
    IFLT L5 
    L6 
    LINENUMBER 107 L6 
    ILOAD 4 
    ILOAD 0 
    ILOAD 5 
    ISHR 
    SIPUSH 255 
    IAND 
    ILOAD 2 
    IMUL 
    ILOAD 1 
    ILOAD 5 
    ISHR 
    SIPUSH 255 
    IAND 
    ILOAD 3 
    IMUL 
    IADD 
    BIPUSH 8 
    ISHR 
    ILOAD 5 
    ISHL 
    IOR 
    ISTORE 4 
    L7 
    LINENUMBER 106 L7 
    IINC 5 -8 
    GOTO L4 
    L5 
    LINENUMBER 109 L5 
    FRAME CHOP 1 
    ILOAD 4 
    IRETURN 
    L8 
    LOCALVARIABLE shift I L4 L5 5 
    LOCALVARIABLE foreground I L0 L8 0 
    LOCALVARIABLE background I L0 L8 1 
    LOCALVARIABLE alpha I L1 L8 2 
    LOCALVARIABLE subAlpha I L2 L8 3 
    LOCALVARIABLE blended I L3 L8 4 
    MAXSTACK = 4 
    MAXLOCALS = 6 

直感的に、ループがデクリメント、条件を評価し、より多くの「仕事」と操作(ジャンプを必要とします、など)。しかし、ループも非常に予測可能です。常に正確に3回実行され、スコープ内で定義された変数は常に同じ3つの値を持ちます。そのようなシナリオで

、JITコンパイラ(または賢く静的コンパイラは?)alphaBlend実装に見られるように、おそらく長いワンライナーにそれを拡張することによって、このような些細なループを最適化することができるだろうか?あるいは、ループは一般的にそのような方法で最適化できないものですか?

+0

実際のコードでパフォーマンスを測定してから、コードを最適化することを決定してください。 – markspace

答えて

5

はい、HotSpotのJITはと定数伝播を作る最適化をアンロールループをサポートしていますalphaBlendLoopを手動で展開したalphaBlendに似たものに変換することができます。さらに読みやすいコードを作る小さなヘルパー関数:私はすべての3つのオプションは、性能が似ていることを確認するためにJMH benchmarkを作った

private static int blend(int foreground, int background, int alpha, int shift) { 
    int fg = (foreground >>> shift) & 0xff; 
    int bg = (background >>> shift) & 0xff; 
    return (fg * alpha + bg * (256 - alpha)) >>> 8 << shift; 
} 

public static int alphaBlend(int foreground, int background) { 
    int alpha = foreground >>> 24; 
    int R = blend(foreground, background, alpha, 0); 
    int G = blend(foreground, background, alpha, 8); 
    int B = blend(foreground, background, alpha, 16); 
    return R | G | B; 
} 

は、私は個人的には第三の選択肢を好むだろう。
Java 8u77 x64でテスト済みです。

Benchmark    Mode Cnt Score Error Units 
Blend.alphaBlendInline avgt 10 7,831 ± 0,045 ns/op 
Blend.alphaBlendLoop avgt 10 7,860 ± 0,025 ns/op 
Blend.alphaBlendMethod avgt 10 7,769 ± 0,056 ns/op 
1

Oracle VMの場合は、-XX:+PrintAssemblyを使用してJIT出力を検査できます。

(でキックするJITを取得するには、あなたのテストプログラムは、対象の方法に少なくとも10 000回を呼び出す必要があります。)

関連する問題