1

少なくとも Java では、プロキシ パターンには多くのオーバーヘッドがあります。正確な数値は覚えていませんが、小さなメソッドをラップする場合、プロキシはラップされたメソッドの 50 倍程度の時間がかかります。これが、たとえばjava.awt.image.BufferedImage.setRGB&getRGB本当に遅い理由です。実際の をラップする約 3 つのプロキシがありますbyte[]

なぜ50回?! プロキシが時間を 2 倍にしないのはなぜですか?


編集:=(

SO ではいつものように、私の質問が間違っているという回答がたくさんありました。そうではありません。これらのマイクロベンチマークではなく、BufferedImage、またはその他の実際のプロキシ パターンを確認してください。実際、BufferedImage の多くのピクセル操作を行う必要があり、その構造を知っている場合は、プロキシを手動で元に戻すことで、前述の大幅な高速化を実現できます。この回答を参照してください。

ああ、これが 50xのソースです。記事で詳しく説明されているように、ラップに時間がかかる場合、プロキシには顕著なペナルティはありませんが、小さなメソッドをラップしている場合は、大きなオーバーヘッドが発生します。

4

3 に答える 3

7

その「50回」という数字がどこから来たのかはわかりませんが、かなり疑わしいです. それぞれが何をしているかによっては、特定のプロキシがプロキシしているものよりも著しく遅い場合がありますが、それを一般化して「プロキシ パターンが非常に遅い」と言うのは、非常に劇的で非常に疑わしいものです論理的に飛躍する。

これを試して:

Thingy.java:

public class Thingy
{
    public int foo(int param1, int param2)
    {
        return param2 - param1;
    }
}

ThingyProxy.java:

public class ThingyProxy
{
    Thingy thingy;

    public ThingyProxy()
    {
        this.thingy = new Thingy();
    }

    public int foo(int param1, int param2)
    {
        return this.thingy.foo(param1, param2);
    }
}

WithoutProxy.java:

public class WithoutProxy
{
    public static final void main(String[] args)
    {
        Thingy t;
        int sum;
        int counter;
        int loops;

        sum = 0;
        t = new Thingy();
        for (loops = 0; loops < 300000000; ++loops) {
            sum = 0;
            for (counter = 0; counter < 100000000; ++counter) {
                sum += t.foo(1, 2);
            }
            if (sum != 100000000) {
                System.out.println("ERROR");
                return;
            }
        }
        System.exit(0);
    }
}

WithProxy.java:

public class WithProxy
{
    public static final void main(String[] args)
    {
        ThingyProxy t;
        int sum;
        int counter;
        int loops;

        sum = 0;
        t = new ThingyProxy();
        for (loops = 0; loops < 300000000; ++loops) {
            sum = 0;
            for (counter = 0; counter < 100000000; ++counter) {
                sum += t.foo(1, 2);
            }
            if (sum != 100000000) {
                System.out.println("ERROR");
                return;
            }
        }
        System.exit(0);
    }
}

私のマシンでの簡単な試行:

$ 時間 Java なしプロキシ

実質 0m0.894s
ユーザー 0分0.900秒
システム 0m0.000s

$ time Java WithProxy

実質 0m0.934s
ユーザー 0分0.940秒
システム 0m0.000s

$ 時間 Java なしプロキシ

実質 0m0.883s
ユーザー 0分0.850秒
システム 0m0.040s

$ time Java WithProxy

実質 0m0.937s
ユーザー 0分0.920秒
システム 0m0.030s

$ 時間 Java なしプロキシ

実質 0m0.898s
ユーザー 0分0.880秒
システム 0m0.030s

$ time Java WithProxy

実質 0m0.936s
ユーザー 0分0.950秒
システム 0m0.000s

少し遅い?はい。50倍遅い?いいえ。

現在、JVM のタイミングを計ることは非常に難しいことで有名であり、上記のような単純な実験は必然的に疑わしいものです。しかし、おそらく 50 倍の差が現れたと思います。

編集:上記の非常に少数のループが次のような数を投稿していることに言及する必要がありました。

実質 0m0.058s
ユーザー 0分0.040秒
システム 0m0.020s

...これにより、環境内での VM の起動時間がわかります。たとえば、上記のタイミングはほとんどが VM の起動ではなく、実際の実行時間にわずか 1 マイクロ秒の差があり、ほとんどが実行時間です。

于 2011-05-08T13:32:08.513 に答える
2

それらが違いを生むのを見た1つの場所は、何もしないコードです。JVMは、何もしないコードを検出でき、それを排除できます。ただし、メソッド呼び出しを使用すると、このチェックが混乱する可能性があり、コードは削除されません。このような例でメソッドがある場合とない場合のタイミングを比較すると、任意の比率を得ることができますが、メソッドなしのテストがどのように行われるかを見ると、コードが削除され、不当に高速になっていることがわかります。 。たとえば、ループごとに1クロックサイクルよりもはるかに高速です。


ゲッターやセッターのように、簡単なメソッドがインライン化されています。パフォーマンスにまったく影響を与えることはありません。私は実際のプログラムの50倍の主張を非常に疑っています。適切にテストすれば、これまでにない違いに近づくと思います。

于 2011-05-08T13:54:56.063 に答える