4

したがって、メインスレッドが通常の更新/レンダリングロジックを実行するゲームと、非常に集中的な処理を行う2番目のスレッドがあります。私が抱えている問題は、時々メインスレッドが中断され、ゲームが 60FPS を下回ることです。他のスレッドによってブロックされていることは確かですが、明示的なロックがないため、それを証明する方法がありません。

メイン スレッドがセカンダリ スレッドによってブロックされる理由について、考えられるいくつかのシナリオがあります。

  • 2 番目のスレッドは、多数の小さなオブジェクトを割り当てます。メモリ割り当てにより、一方のスレッドが強制的に待機し、もう一方のスレッドがメモリを割り当てます。1 つの小さなオブジェクトを割り当てた後、メイン スレッドが必要なものの割り当てを続行できると予想されるため、これはありそうもないことです。
  • 時間がかかりすぎる場合にセカンダリ スレッドが中断されるのを防ぐ、何らかの形式の JIT 最適化。これはまったく意味がありません。
  • ロックされているある種のクロススレッド参照。二次スレッドがキューからアイテムを取得するキューによってコードが意図的に分離されている可能性は低いですが、アイテムがキューに配置されるのをロックして防止することはありません。
  • この問題は Linux と Windows の両方で発生するため、OS による不適切なスレッドの優先順位付けも非常にまれです。

ストップウォッチを入れて、コードのどの領域に時間がかかるかを測定しようとしましたが、これは「メインスレッドがランダムに 500 ミリ秒停止した」以上のことはほとんどわかりません。メインスレッドを長時間ブロックしているロックがあるかどうかは実際にはわかりません。

この問題の原因を絞り込むために使用できる手法はありますか?

- - - 編集 - - -

これらは、Mono プロファイラーを実行し、ロックの競合について報告した結果です。

Monitor lock summary
    Lock object 0x7f05190c9fe0: 1 contentions
            0.002126 secs total wait time, 0.002126 max, 0.002126 average
    1 contentions from:
            (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)
            System.Threading.Thread:StartInternal ()
            System.Threading.Timer/Scheduler:SchedulerThread ()
            (wrapper unknown) System.Threading.Monitor:FastMonitorEnterV4 (object,bool&)
            System.Threading.Monitor:Enter (object,bool&)
            System.Threading.Monitor:TryEnter (object,int,bool&)
            (wrapper managed-to-native) System.Threading.Monitor:try_enter_with_atomic_var (object,int,bool&)
    Lock object 0x7f051910b100: 1 contentions
            0.000628 secs total wait time, 0.000628 max, 0.000628 average
    1 contentions from:
            Ninject.Components.ComponentContainer:Get (System.Type)
            Ninject.Components.ComponentContainer:ResolveInstance (System.Type,System.Type)
            Ninject.Components.ComponentContainer:CreateNewInstance (System.Type,System.Type)
            System.Reflection.ConstructorInfo:Invoke (object[])
            System.Reflection.MonoCMethod:Invoke (System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo)
            System.Reflection.MonoCMethod:DoInvoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo)
            System.Reflection.MonoCMethod:InternalInvoke (object,object[])
            (wrapper managed-to-native) System.Reflection.MonoCMethod:InternalInvoke (System.Reflection.MonoCMethod,object,object[],System.Exception&)
            (wrapper runtime-invoke) <Module>:runtime_invoke_void__this___object (object,intptr,intptr,intptr)
            Ninject.Activation.Caching.ActivationCache:.ctor (Ninject.Activation.Caching.ICachePruner)
            Ninject.Activation.Caching.GarbageCollectionCachePruner:Start (Ninject.Activation.Caching.IPruneable)
            (wrapper remoting-invoke-with-check) System.Threading.Timer:.ctor (System.Threading.TimerCallback,object,int,int)
            System.Threading.Timer:.ctor (System.Threading.TimerCallback,object,int,int)
            System.Threading.Timer:Init (System.Threading.TimerCallback,object,long,long)
            System.Threading.Timer:Change (long,long,bool)
            System.Threading.Timer/Scheduler:Change (System.Threading.Timer,long)
            (wrapper unknown) System.Threading.Monitor:FastMonitorEnterV4 (object,bool&)
            System.Threading.Monitor:Enter (object,bool&)
            System.Threading.Monitor:TryEnter (object,int,bool&)
            (wrapper managed-to-native) System.Threading.Monitor:try_enter_with_atomic_var (object,int,bool&)
    Lock object 0x7f05190ca000: 1 contentions
            0.000347 secs total wait time, 0.000347 max, 0.000347 average
    1 contentions from:
            (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)
            System.Threading.Thread:StartInternal ()
            System.Threading.Timer/Scheduler:SchedulerThread ()
            (wrapper remoting-invoke-with-check) System.Threading.EventWaitHandle:Reset ()
            System.Threading.EventWaitHandle:Reset ()
            (wrapper unknown) System.Threading.Monitor:FastMonitorEnterV4 (object,bool&)
            System.Threading.Monitor:Enter (object,bool&)
            System.Threading.Monitor:TryEnter (object,int,bool&)
            (wrapper managed-to-native) System.Threading.Monitor:try_enter_with_atomic_var (object,int,bool&)
    Lock contentions: 3
    Lock acquired: 3
    Lock failures: 0

これは、ゲームを約 20 ~ 30 秒間実行した結果であり、この間に少なくとも 10 回のラグ スパイクが観察されました。その間、ロックの競合は 3 つしかなく、すべての解決に 16 ミリ秒もかかりません。

4

1 に答える 1

4

ガベージ コレクション プラットフォームを使用してリアルタイム アプリケーションを実行しているため、この時点からは予測性があまり期待できません。ガベージ コレクション システムは、スペースが不足すると実行中のすべてのスレッドをロックし、スパニング ツリー検出最適化システムとして 3 世代の「最近」のデータを使用して、「ルート オブジェクトからスパン可能なツリー」をウォークすることにより、ぶら下がっているオブジェクトをクリーンアップします。ただし、いずれにせよ、メイン スレッドに同期が存在するかどうかに関係なく、同期が時々停止されることはありません。

次に、レンダリングがスワップ関数 (直接 3D 用語で「画面に表示」) でブロックされています。これは、「3 番目に古いフレームのレンダリングが終了し、最後のレンダリング コマンド リストがフラッシュされ、VSync 信号が受信されるのを待っている関数です。 "メインスレッドを続行させる前に。スワップ呼び出しのプロファイリングを試して、ドライバーがロックアップに関係しているかどうかを確認できます。

第三に、あなたが言及したように、OSスケジューラはプリエンプティブであり、1〜15ミリ秒の長さのカーネルティックごとに、コンテキストを切り替えることができます。カーネル V 3.1 より新しい (または同等の) Linux を使用している場合は、カーネル ビルド オプション FULL_DYN_TICKS を使用して、システム全体でアクティブなタスクが 1 つだけの場合にプリエンプティブ割り込みタイマーを無効にします。この要件が満たされる可能性は低いです。ただし、500 ミリ秒は非常に長い時間 (33 ティック) を表します。これは、同じ優先度で同時にフル CPU を実行している 33 の他のタスクがある場合にのみ発生する可能性があります。ありそうもない。

温度の理由で CPU を調整するハードウェアの決定、またはその点で GPU を調整する可能性があります。

グラフィック カードのメモリとリークを共有する複合デスクトップを使用すると、ドライバがメイン メモリ内のテクスチャを時々スワップすることになります。この種のバグは、特に emerald や compiz などの「危険な」デスクトップで、Linux で多く発生します。

別の 3D アプリケーションをチェックして動作を確認し、ワーカー スレッドをすべて停止して、メイン スレッドの健全性に役立つかどうかを確認します。小さなオブジェクトの割り当てと、古いオブジェクトの割り当てを確認してください。第 1 世代のガベージ コレクションは、実行に負荷がかかる場合があります。

幸運を

于 2013-09-27T00:43:13.713 に答える