4

要するに:

シングル コアでマルチスレッド アプリを実行すると、どのようなシナリオでパフォーマンスが低下する可能性がありますか?

マルチスレッド アプリのアフィニティを 1 つのコアのみを使用するように設定するのはどうですか?

長文:

独自のスレッドで 2D エンジンの物理演算を実行しようとしています。それは機能し、最初はパフォーマンスが正常に見えましたが、ゲームを 10K FPS で実行し、物理を 120FPS で実行するように指示し、タスク マネージャーに移動して、プログラムが 1 つのコアのみを使用できるようにアフィニティを設定しました。

FPS は、アフィニティを 1 つのコアに設定する前は ~1700 でしたが、その後は ~70FPS になりました。こんなに減るとは思わなかった。私は、ゲームを 300 FPS で実行し、物理演算を 60 FPS で実行するように指示しました。

同じことが起こりました。

あまり考えていなかったので、エンジンの改造を続けました。描画コードの一部を 300 FPS、物理演算用に 60FPS に変更した後、後でもう一度テストしました。すべてのコアを許可すると、300 FPS を問題なく管理でき、シングル コア FPS との親和性は 4 に低下しました。シングル コアでマルチスレッド アプリを実行するのがそれほど悪いことではないことがわかりました。アフィニティを単一のコアに設定します。

これは、レンダリング/物理がどのように実行されるかについてです...

ループ開始

(1.0 / FPS) が経過するまで入力を収集します。

更新を呼び出します。

ゲーム内で物理データが使用されるため、物理スレッド ミューテックスをロックします。この更新呼び出しのすべてが完了するまで、エンジンは何も更新しないようにします。

Draw 関数オブジェクト (何を描画するか、どこに描画するか、どのように描画するかを保持する) を Render キューに送信するゲーム内のすべてを更新します。

ミューテックスのロックを解除します。

レンダラーは各関数オブジェクトで operator() を呼び出し、それらをキューから削除します。

画面を更新します。

ループを繰り返します。

物理スレッド ループ:

    ALLEGRO_TIMER* timer(al_create_timer(1.0f / 60.0f));
    double prevCount(0);

    al_start_timer(timer);
    while(true)
    {
        auto_mutex lock(m_mutex);

        if(m_shutdown)
            break;
        if (!m_allowedToStep)
            continue;
                    // Don't run too fast. This isn't final, just simple test code.
        if (!(al_get_timer_count(timer) > prevCount))
            continue;

        prevCount = al_get_timer_count(timer);

        m_world->Step(1.0f / 60.0f, 10, 10); 
        m_world->ClearForces();

    }

// 注: 自動ミューテックスは、コンストラクタでミューテックスをロックし、デストラクタでロックを解除するために作成した単純なオブジェクトです。Allegro 5 のスレッド機能を使用しています。

4

2 に答える 2

8

シングル コアでマルチスレッド アプリを実行すると、どのようなシナリオでパフォーマンスが低下する可能性がありますか?

マルチスレッド アプリのアフィニティを 1 つのコアのみを使用するように設定するのはどうですか?

どちらの場合も、答えはほとんど同じです。プログラムが単一のコアで実行されている場合、一度に実行されるスレッドは 1 つだけです。つまり、あるスレッドが別のスレッドを待たなければならないときはいつでも、OS がコンテキスト スイッチを実行する必要がありますが、これはかなりコストのかかる操作です。

複数のコアで実行する場合、対話する必要がある 2 つのスレッドが両方とも同時に実行される可能性が十分にあるため、OS はコードを続行するためにコンテキスト スイッチを実行する必要はありません。

つまり、多くのスレッド間同期を必要とするコードは、シングル コアでは実行速度が遅くなります。

しかし、あなたはそれを悪化させることができます。スピンロック、またはあらゆる種類のビジー待機ループは、パフォーマンスを完全に破壊します。そして、その理由は明らかであるべきです。一度に実行できるスレッドは 1 つだけなので、あるイベントを待機するスレッドが必要な場合は、別のスレッドを実行できるように、すぐにスレッドをスリープ状態にするよう OS に指示する必要があります。

代わりに、「条件が満たされていない間、ループし続ける」ビジーループを実行すると、何もする必要がないにもかかわらず、スレッドを実行し続けます。OS が時間切れであると判断し、別のスレッドをスケジュールするまで、* ループし続けます。(また、スレッドが何かによってブロックされない場合、通常、一度に 10 ミリ秒以上実行することが許可されます。)

一般的なマルチスレッド プログラミング、および *特にシングル コアで実行されるマルチスレッド コードでは、適切にプレイする必要があり、CPU コアを必要以上に占有しないようにする必要があります。やるべきことが何もない場合は、別のスレッドの実行を許可してください。

そして、あなたのコードが何をしているのかを推測してください。

これらの線の効果は何だと思いますか?

   if (!(al_get_timer_count(timer) > prevCount))
        continue;

ループを実行してください!走る準備はできていますか? いいえ?その後、ループを再度実行します。今すぐ走る準備はできていますか? まだいいえ?ループをもう一度実行してください.....

言い換えれば、「私は今 CPU を持っています、そして私は決して手放すつもりはありません! 他の誰かが CPU を欲しがっているなら、彼らは私の冷たい死体からそれを奪わなければならないでしょう!」

CPUを使用するものが何もない場合は、特に実行する準備ができている別のスレッドがある場合は、それをあきらめてください。

ミューテックスまたはその他の同期プリミティブを使用するか、よりおおよその時間ベースのスリープ期間で問題ない場合は、 を呼び出しますSleep()

ただし、何らかの適切なパフォーマンスが必要場合は、別のスレッドが何らかの処理を行うのを待っている場合に、CPU を無期限に占有しないでください。

于 2011-09-23T20:48:02.347 に答える
1

プロセッサを見るときは、単に次から次へと計算するだけのブロックのように見てはいけません。あなたが時間を予約しなければならない計算機としてそれを見てください

Windows (およびすべてのオペレーティング システム) では、実行中のすべてのアプリに対してこの時間が確保されます。プログラムを実行すると、コンピューターは新しいプログラムが必要とするすべての計算を行うだけでなく、Windows がプログラムに特定の時間を割り当てます。その時間が終わると、次のプログラムに時間がかかります。Windows がこれをすべて実行してくれるので、理解したい場合にのみ関係があります。

ただし、これはマルチスレッドの見方に影響します。Windows が周囲を見回してマルチスレッド アプリケーションを認識すると、「これを 2 つの別々のプログラムとして処理します」と表示されるため、両方に時間を割り当てるからです。したがって、一方が他方の計算を完全に停止することはありません。

いいえ、プログラムをマルチスレッドで実行してもパフォーマンスが低下することはありませんが、その周りの他のプログラムが少し遅くなります。少量のオーバーヘッドを作成します。しかし、大規模な計算を行っていてプログラムがハングする場合は、気軽にマルチスレッド化してください。

于 2011-09-23T18:54:46.010 に答える