15

以前にいくつかのパフォーマンス テストを行っていましたが、得られた結果を説明できません。

以下のテストを実行するとき、コメントを外すprivate final List<String> list = new ArrayList<String>();と、パフォーマンスが大幅に向上します。私のマシンでは、そのフィールドが存在する場合は 70 ~ 90 ミリ秒でテストが実行されますが、コメント アウトされている場合は 650 ミリ秒です。

System.out.println((end - start) / 1000000);また、print ステートメントを に変更すると、変数のないテストが 650 ミリ秒ではなく 450 ~ 500 ミリ秒で実行されることにも気付きました。変数が存在する場合は効果がありません。

私の質問:

  1. 私がその変数を使用していないことを考えると、変数の有無にかかわらず、ほぼ10の係数を説明できる人はいますか?
  2. その print ステートメントはパフォーマンスをどのように変更できますか (特に、パフォーマンス測定ウィンドウの後に来るため)。

ps: 順次実行すると、3 つのシナリオ (変数あり、変数なし、別の print ステートメントあり) はすべて約 260 ミリ秒かかります。

public class SOTest {

    private static final int ITERATIONS = 10000000;
    private static final int THREADS = 4;

    private volatile long id = 0L;
    //private final List<String> list = new ArrayList<String>();

    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(THREADS);
        final List<SOTest> objects = new ArrayList<SOTest>();
        for (int i = 0; i < THREADS; i++) {
            objects.add(new SOTest());
        }

        //warm up
        for (SOTest t : objects) {
            getRunnable(t).run();
        }

        long start = System.nanoTime();

        for (SOTest t : objects) {
            executor.submit(getRunnable(t));
        }
        executor.shutdown();
        executor.awaitTermination(10, TimeUnit.SECONDS);

        long end = System.nanoTime();
        System.out.println(objects.get(0).id + " " + (end - start) / 1000000);
    }

    public static Runnable getRunnable(final SOTest object) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < ITERATIONS; i++) {
                    object.id++;
                }
            }
        };
        return r;
    }
}

編集

3 つのシナリオで 10 回実行した結果を以下に示します。

  • 短い print ステートメントを使用して、変数なしで
  • 変数なしで、長い print ステートメントを使用 (オブジェクトの 1 つを出力)
  • 順次実行 (1 スレッド)
  • 変数で
1   657 473 261 74
2   641 501 261 78
3   651 465 259 86
4   585 462 259 78
5   639 506 259 68
6   659 477 258 72
7   653 479 259 82
8   645 486 259 72
9   650 457 259 78
10  639 487 272 79
4

4 に答える 4

10

クリア (偽) 共有

メモリ内のレイアウトにより、オブジェクトはキャッシュ ラインを共有します... (このサイトでも) 何度も説明されています。この問題は、C# (または C/C++)にも同様に適用されます。

コメントアウトされた行を追加してオブジェクトをパディングすると、共有が少なくなり、パフォーマンスが向上します。

編集:2番目の質問を見逃しました:


その print ステートメントはパフォーマンスをどのように変更できますか (特に、パフォーマンス測定ウィンドウの後に来るため)。

ウォーミングが不十分だと思います。GC ログとコンパイル ログの両方を出力して、干渉がなく、コードが実際にコンパイルされていることを確認してください。 java -server適切なコードを生成するには、できればすべてがメイン ループにあるわけではなく、10,000 回の反復が必要です。

于 2012-08-07T15:20:33.623 に答える
1

かなり面白い旅。これはもっと「私の結果の答えはここにあります」です。他の人がより良い反応を思い付くのではないかと思います。

あなたは明らかにいくつかの興味深い最適化ポイントに到達しています。objects.get(0).idlongステートメントにinを追加するprintlnと、フィールドの使用に関するいくつかの最適化が削除されていると思いidます。++他に使用法がないことを除けばid、オプティマイザがいくつかのアクセスを最適化volatile idして速度を向上させている可能性があります。idaを使用してフィールドにアクセスするだけでlong x = objects.get(0).id;、同じパフォーマンスが向上します。

このList分野ははるかに興味深いものです。private String foo = new String("weofjwe");フィールドが追加された場合も同じパフォーマンスの向上が見られますが、コンパイル時に行われるため、オブジェクトを作成していない場合はそうではありません。私はそれが適切であると確信していましたが、そうではないようです。これはコンストラクターの最適化と関係があると推測できますが、それをより効果的に行ったとしても、最適化を停止させることが追加されています。private String foo = "weofjwe";"..."finalnewvolatile

于 2012-08-07T15:13:14.573 に答える