none
directx9 でのマルチスレッド処理について。 RRS feed

  • 質問

  • 質問するのはじめtなのでよろしくお願いします。

    ただいまdirectx9 3dでゲーム開発しています。

    windows7 64bit

    visual studio2010professional

    です。

    火力の弱いノートパソコンでも動くようにしたいのです。

    対象は モバイル corei3 です。

    今開発中のゲームでは、CPU使用率が20~30ぐらいです。

    corei3の4スレッドのうち 主に1スレッドがフル回転している状態です。しかしまだCPUは使えます。

    理由としては描画スレッドを一本でやっているので当然なのですが、この使用率から見るとGPU が休んでいる時間が多いように思えました。

    そこであることを考えました。

    描画スレッドとは別に頂点処理をソフトウェアーで処理するスレッドを複数走らせ、それをどんどんピクセルシェーダを実行しているスレッドに送り込むというものです。

    Idirect3ddevice9::ProcessVertices関数で頂点を先に処理しようとしています。

    ここで疑問なのですがピクセルシェーダを実行している最中に別のスレッドで頂点シェーダを実行できるのでしょうか?

    デッドロックになったりしますか?

     

    もうひとつ質問があるのですがデバイスはひとつのアプリケーションで複数作れるのでしょうか?

    そしてそれらは独立に動かせるのでしょうか?

    ピクセルシェーダは無理そうだとしても、ソフトウェアーでの頂点処理は同時に動かせるかどうかなどです。

    ご教授お願いします。

    2011年4月21日 15:23

回答

  • ボトルネックがどこにあるかは調査してみましたか?CPU処理を分散すれば、早くなるとは限りません。

    XNAなので、DirextX + C++ でそのまま当てはまるわけではないと思いますが、基本的なアーキテクチャは同じですので、参考になりそうなまとめ回の記事のリンクを張っておきます。

    http://blogs.msdn.com/b/ito/archive/2010/05/25/code-detective-case-06-light-in-the-darkness.aspx

    おそらく今抱えている部分の問題点と類似しているのでは?と思います。

     


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク yoka333333 2011年4月23日 13:05
    2011年4月22日 5:40
  • しかし私の実行環境はRadeon HD5850なのでどんなGPUでもCPU頂点処理を上回る性能が出るのでしょうか?そこが気になるのですが。

    CPU より処理性能が低い GPU の存在の有無は知りません。
    ただ、そんな GPU が存在したとしても、今時の GPU としての役割を果たしていないように思えます。

    実際にGPU頂点処理にすると速くはなるのですが、CPU使用率が+10%弱ぐらいになります。これはデータを取りにいくのに時間がかかっているのでしょうか。

    正直、そんな数値だけ示されても、何をしているのかわからないので、それが何か断言することは第三者には不可能です。
    自分で必要な知識を取り入れて、計測して、ご自身で判断してください。

    ただ、データのやりとりが多いと遅くなるのは必然だと思ってはいます。

    なのであまっているスレッドも9割近く使用したいのです。

    正直、ゲームは軽い方がよいと思っている人からすると、余っているリソースは何が何でも使いたいとする思想は共感できません。
    そんなにリソースを使わないと実現できないゲームなのですか?本当に軽減できる処理はないのですか?

    ゲームを快適に動かすのであれば、処理を軽くすること(無駄な処理を書かない、より適切な処理方法を選択する、環境によってクオリティを調整するなど)を重視すべきです。
    無駄な処理があったのですから、まだまだチューニングの余地はあるのだと推測します。

    どうすればよいかといった、具体的なアドバイスはできかねます。
    その手の書物、サイトを読んでいって頂いた方がご自身にあった最適化につながると思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。


    • 回答としてマーク yoka333333 2011年4月23日 12:28
    2011年4月23日 12:05
    モデレータ

すべての返信

  • ボトルネックがどこにあるかは調査してみましたか?CPU処理を分散すれば、早くなるとは限りません。

    XNAなので、DirextX + C++ でそのまま当てはまるわけではないと思いますが、基本的なアーキテクチャは同じですので、参考になりそうなまとめ回の記事のリンクを張っておきます。

    http://blogs.msdn.com/b/ito/archive/2010/05/25/code-detective-case-06-light-in-the-darkness.aspx

    おそらく今抱えている部分の問題点と類似しているのでは?と思います。

     


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク yoka333333 2011年4月23日 13:05
    2011年4月22日 5:40
  • ボトルネックを教えてもらったサイトと自分なりに探してみました。参考になりました。ありがとうございます。

    ウインドウ解像度を変更してCPU、GPU使用率を調べる。GPUのほうはMSI AfterBurnerを使いました。

    今私の環境はデスクトップPCでCore i7、Radeon HD5850を使っているので、処理落ちさせるのが大変なので、Core i7のCPUコアを1コアだけにして実験しました。

    ボトルネック : CPU55% 、 GPU 10%で処理落ちしました。明らかにGPUが待機している。と思いました。

    CPU使用率はどの解像度でも一定です。GPUのほうは解像度に比例しました。

    やはりCPUがGPUの足をひっぱている気がします。

    ほかにはBGMの再生でマルチバッファを使っていますので、その読み込みが描画と同じスレッドで行われている可能性がありました。HDDのランダムアクセスの遅さを考えるとこれの影響が大きいかも知れません。

     

    キャッシュメモリについてですがこれが難しくてよくわかりません。同じ処理をまとめたりしているのですがほかにもできることがあるのでしょうか。

    Idirect3ddevice9::ProcessVerticesの処理のされ方について知りたいのですが、もしこの関数がピクセルシェーダと排他的に実行されるのなら、別スレッドで実行する意味がありませんし、仮に実行できても呼び出しスレッドで実行してくれないといみがありません。どのように実行されるのでしょうか?

    並列化の方法としては

    1フレーム前にCPUによる更新処理をすべて終わらせ、次のフレームでGPUにひたすら描画させたいのです。

    2011年4月22日 15:01
  • 描画処理の改善の話にばかり目が行っているように見受けられましたが、本当にそこを改善すれば低スペック PC でも動くのでしょうか。
    描画処理は余裕な部分が多くて GPU 使用率が低く抑えられており、ほかの CPU に依存した処理が重いとか、そういった可能性は否定されていると思ってよいのでしょうか。
    今の調査結果を拝見させて頂く限り、論理的にそれを証明できていないので指摘させて頂きました。

    あと、1 コアにしたら処理落ちしたとのことですが、その時点で低スペック PC での処理落ちは避けられないのではないでしょうか?
    低スペック PC は必ずしもデュアルコアとは限りませんし、処理性能も低いでしょう。
    また、GPU に比べ、CPU での頂点処理などは非常に低速だと思われるので、改善効果どころか、悪化するように予想されます。

    # Direct3D 関連の最適化の話に関しては、アドバイスできないかもしれませんが…。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年4月22日 22:37
    モデレータ
  • ここでいう処理落ちとはどういうものを指しているのでしょうか?フレーム前に。。。とあるから、いわゆる駒落ちでしょうか?

    記事からのリンクでCPUバウンドとGPUバウンドを。。。というところで、GPUが遊んでいるからといって必ずしもCPUの処理が追いついていないとは限らないということが出ていたはずですが読んでみましたか?

     

    最初にリンクした記事は、かなり広範囲に考慮すべき点が出ています。XNA/D3D の利用に限らずボトルネックを調査するという点では参考にできる記事です。それ故、ボトルネックが絞り込めていない今の状況には一番いいだろう。。。と思って紹介したものです。

    GPUが待機しているということらしいですが、その待機状態がどういう待機状態かは調べていますか?紹介した記事でも複数パターンがあるということが書いてあったと思いますし、その調査の仕方も出ていた気がします。

    また、BGMの処理が邪魔くさい。。。ということのようですが、これを外して(コードレベルでコメントアウトするなどプログラム的に存在しない状態にする)計測してみましたか?

    キャッシュメモリが難しくて。。。とあります。が、ここでいうキャッシュはどのキャッシュを指していますか?単純にキャッシュメモリといっても、パソコン(ゲームに影響する何か)はいくつもあります。画面データキャッシュ(Surface関連)、CPUのn次キャッシュ、HDDのリード・ライトキャッシュ、プログラムが独自に用意しているキャッシュなどなど、これらはすべてどこかにあるメモリに存在するキャッシュと呼ばれるものです。そのそれぞれに有効活用方法が異なります(特性も異なる)。

    IDirect3DDevice9::ProcessVertices については、D3Dの知識があるわけじゃないので、正直わかりません。こちらはほかの人のフォローを期待したいところです。

     


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    2011年4月23日 1:11
  • 指摘ありがとうございます。

    もう一度プログラムを見直したところ、得点表示、などで無駄にループ処理をしていまして、そこを削除すると軽くなりました。

    ランド関数、三角関数を多用しているところもありそこも重かったようです。改善に値しました。

    この処理を消してみたところCPU 使用率は半減しました。  70%ー>30%

    これを計算を単純にするか、あるは前もって計算しておくか、

    できれば別のスレッドで処理したいのです。

     

    CPU依存の処理は重いです。なので更新処理用のスレッドを作りたいのでした。

     

    今までCPUによる頂点処理をさせていましたが、GPUに変えてみたところ4倍近い速度が出ました。確かにCPU頂点処理は遅かったです。指摘ありがとうございます。

    しかし私の実行環境はRadeon HD5850なのでどんなGPUでもCPU頂点処理を上回る性能が出るのでしょうか?そこが気になるのですが。

    GPU 頂点処理にすると、GPU側にデータが残ると思われるので頂点データの書き換えをするときにやはり遅くなりますでしょうか?

    実際にGPU頂点処理にすると速くはなるのですが、CPU使用率が+10%弱ぐらいになります。これはデータを取りにいくのに時間がかかっているのでしょうか。

     

    対象PCですが、最低デュアルコアを対象としています。低スペックといっても、core i3を搭載した4スレッド処理可能なノートPCぐらいを想定しています。

    なのであまっているスレッドも9割近く使用したいのです。

    2011年4月23日 11:34
  • しかし私の実行環境はRadeon HD5850なのでどんなGPUでもCPU頂点処理を上回る性能が出るのでしょうか?そこが気になるのですが。

    CPU より処理性能が低い GPU の存在の有無は知りません。
    ただ、そんな GPU が存在したとしても、今時の GPU としての役割を果たしていないように思えます。

    実際にGPU頂点処理にすると速くはなるのですが、CPU使用率が+10%弱ぐらいになります。これはデータを取りにいくのに時間がかかっているのでしょうか。

    正直、そんな数値だけ示されても、何をしているのかわからないので、それが何か断言することは第三者には不可能です。
    自分で必要な知識を取り入れて、計測して、ご自身で判断してください。

    ただ、データのやりとりが多いと遅くなるのは必然だと思ってはいます。

    なのであまっているスレッドも9割近く使用したいのです。

    正直、ゲームは軽い方がよいと思っている人からすると、余っているリソースは何が何でも使いたいとする思想は共感できません。
    そんなにリソースを使わないと実現できないゲームなのですか?本当に軽減できる処理はないのですか?

    ゲームを快適に動かすのであれば、処理を軽くすること(無駄な処理を書かない、より適切な処理方法を選択する、環境によってクオリティを調整するなど)を重視すべきです。
    無駄な処理があったのですから、まだまだチューニングの余地はあるのだと推測します。

    どうすればよいかといった、具体的なアドバイスはできかねます。
    その手の書物、サイトを読んでいって頂いた方がご自身にあった最適化につながると思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。


    • 回答としてマーク yoka333333 2011年4月23日 12:28
    2011年4月23日 12:05
    モデレータ
  • すいません。

    こま落ちのことです。

    キャッシュはCPUのn次キャッシュのことです。

    CPUでの頂点処理とGPUピクセル処理を分けた場合、一度メインメモリにデータを戻す確立があがってしまい、逆に性能が悪くなってしまうことを心配しました。

    しかし、可能なら、頂点処理もGPUでやらせることにしましたので、今は気にするのをやめました。

     

    ボトルネックの絞込みですが、記事に書いてあることをやってみました。解像度を極端に落とすやり方です。当然ピクセル処理は軽くなるのでそれでこま落ちするならCPUバウンドということになります。

    結果、こま落ちしました。

    やはりCPUバウンドの可能性が高いです。更新処理に時間食っていいます。

     

    C標準ライブラリの関数を多様している箇所も多々ありますので、そこらへんを削ってみたところ、だいぶ軽くなりました。

    解決策としては、そんな動的な計算が必要なところではないので、あらかじめシュミレート計算しておいて、それを呼び出す形にしようと思っています。

     

    それとBGM再生ですが、なくしてもたいした差は見当たりませんでした。しかし、マルチバッファで音楽再生をしようとしていたのでHDDにアクセス要求が多発しているときに処理落ちするのではと思ったのです。

    でもHDDにベンチかけながら再生しても問題ないぐらいでしたからやはり問題なさそうでした。ひとつ取り越し苦労が減った気がします。

     

    以上が判明しました。

    例の記事はとても参考になりました。ありがとうございます。

     

     

    2011年4月23日 13:03
  • CPUのn次キャッシュについては、ゲームの世界ではあまり考慮する必要はないと思います。まぁあるなら使う。。。というものでもないので。

    頂点処理をGPUにしたら。。。とあったので、GPUの性能を引き出せていないのが速度が上がらない最大の要因と思います。

     

    いろいろあるとは思いますが、D3D9を使っているのなら、まずはそのハードウェア要件を調べてみてください。

    何はできなければならないか?何はできなくてもいいか?ということが明確に決まっています。

     

    ・数学関数について

    浮動小数点演算は遅いです。x86系のCPUは今のところすべての世代にわたって共通しています。なので、浮動小数点演算を行わないようにするという最適化手法があります。

    また、最近ではGPUに処理させる(GPGPUなど)という最適化方法も着目されています。D3D9世代でもいくつかの処理はGPUでできると思いますので、調べてみる価値はあると思いますよ。


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    2011年4月23日 17:35
  • こんにちは。 あれからいろいろと教えていただいたことを参考にパフォーマンス改善を続けていました。

    大変改善したと思っています。全体で3倍近い改善でした。

     

    こちらの話でタイムルーラーなるものがあると教えてもらったサイトにはあったのですが、PC用のdirectXでは見当たらなく困っていました。そのほかの方法を使えば調べることはできないわけではなく、それで改善できた点もあったのですが、やはりタイムルーラがほしかったです。

    そこでふと思ったのがスレッドをもうひとつ走らせ、そこでひたすら足し算を繰り返し、その値を時計として使って記録すればどうだろうということでやってみました。

    Corei7 は4コアなので二つぐらいはいつもスリープ状態です。なのでそのコアに足し算を繰り返させればゲームの動作に影響はでずに時間を高精度に測定できると思ったのです。

     

    見事に機能しました。

    マルチコアが役に立つ瞬間です。もちろんHyperThreading、TurboBoost機能はオフにしています。(目玉機能なんですが・・・)

    ほかにQueryPerformanzeCounterというAPIもあったようですが、どうも分解能は高いようですが、精度がよくないようで。

    なのでこの方法が今の自分の開発環境にとって一番手っ取り早かったのです。

     

    数学関数を多用しているとこはやはり軒並み遅かったです。powfなんかは死語でしょう。おそらく内部でテイラー展開をしていると思うのですが聞くからに遅そうです。Sinfとかもです。

    それと倍制度浮動少数を使うのをやめました。32BITアプリケーションで64Bit値はよくなさそうです。しかしあまり変わりませんでしたが・・・

     

    しかし、もっと重症なのが短い頂点数で繰り返しレンダリングしている箇所でした。GPUに命令を送ってから、レンダリングが始まるまでに時間がかかるためだと思いました。

    なので4個ずつ頂点処理をしていたところを、300個ぐらいにまとめることができた場所は10倍近い速度を記録しました。

     

    頂点はたとえトライアングルリストを使ったとしてもまとめたほうがよさそうです。計算してみると色と座標だけなら数KBです。

     

    ほかにもありました。

    よくプログラミングではローカル変数の利用を強く勧められます。確かにコードは見やすくなります。

    しかし、自作タイムルーラーによれば多用する関数のローカル変数はOSにとってとても迷惑だったようです。1KBを超えたあたりから、スタックメモリを確保するのが以上に遅くなることが判明しました。特に巨大な配列はすさまじく時間がかかります。

     

    私のGPUはRadeonHD5850なのですが、このGPUは連続した頂点なら10000個ぐらいはびくともしません。(描画範囲にもよりますが・・・)

    大体300個ならCore i7 870 (2.93GHz)が500*2回の命令(if+インクリメントのサイクル)を実行する間に終わります。コマンドバッファの発行も含みます。

     

    これに比べメモリを1キロバイト確保するのに、同じくCore i7が140000回例の命令を実行します。

    目が飛び出ます。

     

    ローカル変数を使い、グローバル変数を使うな。

    という教えはパフォーマンスから言えば明らかに間違えだと思います。メモリはやはりいつの時代もボトルネックなのです。最近究極のメモリ(Ultimate Ram で URAM)が開発されたとか言う話を聞きましたが本当に早く改善してほしいものです。

    解決法としてはメモリはグローバル変数、(クラスのメンバ変数)として確保し、内容は保障しないとして使えば解決しました。

    マルチスレッドじゃないなら、今の時代メモリを1MB( 実際は10KBもあればいい )ぐらい占有しても微々たる物だと思うのでローカル変数の時代はもう化石なんだなと思いました。

     

    後はHDDです。マルチバッファにして音楽を再生してもHDDへのアクセスで異常に時間がかかるので今時のメモリ事情からするとマルチバッファの恩恵は薄いように感じました。

    50MBぐらい一気に読み込んでもよさそうな気がします。シングルコアのCPUならHDDアクセスに別のスレッドを立てても逃げ場がないのでなおさらだと思いました。マルチバッファはやめました。

     

    GPGPUとのことでしたが、nVidiaはとても早いようですがAMDはぜんぜん遅いそうで、この速度の差は10倍近いようでした。確かに使ってみたいんですがこれでは互換性に問題があるので数年はやめときます。

     

    これから見る人の参考にと、助言をしてくださった二方への報告として書きました。

    ありがとうございました。 だいぶ成長して初心者を抜け出せた気がします。

    2011年5月3日 13:50