-
Notifications
You must be signed in to change notification settings - Fork 168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
背景塗りつぶしにPatBltを使う Part1 #765
背景塗りつぶしにPatBltを使う Part1 #765
Conversation
従来ExtTextOutを使っていた塗りつぶしにPatBltを使うように変更します。 描画速度は FillRect > ExtTextOut > PatBlt と言われています。
速度を目的とした変更なら、アプリケーションに適用した場合のベンチマークが知りたいです。フィボナッチ数を数えるようなベンチマークではなくて。 |
実動作に合わせたベンチマークは用意してません。 このPRで行いたかったことは、常識としての「PatBltのが速いらしい」のウラを取ることとPatBltの方式に転換するために障害があるのかを知ることです。 いまPRを出しているのは、ウラは取れた、と思っていて、転換するための障害はほとんどないと判断しているからです。 |
(自分は)脱落しました。 |
了解っす。なんか方向性おかしなことになった場合のツッコミはお願いします。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
問題無いと思います。
マウスカーソルをURLに合わせた際にちゃんと背景が黄色に変化する事を確認しました。
背景塗りつぶしに使うAPIを変更する目的が速度、つまり描画を速くして描画が完了するまでの時間と消費電力を削る事だとしたら気になるのは以下の3点です。
色々と検証しないとはっきりしないと思うので用意してくれた機材をこちらでも確認しようと思います。 |
レビューありがとうございます。 |
目的も根拠も不明確なまま、つまりは「変更のための変更」がマージされたのが残念です。 #765 (comment) で否定されたことにより速度改善が目的ではないため、この PR 本文の「根拠説明」は意味がありません。「目的」に書かれた要旨は「APIを変更します」であり、変更の目的を何ら説明していません。 残念です。 |
自分も、なぜこれで話が通じていないのかが理解できません。これまでのやりとりからそうなることが見えていたから、早々に「脱落しました」。 これは @berryzplus さんの言葉です。
目的が明かされなければ妥当もクソもありません。
目的と、根拠が、この PR の本文からは読み取れないので体裁を整えて欲しいということです。マージが行われる前に、PR を出すときに、それを済ませて欲しかったです。 以下は berryzplus さんの書き込みです。
ここに書かれていることは、サクラエディタとは無関係の理由と内向きの理由であって、他者への働きかけがありません。どこにも「リクエスト」がありません。だから自分は離れました。 |
にリンクが張られていた検証資材をビルドして動かしてみました。
出力値の単位は 自分の環境では、case2 と case 5 が速いようです。実装を見るとどちらも なお使っているグラフィックスカードは、NVIDIA GeForce GTX 1070 を使ったものでドライバのバージョンは 24.21.14.1131 です。WDDM 1.1 で GDI のハードウェアアクセラレーションが規定されてますがそれ使ってるのかどうかは謎です。 デスクトップのDCを取得してそこに描画しているのはちょっと良くないかなと思います。実際にアプリで描画する際も表示用のDCではなくてメモリDCを対象に描画するようにしないと垂直同期の関係でティアリングが起きるので表示のちらつきが気になってしまいます。それにメモリDCの方が処理も高速に終わるのではないかと。 あと |
自分が高速だと思う単色塗りつぶし方法は、32bit の DIB を描画用に確保して、そのDIBのビットマップのメモリを直接 そこそこ最近のコンパイラであればおそらく x86/64 のストリング命令 |
自分も beru さんと同じように PR の「根拠説明」から速度改善が目的だと読み取ったのですが、「実動作に合わせたベンチマークは用意してません。 速度改善効果の確認がしたいわけじゃないからです。」と書かれてしまいました。 噂の裏を取って速度が障害にならないことを確認して、でも目的は速度改善ではないらしいのです。では何をする目的があって使用する API を変更しようとしていて、それによる速度への影響が障害にならないことを確認したのか。PR を出す時点で示されているべき説明がその後も出てこないことにいらだっています。 |
うちの結果は Debug x86 です。500で何回か試して、実行時間の比率がほとんどブレないことを見たあとで結果だけ5000のものを整形しました。 速いほう2つの結果が逆転したのは興味深いです。
考えるのがめんどくさくなってコメントアウトしてしまったのは、お察しの通りです・・・。
FillRectが遅いのは「よくない作り」のツケを支払わされてるんじゃないかと思ってます。
Visual Studio側から File ⇒ Open ⇒ CMake で |
berryzplus さんが作ってくれた検証資材を改造しました。
デスクトップウィンドウに対しての FillRect は1秒間に20回とかなり低いパフォーマンスでした。 メモリDCの方が速度が大分速いので、描画はちゃんと裏画面を作ってやらないとパフォーマンス的にも良くないですね。なおメモリDCが選択するビットマップも描画の度に毎回作るのはパフォーマンスを落とす原因になると思うので事前に作成するのが良いと思います。 「こうすれば性能が上がるのにっ」って思ってはいてもサクラエディタの今の描画のコードを大幅に改造するのは大変なので指をたくさん動かさないと実現は出来ないですね。:sweat_smile: |
メモリDCに変えると速度差が誤差レベルになるんですね。 懸念としてあるのは、NVIDIA GTX(ゲーマー向けハイエンドグラボ)で検証した結果に基づいて、非ゲームアプリのコードに手を入れていいかどうか?ってことです。GTXは数GB単位のグラフィックスメモリを積んでますが、世の中にはグラボなし(≒CPU内臓 Intel HD Graphics のみ)のマシンもあってメインメモリと共用になってる場合があります。これについてはあとでノートPC(グラボなし)の結果を見とこうと思ってます。 サクラエディタも、オプションでメモリDCを使う描画モードを選べます。 @beru さんの検証結果によると、キャッシュなし部分もメモリDCを経由したほうが速い、ということになるんですけど、このあたりを踏まえた改善コードを一度で作るとなると内容の把握が大変めんどくさいことになりそうな予感がしています。 個人的に思っているサクラエディタの「描画が遅い理由」は、これまでも何度か書いているんですけど、「文字色などのスタイルを動的に判断しながら描画している」ということと「再描画を指示するときに InvalidateRect(ぬるぬる) してる(≒再描画範囲を指定してない)」ということを挙げられます。 構築済みのレイアウト情報に基づいて描画する場合、背景を描画して、青い文字を描画して、緑色の文字を描画して、黒い文字を描画して・・・のように色切替を最小限に抑えることができます。現状の処理スタイルだと、色が変わるたびに切替命令を発行するので遅いです。 再描画範囲の指定については、1回で済むはずの描画なのに3回描画しているケースがありそうだ、ということを言ってます。「1回分でも3回分でも速度は変わらない」と考えるのは不自然だと思ってます。もしかしたら「無視できるレベル」なのかも知れませんけど、そういうのが積み重なった結果で「遅い」になってると思っています。 |
グラボなしマシンの測定結果を上げときます。
Intel HD Graphics 520搭載機(グラボなし)です。RAM多めに積んでるので省メモリの参考にはならんです。 |
まだちゃんとコードを見てないんですが、メモリDCに(常に)使っているわけではないんですね。 |
体全体への影響を見ないで、ポリフェノールが体にいい、炭水化物は避けたほうがいい、というような論法はナンセンスです。だから、アプリケーションに適用した結果が知りたいと書きました。 実アプリケーションでプロファイルを取って有意な差が出て初めて最適化は意味があります。他人の手間なのでかけた手間に比して大した効果がなくても構わないといえば構わないわけですが、思惑に反して悪影響が出ていないことの確認は必須です。それができないのならそもそも最適化に手を出してはいけない。 |
(続き) やらなくてもいいことなんだから。 |
これだけ限定条件を並べながら実際の効果を調べずに「たぶん良くなるだろう」で話を進められることが信じられません。 |
自プロセスのウィンドウに対して サクラエディタのアプリ本体に描画ベンチ機能が無いので、実アプリケーションのプロファイルは少し手間ですね。 一番お手軽なのは Visual Studio の Performance Profiler で確認ですが、多分今のソースコードそのままだと厳しい気もします。#766 でやったように間接層の |
メモリDCは裏画面に描いているようなもので描画途中の内容が画面に反映される事が無いのが速い理由かなと思います。メモリDCを使った方が速くてちらつかない事は昔から有名な話でした。 ただグラボ性能との関連となると、なかなか見えないコードで処理されている事なので分からないですね。。結局手持ちの複数の環境で色々なパターンで試験して有意な差を確認出来なければ関連は薄いと判断せざるを得ないとは思います。 GDIのハードウェアアクセラレーションについては WDDM で決まっていて下記に書かれていますが、 |
WDMはフレームキューイングしてるようですが、FillRect の場合はもろにそれと絡んでるって事でしょうかね。具体的なメカニズムの解説はあんまり詳しくないので出来ません…。
NVIDIA GTXシリーズはハイエンド、ローエンド、ミドルレンジ(+拡張された概念のミドルエンド)と色々あるし、Maxwellアーキテクチャ以降はAMDのより出来が良いGPUをコンスタントに出してきているのでそれを検証に使う事自体は問題無いと思います。おっしゃる通り内蔵グラフィックスを使った場合の検証も必要ですが。Intel HD Graphics 等の内蔵グラフィックスで済ませられるならアスク税を大幅に払う必要が無いのでそれが良いんですが、根本的にメモリ帯域が狭いので残念な性能になってますね。 テキストエディタをフルスクリーン表示で使う人はちらほらいるので、DWMの弊害を回避する為にもDirectXのフルスクリーン表示を使うのは有りだと思います。
メモリDCの活用は後付け的な感じですよね。
面倒なんですが描画速度の改善の為には 32bitのDIB を作成して、GDIを使った描画はメモリDCでそれを選択して行う、という対応が必要だと考えてます。
自分あんまりそこらへん追えてないので詳しくは分かりませんが、なんか色々な事情があるんですね。なるべく少ない手間で描画速度を大幅に改善できるに越したことは無いんですが、そうもいかない事は覚悟しないといけなそうですね。 |
@ds14050 さん
1回しか呼ばれない単発処理について実測して改善効果の有無を見極めるのは無理だと思います。 必ずしもそうじゃない、と思っています。 |
初めて教えてもらいましたが、この PR の目的は速度の改善だったのですね。詳細に言うなら、速度の改善のために PatBlt を使う計画があって、そのプロトタイプとして1か所だけ変更するのがこの PR だというわけですね。今からでも本文を修正してもらいたいです。 そしてそれを踏まえて意見を述べると、この PR は WIP を付けてマージを目的とはせず、最小の差分を提示するだけを目的とすべきだったと思います。 横展開したものは当然、効果を見極めることができるはずです。効果を見極める前にプロトタイプだけをマージしては、あとで引き返すのが面倒です。 |
実際そうですよ。できなくはありませんが、意味がありません。コードが汚くなるなら有害ですらあります。 |
自分は以前から何度もしつこく「塗りつぶし操作は 32bit の DIBを作ってそのメモリ領域を直接書き換えてしまうのが一番速いはず」と主張をしていたのですが、言うだけじゃなくてちゃんとプログラム書いて確認した方が良いよな…と思ったので書いてみました。 https://gist.github.com/beru/b8e585d92d91f73dc64f74584116ffeb 結果としては小さい領域なら DIB のメモリを書き換えるやり方は速いですが、領域のサイズがどんどんと大きくなっていくと API を使った方が速い事が自分の環境では確認出来ました。 なおAPIを使う場合はDIBではなくDDBを使っていてそしてハードウェアアクセラレーションも掛かっていると考えられます。まぁ処理の仕組みを考えるとこの結果は納得出来るような気もします。 なおDIBのメモリを書き換える方法もマルチスレッド使えばもっと速く出来るので、ハードウェアアクセラレーションに頼らないやり方も(CPUが十分速い環境なら)捨てたもんじゃないかなとは思います、という負け惜しみを残しておきます。 |
環境によるかもしれないですけど、ExtTextOut より PatBlt の方が速いのかというとそんな事無いような気がしてしまいますね。検証プログラムも試験規模も小さくて、十分な分析が出来ているかというと出来ていないと思うので、あんまり言い切れませんが。 |
検証用のコードを更新しました。 DDBの場合はハードウェアアクセラレーションが効きますが、DIBの場合は当然効かない結果となり自前で塗りつぶした方が速い事が確認出来ました。マイクロソフトがGDIのソフトウェア実装を見直してくれると良いですね。。 さて、この検証プログラムはまだウィンドウを作って表示をしていないので不十分です。ぼちぼち追加してみようかと思いますが…。 単に全面塗りつぶし表示だけを行うなら表示用のDCを使って塗りつぶしをするAPIを呼ぶだけで済みますが、実際のアプリでは色々な表示を行うのでメモリDCを作成し各種イベント時に(ゲームだと一定間隔)色々描画して、 サクラエディタは上記の定石に沿った実装ではないので、余計な再描画等を行ってしまってるのではないかなと推測しています。 |
定石といえば BeginPaint の前に GetUpdateRect を呼んでないことが気になっています。 [定石のフロー] ところどころ主語が違ってるんで注意してください。 要するに「WM_PAINTの冒頭にあるべき処理」がないんじゃないかと言いたい。 これは更新指示が |
@berryzplus さん
描画処理というのは具体的に何の事でしょうか?自分の考えでは ただし裏画面の再描画を減らすには、裏画面も1画面ではなくレイヤーで複数画面持って、本当に更新された箇所だけ再描画するようにするとか、色々な工夫は出来ると思います。コードの書き換えが大量に必要な面倒くさい話ですが。。 あと他に描画を速くするのに必要な事として、 色々と書きましたが、背景描画より文字描画の負荷の方が割合としては大きいと思うので、そこも今後改良していきたいですね。 |
まだ起きてたので一点だけ。
BeginPaint の呼出は GetUpdateRect よりも「かなり」重いとむかし何かで見ました。 |
むかしっていつ頃でしょうか? Windows 95 が登場した頃のCPUが 80386 SX 16MHz とかそれぐらいでしょうか?まんが日本昔ばなしがまだ放送してた頃の話でしょうか。。 |
い・い・な い・い・な~・・・って歌っとる場合かっ!とセルフ突っ込みしつつ。 時期は1999年~2001年のどっかだと思います。 |
…g_patblt 背景塗りつぶしにPatBltを使う Part1
目的
背景塗りつぶしに使うAPIを変更します。
まずは、試しに1箇所だけ変更して、どのような違いになるのかを示します。
根拠説明のところで、変更すべき状況を示すので、横展開は別PRとするつもりです。
経緯
描画速度は FillRect > ExtTextOut > PatBlt と言われています。
サクラエディタでは元々 ExtTextOut を改変した独自関数を使っています。
各関数の概要は以下のとおりです。
ExtTextOut関数は文字列描画が目的の関数ですが、渡した矩形領域を塗りつぶす機能があります。
サクラエディタの塗りつぶし処理のほとんどは、空文字列をExtTextOut関数に渡すことで行われています。
根拠説明
プログラムを変更するには一定の根拠が必要だと思います。
FillRectよりPatBltのが速い?そんなの常識じゃん? で突き進むのはなんか違うと思います。
形式的にでも相応の根拠が必要だと思ったので、探してみました。
とりあえずググってみるとこんなページがヒットします。
テテのつぶやき - ベンチマークテスト(長方形の単色塗りつぶし)
ベンチマークテストの実施条件と結果が載っています。
(GetDCでデスクトップのDCを取得し、5000回ずつ塗りつぶしさせた結果を比較)
せっかく実施条件が書いてあるので 検証資材 を作って似た検証ができるようにしてみました。
サクラエディタの事情で検証内容を少し変えていますが、以下が実行結果です。
「速度考慮」の有無は、該当関数を使うための準備(オブジェクト生成等)を
ループの外で行うか中で行うかの違いです。
速度考慮なしでPatBltが最速、
速度考慮ありでExtTextOutが最速
という結果になりました。
サクラエディタの塗りつぶしは、基本ExtTextOutになっています。
過去の開発者も似たような感じで「ExtTextOut最速じゃん!」ってなったんだと思います。
ただ、ExtTextOutが最速なのは「下準備を行わない場合」です。
背景塗りつぶしのためにExtTextOut関数を使うにあたっての下準備はSetBkColorの呼出です。
他の理由でSetBkColorを呼出済みの状態から塗り潰す場合はExtTextOutでいいんですが、
単純に背景色を指定して塗りつぶしたい場合はPatBltのほうが速いです。
つまり、
SetBkColorとセットで呼び出しているExtTextOutは
変更した方が「(速度効果的に)良い」になります。
このPRの確認方法
バージョン情報ダイアログのURL部分を変更しています。
URL部分のウインドウにカーソルを当てた時に背景色を変更する処理です。
デバッグ版ビルドで確認すると以下のような違いを確認できます。
変更前)URL部分にカーソルを当てると背景色が黄色になり、左上に黒点が打たれる
変更後)URL部分にカーソルを当てると背景色が黄色になり、黒点は打たれない
変更前が黒点を打つ挙動はvista向けのデバッグ仕様だそうです。