6

簡単な計算を実行しようとしています(Math.random()10000000回呼び出されます)。驚くべきことに、単純な方法で実行すると、ExecutorServiceを使用するよりもはるかに高速に実行されます。

ExecutorServiceの驚くべきパフォーマンスの損益分岐点で別のスレッドを読んだことがあります---経験則?バッチを使用して実行して答えを追跡しようとしましたCallableが、パフォーマンスはまだ悪いです

現在のコードに基づいてパフォーマンスを向上させるにはどうすればよいですか?

import java.util.*;
import java.util.concurrent.*;

public class MainTest {
    public static void main(String[]args) throws Exception {
        new MainTest().start();;
    }

    final List<Worker> workermulti = new ArrayList<Worker>();
    final List<Worker> workersingle = new ArrayList<Worker>();
    final int count=10000000;

    public void start() throws Exception {
        int n=2;

        workersingle.add(new Worker(1));
        for (int i=0;i<n;i++) {
            // worker will only do count/n job
            workermulti.add(new Worker(n));
        }

        ExecutorService serviceSingle = Executors.newSingleThreadExecutor();
        ExecutorService serviceMulti = Executors.newFixedThreadPool(n);
        long s,e;
        int tests=10;
        List<Long> simple = new ArrayList<Long>();
        List<Long> single = new ArrayList<Long>();
        List<Long> multi = new ArrayList<Long>();

        for (int i=0;i<tests;i++) {
            // simple
            s = System.currentTimeMillis();
            simple();
            e = System.currentTimeMillis();
            simple.add(e-s);

            // single thread
            s = System.currentTimeMillis();
               serviceSingle.invokeAll(workersingle); // single thread
            e = System.currentTimeMillis();
            single.add(e-s);

            // multi thread
            s = System.currentTimeMillis();
               serviceMulti.invokeAll(workermulti);
            e = System.currentTimeMillis();
            multi.add(e-s);
        }
        long avgSimple=sum(simple)/tests;
        long avgSingle=sum(single)/tests;
        long avgMulti=sum(multi)/tests;
        System.out.println("Average simple: "+avgSimple+" ms");
        System.out.println("Average single thread: "+avgSingle+" ms");
        System.out.println("Average multi thread: "+avgMulti+" ms");

        serviceSingle.shutdown();
        serviceMulti.shutdown();
    }

    long sum(List<Long> list) {
        long sum=0;
        for (long l : list) {
            sum+=l;
        }
        return sum;
    }

    private void simple() {
        for (int i=0;i<count;i++){
            Math.random();
        }
    }

    class Worker implements Callable<Void> {
        int n;

        public Worker(int n) {
            this.n=n;
        }

        @Override
        public Void call() throws Exception {
            // divide count with n to perform batch execution
            for (int i=0;i<(count/n);i++) {
                Math.random();
            }
            return null;
        }
    }
}

このコードの出力

Average simple: 920 ms
Average single thread: 1034 ms
Average multi thread: 1393 ms

編集:Math.random()が同期されたメソッドであるため、パフォーマンスが低下します。スレッドごとに新しいRandomオブジェクトでMath.random()を変更した後、パフォーマンスが向上しました。

新しいコードの出力(スレッドごとにMath.random()をRandomに置き換えた後)

Average simple: 928 ms
Average single thread: 1046 ms
Average multi thread: 642 ms
4

3 に答える 3

13

Math.random()は同期されます。同期の全体的なポイントの一種は、物事が衝突しないように速度を落とすことです。新しいRandomのように、同期されていないものを使用したり、各スレッドに独自のオブジェクトを与えて操作したりします。

于 2011-08-23T02:20:20.923 に答える
4

他のスレッドの内容を読むとよいでしょう。そこにはたくさんの良いヒントがあります。

おそらく、ベンチマークで最も重要な問題は、Math.random()コントラクトによると、「このメソッドは適切に同期されているため、複数のスレッドで正しく使用できます。ただし、多くのスレッドで疑似乱数を大量に生成する必要がある場合、各スレッドが独自の疑似乱数ジェネレーターを持つようにすることで、競合を減らすことができます。」

これを次のように読みます。メソッドは同期されているため、1つのスレッドだけが同時に有効に使用できる可能性があります。したがって、タスクを分散するために大量のオーバーヘッドを実行しますが、タスクを再度シリアルに実行するように強制するだけです。

于 2011-08-23T02:25:18.333 に答える
1

複数のスレッドを使用する場合は、追加のスレッドを使用するオーバーヘッドに注意する必要があります。また、アルゴリズムに並行して実行できる作業があるかどうかを判断する必要があります。したがって、複数のスレッドを使用するオーバーヘッドを超えるのに十分な大きさの、同時に実行できる作業が必要です。

この場合、最も簡単な回避策は、各スレッドで個別のランダムを使用することです。あなたが抱えている問題は、マイクロベンチマークとして、ループは実際には何も行わず、JITは何も行わないコードを破棄するのに非常に優れているということです。これの回避策は、ランダムな結果を合計してから返すことcall()です。これは通常、JITがコードを破棄するのを防ぐのに十分だからです。

最後に、たくさんの数値を合計したい場合は、それらを保存して後で合計する必要はありません。あなたが行くようにあなたはそれらを合計することができます。

于 2011-08-23T08:12:23.033 に答える