3

2つの異なるプログラムが1つのタスクを実行するのに必要な時間を測定したかったのです。1つのプログラムはスレッドを使用し、もう1つのプログラムは使用しませんでした。タスクは2000000までカウントすることでした。

スレッドあるクラス:

public class Main {
    private int res1 = 0;
    private int res2 = 0;

    public static void main(String[] args) {
        Main m = new Main();

        long startTime = System.nanoTime();
        m.func();
        long endTime = System.nanoTime();

        long duration = endTime - startTime;
        System.out.println("duration: " + duration);
    }

    public void func() {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 1000000; i++) {
                    res1++;
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 1000000; i < 2000000; i++) {
                    res2++;
                }
            }
        });

        t1.start();
        t2.start();

        System.out.println(res1 + res2);
    }
}

スレッドのないクラス:

public class Main {

    private int res = 0;

    public static void main(String[] args) {
        Main m = new Main();

        long startTime = System.nanoTime();
        m.func();
        long endTime = System.nanoTime();

        long duration = endTime - startTime;
        System.out.println("duration: " + duration);

    }

    public void func() {

        for (int i = 0; i < 2000000; i++) {
            res++;
        }
        System.out.println(res);
    }
}

10回の測定後、平均結果(ナノ秒単位)は次のとおりです。

With threads:    1952358
Without threads: 7941479

私はそれを正しくやっていますか?
どうして、2つのスレッドを使用すると、2倍だけでなく、4倍速くなるのでしょうか。

4

4 に答える 4

8

行で

    t1.start();
    t2.start();

スレッドの実行を開始していますが、時間の測定を行う前に、実際にスレッドの実行が終了するのを待っているわけではありません。スレッドが終了するまで待つには、

   t1.join();
   t2.join();

joinメソッドは、スレッドが終了するまでブロックします。次に、実行時間を測定します。

于 2013-01-13T22:21:40.493 に答える
5

並列バージョンでは、メインスレッドが他の2つのスレッドを作成する量を測定しています。あなたは彼らの実行時間を測定していません。これが、超線形の高速化を実現している理由です。それらの実行時間を含めるには、それらをメインスレッドに結合する必要があります。

これらの行を後に追加しますt2.start();

     t1.join();  // wait until thread t1 terminates
     t2.join(); // wait until thread t2 terminates
于 2013-01-13T22:19:45.817 に答える
2

マルチスレッドバージョンが高速である主な理由は、ループが終了するのを待たないことです。スレッドが開始するのを待つだけです。

start()の後に追加する必要があります。

    t1.join();
    t2.join();

これを行うと、スレッドの開始に非常に時間がかかり、かなり遅くなることに注意してください。テストを100倍長くすると、スレッドを開始するコストはそれほど重要ではなくなります。

シングルスレッドの例では、適切にJIttedするのに時間がかかります。テストを少なくとも2秒間、繰り返し実行する必要があります

私のマルチスレッドバージョンは

public class Main {
    private long res1 = 0;
    public long p0, p1, p2, p3, p4, p5, p6, p7;
    private long res2 = 0;

    public static void main(String[] args) throws InterruptedException {
        Main m = new Main();

        for (int i = 0; i < 10; i++) {
            long startTime = System.nanoTime();
            m.func();
            long endTime = System.nanoTime();

            long duration = endTime - startTime;
            System.out.println("duration: " + duration);
        }
        assert m.p0 + m.p1 + m.p2 + m.p3 + m.p4 + m.p5 + m.p6 + m.p7 == 0;
    }

    public void func() throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000000000; i++) {
                    res1++;
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1000000000; i < 2000000000; i++) {
                    res2++;
                }
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(res1 + res2);
    }
}

マルチスレッドテストの場合、以下を出力します。

2000000000
duration: 179014396
4000000000
duration: 148814805
.. deleted ..
18000000000
duration: 61767861
20000000000
duration: 72396259

シングルスレッドバージョンの場合、1つのスレッドをコメントアウトして取得します

2000000000
duration: 266228421
4000000000
duration: 255203050
... deleted ...
18000000000
duration: 125434383
20000000000
duration: 125230354

予想どおり、十分に長く実行すると、2つのスレッドは1つのスレッドのほぼ2倍の速度になります。

要するに、

  • マルチスレッドコードでは、非同期ロギングやメッセージングなどの操作が完了するのを待たない場合、現在のスレッドの遅延を小さくすることができます。

  • 実行する重要なCPUバウンドタスクがない限り、シングルスレッドコーディングはマルチスレッドコードよりもはるかに高速(かつ単純)になります(または同時IOを実行できます)。

  • 同じJVMでテストを繰り返し実行すると、異なる結果が得られる可能性があります

于 2013-01-13T22:43:29.160 に答える
1

Javaでベンチマークを行うときに覚えておく必要のあるトリックがいくつかあります。

最初のこれは、何かをベンチマークする場合と同じです。意味のある理由がないため、ある実行が別の実行よりも遅くなる可能性があります。これを回避するには、複数回実行して平均を取ります(つまり、多くの場合)。

2つ目は、Javaに固有ではないかもしれませんが、驚くべきことかもしれません。JavaVMは、「ウォームアップ」に時間がかかる場合があります。コードを100回実行すると、コンパイルされたコードchangeは、非常に一般的なコードパスに従うことができます。これに対抗するには、統計の取得を開始する前にコードを何度も実行します。

ウォームアップにかかる時間は、JVMの設定によって異なります。頭のてっぺんから思い出せません。

もちろん、これは、スレッド化されたプログラムを実際に測定していないという他の回答が指摘している問題とはまったく別のものです。

編集:注意すべきもう1つのことは、特定の変数/ループ/プログラム全体が完全に無意味であることをコンパイラーが認識していることです。このような状況では、完全に削除される可能性があります。使用する必要がある場合res1res2、コンパイルされたコードからループが完全に削除される場合があります。

編集:あなたが実際にすべての計数変数を使用していることに気づきました-それでも知っておくと便利なことなので、そのままにしておきます。

于 2013-01-13T22:25:07.800 に答える