1 :
デフォルトの名無しさん :
2001/07/15(日) 18:51 今、半透明ってやつをやってるんだけど。256色の。 どうにも速度が出ないんだよね・・・・ 下に俺のソースの抜粋書くから、 わかる人は教えてくれないかな・・・?
2 :
デフォルトの名無しさん :2001/07/15(日) 18:52
あらかじめ、半透明のカラーテーブル(256*256)を 64KB境界に合わせておいて、 mov EBX, [Table] というふうにEBXに入れておく。 で、下が<半透明書き込み部分> mov BL, [ESI] //転送元をBLに mov BH, [EDI] //転送先のBHに mov AL, [EBX] //書き込む色をALに mov [EDI],AL inc EDI あとは横×縦のループでこれを囲む。
アセンブラスレなら、既にいくつかあっただろう・・
4 :
デフォルトの名無しさん :2001/07/15(日) 19:00
ちなみに、かかるクロック数は、上から 1,1,5,2、1。 mov AL, [EBX] でストールが発生しちゃってるんだよね・・・ かといって、普通にADD使うと遅そうだし。
スマン。新スレ立てることはなかったな。 上のほう見てなかったんで。 もしわかる人いたらsageでいいから頼む・・・
6 :
デフォルトの名無しさん :2001/07/15(日) 19:05
パーシャルレジスタストールかな データを3個くらい並列処理したら?
>>2 これってもろパーシャルレジスタストールじゃない?
P6系ではBLとBHを別々に書き込んで、EBXで合わせて使うと
ストールが発生したはず。
そのまま素直にシフト+加算で計算した方がいいよ。
ダブった。
なるほど・・・・今、調べてきてみた。 下位だけ書き換えちゃいけなかったのね。
>>6 >>7 どうもありがとう。
並列は、ループでECX,EDX使ってるから
ちょっと無理そう。(EDI,ESIは各矩形のポインタ)
ADD+シフトでやってみるよ。
でも、 普通にシフトと足し算でやるにしても 結局下位8にロードして、左8シフト。さらに下位8にロードして ポインタを足すわけでしょう? 結局このストールがおきるんじゃないのかな?
12 :
デフォルトの名無しさん :2001/07/15(日) 19:49
実行結果。 mov EBX, [ESI] mov EAX, [EDI] and EBX, 255 and EAX, 255 shl EAX, 8 add EBX, tbl add EBX, EAX mov AL, [EBX] mov [EDI],AL inc EDI inc ESI inc EDX というふうに修正したら、 9クロックから6クロックまであがったよ。 まだまだ実用的じゃないみたいだけど大きな進歩だね。 本当にありがとう。
13 :
デフォルトの名無しさん :2001/07/15(日) 19:51
スマ。間違い。上のは8クロックのソースだ。 これが6。 mov EBX, [ESI] mov EAX, [EDI] and EBX, 255 and EAX, 255 shl EAX, 8 add EBX, cll add EBX, EAX inc EDI inc ESI inc EDX mov AL, [EBX] mov [EDI-1],AL
>>11 パーシャルレジスタストールはP6のアウトオブオーダー実行コアの
命令スケジューラの制限じゃないかな?
パーシャルレジスタアクセスを行うとP6の命令スケジューラがマヒするんだと思う。
Pentium4ではパーシャルレジスタストールは無いらしい。
>>13 mov EBX, [ESI]
mov EAX, [EDI]
and EBX, 255
and EAX, 255
は
movzx EBX, byte ptr [ESI]
movzx EAX, byte ptr [EDI]
でいいんじゃないかな。
16 :
デフォルトの名無しさん :2001/07/15(日) 20:33
情報ありがとう!
>>14 なるほどね・・・・
てことは、Pentium4では、最初のが最速になるってことか。
本当に高速化を目指すならCPUごとに条件分岐すれば一番なのか・・・
>>15 おっと、盲点・・・・・恥ずかしい所を見せたか(^^;
17 :
デフォルトの名無しさん :2001/07/15(日) 20:49
15の修正をしたところ、なんとか5クロック・・・・ というか、もうこれ以上はできない気がする。 できて4だろうが、それでも実用には向かない。 なぜだろう? 暇な人がいたら、でいいんだが 以下に半透明部分の全ソースを書くので 問題点があったら教えてくれないかな? ずうずうしいとは思うんだが、もしできたら頼みたい・・・ _asm{ mov EDI, dat1 //EDIに転送元のポインタ(左上の点) mov ESI, dat2 //ESIに転送先のポインタ(左上の点) add EDI, Dot1 //EDIを転送開始点までずらす(X+Y*WIDTHを足す) add ESI, Dot2 //ESIを転送開始点までずらす(X+Y*WIDTHを足す) mov EBX, tbl //加算テーブル開始ポインタ // 外側ループ開始 mov ECX, 0 // ループカウンタ(Y) FOR_START_Y: push EDI //EDI退避 push ESI //ESI退避 // 内側ループ開始 mov EDX, 0 // ループカウンタ(X) FOR_START_X: movzx EBX, byte ptr [ESI] movzx EAX, byte ptr [EDI] shl EAX, 8 add EBX, tbl add EBX, EAX inc EDI inc ESI inc EDX //Xカウンタのインクリメント mov AL, [EBX] mov [EDI-1],AL cmp EDX, Width //横幅 jl FOR_START_X // 内側ループ終了 pop ESI //ESI復旧 pop EDI //EDI復旧 add EDI, W1 add ESI, W2 inc ECX //Yカウンタのインクリメント cmp ECX, Height //縦幅 jl FOR_START_Y // 外側ループ終了 }
18 :
デフォルトの名無しさん :2001/07/15(日) 20:57
cll が定数ならば。 xor ebx, ebx mov bl, byte ptr [ESI] xor eax, eax mov ah, byte ptr [EDI] inc EDI lea ebx, [cll + eax + ebx] inc ESI mov AL, [EBX] inc EDX mov [EDI-1],AL で 5。
とりあえず、ループ展開しとけ。 byte 単位でメモリアクセスしているのを dword 単位のアクセスにできるだろう。
20 :
デフォルトの名無しさん :2001/07/15(日) 21:15
ばっかみたい! DirectXならハードウェアで半透明やってくれるじゃん。
21 :
デフォルトの名無しさん :2001/07/15(日) 21:16
クロックが8とか6とか意っているけれど、 何で計測しているのですか?
22 :
>16 :2001/07/15(日) 21:30
CPU 毎にインストールするバイナリを変える、だろ。
push EDI //EDI退避 push ESI //ESI退避 pop ESI //ESI復旧 pop EDI //EDI復旧 上のペアになる命令は要らないんじゃん? W1とW2からあらかじめWidthを引いておけば。 mov EDX, 0 // ループカウンタ(X) cmp EDX, Width //横幅 これとかも mov EDX, Width test EDX, EDX としたほうが大抵速い。 せめて xor EDX, EDX // ループカウンタ(X) cmp EDX, Width //横幅 としよう。 x86で、レジスタを0クリアするのに0を代入するか、普通? mov [EDI-1],AL これもあんまり良くないかも。 キャッシュを管理しているOSだと、 ライトキャッシュ効かなくなって遅くなることあるよ。
↑追加修正 当然気づいたとは思うけど、 mov EDX, 0 // ループカウンタ(X) inc EDX //Xカウンタのインクリメント cmp EDX, Width //横幅 を mov EDX, Width // ループカウンタ(X) dec EDX //Xカウンタのインクリメント ^^^ test EDX, EDX としてね。 ついでに言えば、testいらねーや。 mov AL, [EBX] mov [EDI],AL dec EDX //Xカウンタのインクリメント jnz FOR_START_X として。 つーかIAマニュアル読め。
26 :
デフォルトの名無しさん :2001/07/15(日) 21:42
情報その他ありがとう!
>>18 なるほど。勉強になる・・・・
まともにうごかないのはcllがポインタだからかな?
>>19 ループの展開?
4ドット同時に処理せよということかな?
>>20 DIRECT3Dならね・・・
>>21 ネットで見つけた、誰かのプログラムなんだけど
どうも不安定っぽい。
>>22 そうだね(^^;
27 :
デフォルトの名無しさん :2001/07/15(日) 22:25
>>24 >>25 有力な情報ありがとう。
やはり自分はまだまだ未熟だな・・・
やっぱり問題はループにあったのか。
cp+jlって意外と遅かったのね。
とりあえず、現時点のことを全部やったらFPSが52まで出るようになったよ。
みんなありがとう。
28 :
名無しだよもん :2001/07/15(日) 23:21
testでジャンプするよりも JECXZを使えばもっとはやいんじゃないの?
29 :
名無しだよもん :2001/07/15(日) 23:21
スマン。JECXZじゃなくて、LOOPな
30 :
デフォルトの名無しさん :2001/07/15(日) 23:38
ってことは。 今のところ、これが最高速? もうこれ以上はあがらないのかな? mov EDI, dat1 mov ESI, dat2 add EDI, Dot1 add ESI, Dot2 mov EBX, tbl // 外側ループ mov ECX, Height // ループカウンタ(y) FOR_START_Y: // 内側ループ mov EDX, Width // ループカウンタ(x) FOR_START_X: //**処理** movzx EBX, byte ptr [ESI] movzx EAX, byte ptr [EDI] shl EAX, 8 add EBX, tbl add EBX, EAX inc ESI mov AL, [EBX] mov [EDI],AL inc EDI dec EDX // Xカウンタのデクリメント jnz FOR_START_X // 内側ループ終了 add EDI, W1 add ESI, W2 dec ECX // Yカウンタのデクリメント test ECX,ECX jnz FOR_START_Y // 外側ループ終了
32 :
デフォルトの名無しさん :2001/07/15(日) 23:57
ってことは、LOOPはあるだけ無意味ってことなのか(w
今田にアセンブラなんてやってる奴いるんだな。
何人かがアセンブラで最適化した分の利益を,我々は使わせていただいているんだ。 ってことに気付け。
35 :
デフォルトの名無しさん :2001/07/16(月) 01:23
>>30 てか、これでもう最適なのか?
順番変えれば、ペアリングとかできそうじゃない?
くそぉ
ネタにネタレスしてやるっ!!!!
>>33 Perlででもアルファブレンディングのルーチン書いて
死んでやがれヴォケ
37 :
デフォルトの名無しさん :2001/07/16(月) 01:37
>>33 まあ普通は無駄だけど、MMXとかSIMD使う分には価値があるよ。
>>36 ハァ?何で俺がPerlみたいな糞言語やらなきゃならねーんだよ。
Ruby以外の言語は全部糞だっていつも言ってるだろヴォケ。
39 :
デフォルトの名無しさん :2001/07/16(月) 02:20
有意義なスレだと思ったんだが、結局はこうか。
41 :
36 :2001/07/16(月) 02:32
>>38 悪かった。わざと言ったんだ。
じゃあ言い直すぜ!
Rubyででもアルファブレンディングのルーチン書いて
死んでやがれヴォケ
42 :
デフォルトの名無しさん :2001/07/16(月) 02:37
フリーでソース付きの8086〜Pentium向けのアセンブラってありませんか? あんまり大きくない奴で。
43 :
デフォルトの名無しさん :2001/07/16(月) 02:38
あんまり大きくない奴で。 <サイズが。
44 :
#6411 :2001/07/16(月) 02:45
素朴なギモンだが、ターゲットがDirectDrawで、 出力先はフレームバッファだったりしないかな? だとすると、抜本的な高速化は別の方法があると思われ。 ここ2年DirectX触れてないから、用語忘れた。…鬱だ…
45 :
1 :2001/07/16(月) 03:08
>>4 本当?
今回は、LOCKを避けるために配列変数から配列変数への転送だけど
それについてはちょっと興味あるな。
なんせ、LOCK使うと遅くなるからサーフェスはほとんどつかわないつもりだったんで。
46 :
36 :2001/07/16(月) 03:21
48 :
デフォルトの名無しさん :2001/07/16(月) 07:30
>>26 >
>>21 >ネットで見つけた、誰かのプログラムなんだけど
>どうも不安定っぽい。
計測プログラムが気になる。URLキボン。
50 :
デフォルトの名無しさん :2001/07/16(月) 07:50
>>2 の64KByte境界にあわせるってあるけど、こんなことってできるの?
51 :
1 :2001/07/16(月) 08:30
>>47 >>49 レジスタももう埋まってて使えないし
どうやって並行処理するの?
MMX使うとかしかないのかな?
>>50 unsigned char* test;
unsigned char dummy[256*256*2];
test = (unsigned char *) (((unsigned)dummy + 65535) & (-65536))
っていう方法で。もしかしてちと強引なのかな?
52 :
1 :2001/07/16(月) 08:32
何クロックとかって懐かしいね。でも、クロック計算できるのはPentiumまでよ。
P6以降は、μOP命令に変換されて、空いているスロットを使ってOOO実行とか
しちゃうから、クロックの計算は出来ないでしょう。
#V-Tuneでも、クロックは出ないはず。
>>28 Loopは、今や遅い命令です。
dec ecx
jnz @@@
の方が速いです(ただしP6の話。AMDでは使った方が速い)
また、P6はフラグストールとかって面倒なもんがあるんで、
cmpして判定しないと逆に遅くなったりします。
>>30 あとは、Loop Unrollして、Incとかはなるべく[esi + 01]とかに置き換えた方が良いよ。
56 :
1 :2001/07/16(月) 13:23
>>54 なるほどね。だからクロック数が不安定だったのね。
数値が実行ごとにずれたりしてたから。
Loop Unrollって?
あと、Incなしで [esi + xx]のほうが速いんだ。
上にそっちだと遅いって人がいたけど、マシンによるのかな?
>>53 >>55 情報サンクス。
今から印刷してじっくりと見てみるよ。
inc esiよりlea esi,[esi+1]をペアリングの空きに突っ込む方が吉 leaはフラグが変わらんから分岐の直前でもいいぞ
>>57 そうだね。
それより
>>30 で気になるのが、movzxって遅くなかったっけ?
xor eax, eax
mov eax, byte ptr[edi]
としてペアリングした方が大抵速くなかったっけ?
59 :
デフォルトの名無しさん :2001/07/16(月) 16:02
・lea 命令の使用 多くの場合、lea 命令、またはlea、add、sub、およびshift 命令のシーケンスを定数乗算命令 に代えて使用することができる。Pentium II およびPentium III プロセッサ用に設計されたコードを 最適化するには、整数乗算命令を使用する。lea 命令は、3 つまたは4 つのオペランドを取る加算 命令として使用できることがある。次に例を示す。 lea ecx, [eax+ebx+4+a] このようにlea 命令を使用することで、複数の算術命令のオペランドでレジスタが占有されること を避け、レジスタが不要に使われるのを防ぐことができる。 Pentium II およびPentium III プロセッサでは、lea 命令とshift 命令は、どちらも1 サイクルで実 行される単一µop 命令である。しかし、このレイテンシの短さは、将来の使用上でも保たれるかど うかはわからない。 最適なブレンデッド・コードを得るためには、shift 命令を2 つ以上のadd 命令に置き換えると よい。これは、この命令のレイテンシがすべての実装面で短いとは限らないからである。 ・複雑な命令 一般に4 つよりも多くのµop を持ち、デコードに複数のサイクルが必要な複雑な命令( たとえば enter、leave、loop) の使用を避ける。代わりに、単純な命令のシーケンスを使用すること。 ・8/16 ビットのオペランド Pentium II およびPentium III プロセッサでは、同じレジスタの排他的論理和をとり、レジスタの リアがそのレジスタの以前の値に依存しないことを認識する機能を備えている。さらに、上記 コード・シーケンスでのパーシャル・ストールを回避する機能も備えている。詳細については 「パーシャル・レジスタ・ストール」の節を参照のこと。 Pentium II およびPentium III プロセッサでは、パーシャル・ストールの発生を抑えるために、movzx 命令のパフォーマンス向上が図られている。これらのプロセッサ向けのコーディングでは、movzx 命令を使用するとよい。
今はP6系の最適化をしてるんでしょ? なんでP5系のペアリングの話が出てくるんだ? 今時P5最適化なんてナンセンスじゃない? Loop Unrollして、Incを使わず [esi + 1]で処理するというのは 依存性チェーンを断ち切って、P6のOOO実行性能を高めるっていう話でしょ?
>今はP6系の最適化をしてるんでしょ? あ、そうだったのか > なんでP5系のペアリングの話が出てくるんだ? >今時P5最適化なんてナンセンスじゃない? たしかにな(笑)
現在のメインストリームは、P6なので、ターゲットを P6にした方が良いと思うよ。 movzx, movsxは、P6では1μOP。がんがん使いましょう。 あと、最適化マニュアル読めばわかるけど、命令のデコーダが 1Clockで3つの命令をデコードできるんだけど、最初のデコーダのみが 4μOPまでの命令をデコードできて、残りの2つのデコーダは1μOPまで (要するに超単純な命令)しかでコードできないから、なるべく μOP命令数で、4-1-1になるように命令を配置した方がいいよ。 なるべくRSを一杯にしといた方が有利だからね > P6
65 :
デフォルトの名無しさん :2001/07/16(月) 19:42
P6の整数演算ユニットは2個だぞ シフトのレイテンシは大きいぞ 油断大敵!
P6のシフトのレイテンシが大きい?また嘘ばっかり。
67 :
デフォルトの名無しさん :2001/07/17(火) 00:41
Loop Unrollって何?
pdfをテキストかhtmlに変換するツール知らない?
70 :
1 :2001/07/17(火) 03:33
おお、どんどん有力な情報が。
ようやくアンロールが理解できたよ。
いくつくらいに展開すりゃいいんだろ。
8?
>>64 あー、だから3つの命令でも例のプログラムで1クロックって出たり
したのか・・・・・納得。バグだと思ってたよ。
4−1−1ね。やってみる。ペアリングとかはもう気にする時代じゃないんだね。
>>P6系所有者 2のコードだけど、マニュアルを読む限り、 xor EBX,EBX mov EDX, [Table] : : mov BL, [ESI] mov BH, [EDI] mov AL, [EDX+EBX] mov [EDI],AL inc EDI にすれば、パーシャルレジスタストールしないで、 ループ出来そうなんだけど、どうなの?
>>71 mov BH, [EDI]
の行がなければストールしない。
この行があるからストールする。
こういうのはだめかい? mov EAX, [EDI] movzx EBX, byte ptr [ESI] add EBX, tbl XLATB ror EAX,8 movzx EBX, byte ptr [ESI+1] add EBX, tbl XLATB ror EAX,8 movzx EBX, byte ptr [ESI+2] add EBX, tbl XLATB ror EAX,8 movzx EBX, byte ptr [ESI+3] add EBX, tbl XLATB ror EAX,8 LEA ESI,[ESI+4] LEA EDI,[EDI+4] mov [EDI],EAX
もう一息!
ありゃ、EBXを8bitシフトするの忘れてる それから ECX,,EDXをループカウンタに使うのは勿体ないよ ECXは無条件にスタック上で処理すればいいし EDXも内側の処理が短くできるならスタック上に取った方がいいよ XLATBは パーシャルレジスタストールの対象になると思うから この2つのレジスタを参戦させて mov EAX, [EDI] mov EBX, [ESI] ror EBX,8 MOV EDX,EAX SHLD EDX,EBX,8 AND EDX,0xffff ADD EDX,tbl movzx ECX, byte ptr [EDX] SHRD EAX,ECX,8 ror EBX,8 MOV EDX,EAX SHLD EDX,EBX,8 AND EDX,0xffff ADD EDX,tbl ・・・繰り返し・・
77 :
76 :2001/07/17(火) 08:51
それから、tblが固定アドレスなんだったら tbl[EDX] とか [EDX+tbl]と書いた方が 早くなる可能性がありそうな気がする 固定じゃないんだったらEBPを使ったらどう?
78 :
76 :2001/07/17(火) 08:54
さらに8bitで3:3:2とかのパレット固定だったら その場で計算させた方が速い場合もあるよ
push Width FOR_START_X: movzx EDX, byte ptr [ESI] movzx EBX, byte ptr [ESI+1] movzx ECX, byte ptr [EDI] movzx EAX, byte ptr [EDI+1] shl EAX, 8 shl ECX, 8 add EBX, tbl add EDX, tbl LEA ESI,[ESI+2] add EBX, EAX add EDX, ECX mov AL, [EBX] mov AH, [EDX] mov [EDI ],AL mov [EDI+1],AH dec [ESP]; //スタックをカウンタに LEA EDI,[EDI+2] jnz FOR_START_X pop EAX;//読み捨て
76の勝ち!
81 :
64 :2001/07/17(火) 10:35
せっかく書いたのに、改行が多すぎますって言われた。
82 :
デフォルトの名無しさん :2001/07/17(火) 10:51
複数にわければOKじゃない?
83 :
1 :2001/07/17(火) 11:10
みんなすげえなあ・・・・
最初、上のソースが何やってるかさっぱりだったよ。
とりあえず1行ずつ追っていって、なんとかどういう意味なのかわかったよ。
>>74 4つ平行か。
EAXをいちいち読むよりこっちのほうが早いんだ?
>>76 これはどうやってループにするの?
79のソースみたいにスタックを使えばいいのかな?
ためしにやってみたけど、なんか遅かった・・・おかしいな?
>>79 スタックを使ったカウンタのやり方サンクス〜
85 :
79 :2001/07/17(火) 11:21
>>74 は XLATB 直後に ror EAX,8 がどうもパーシャルレジスタストール
する可能性有り
>>76 のコードは 4つ一度にロードしてるけど、
EDXを連続して操作してるから思ったより
早くないかもしれないね。
あ、でも
>>79 は push Width/2 に変えてね
86 :
64 :2001/07/17(火) 12:00
分割行きます。 mov ebx, table mov esi, src_pixel mov edi, dst_pixel lopY: mov ecx, nWidth shr ecx, 2 // 4回Unroll mov nDivided_Counter, ecx // Unroll用のループ回数 cmp ecx, 0 // P6ではフラグストールするのでcmpする jz short modulo // 余った分の処理 lopX: movzx eax, byte ptr [esi + 00] // 1 : src0 load movzx edx, byte ptr [edi + 00] // 1 : dst0 load shl eax, 8 // 1 : scaling(*256) lea edx, [ebx + edx] // 1 : table + index mov ecx, byte ptr [esi + 01] // 1 : src1 load mov al, byte ptr [edx + eax] // 1 : table load mov byte ptr [edi + 00], al // 2 : store0 shl ecx, 8 // 1 : scaling movzx eax, byte ptr [edi + 01] // 1 : dst1 load lea edx, [ebx + ecx] // 1 : table + scaler mov ecx, byte ptr [esi + 02] // 1 : src2 load mov al, byte ptr [edx + eax] // 1 : table load mov byte ptr [edi + 01], al // 2 : store1 shl ecx, 8 // 1 : scaling movzx eax, byte ptr [edi + 02] // 1 : dst2 load lea edx, [ebx + ecx] // 1 : table + scaler mov ecx, byte ptr [esi + 03] // 1 : src3 load mov al, byte ptr [edx + eax] // 1 : table load mov byte ptr [edi + 02], al // 2 : store2 shl ecx, 8 // 1 : scaling movzx eax, byte ptr [edi + 03] // 1 : dst2 load lea edx, [ebx + ecx] // 1 : table + scaler add esi, 4 // 1 : mov al, byte ptr [edx + eax] // 1 : table load mov byte ptr [edi + 03], al // 2 : store3 add edi, 4 // 1 dec nDivided_Width // 2 : LoopCounter jnz short lopX // 1
87 :
64 :2001/07/17(火) 12:01
後半いきます。 modulo: mov ecx, nWidth // 横幅取得 and ecx, 3 // Unroll余り分 // Unrollで余った分だけやる modulo_lopX: movzx eax, byte ptr [esi] movzx edx, byte ptr [edi] shl eax, 8 lea edx, [ebx + edx] add edx, eax mov al, byte ptr [edx] mov byte ptr [edi], al inc esi inc edi dec ecx jnz short modulo_lopX // Y方向のポインタ計算はここでやって! // テーブルの内容が連続しているならこれで良い dec nHeight // ちょっと手抜き jnz short lopY
88 :
64 :2001/07/17(火) 12:04
割と適当に書いて、動かしてないから、動かないかも(藁 コメント以下の数字:が、P6のμOP数です。 もっと詰められるけど、まぁこんな感じでUnrollして 4-1-1配置にすれば良いという参考までに。 あとは、ループの先頭を16Bytesにアラインする、 テーブルをキャッシュにプリロードする、 SSE使っていいなら、prefetch命令を最内周ループの 先頭に入れて、次のループ分をキャッシュに入れちゃうとか まぁそんなところで。
>>85 そうね。一見速そうに見える
>>76 のコードは実は遅い。
なぜかというと、上から下まで同一レジスタが生きているので処理順序を強制され、
アウトオブオーダー&レジスタリネームがほとんど生きない。
この半透明アルゴリズムでボトルネックはどこかというと、この
movzx ECX, byte ptr [EDX]
64KBのテーブルを読む部分。
64KBのテーブルをランダムにアクセスするので、1次キャッシュをミスする可能性が高く、
ここで処理が滞る事が多い。
だから、ここで処理が滞っても、アウトオブオーダーで次のドットを先に処理できるように、
コードを工夫した方がいい。
みんな、もっとスタックフレームを使おうぜ。 今後はますます平行処理出来る事をCPUにどう知らせるかが高速化の 決め手になるんだからさ
91 :
デフォルトの名無しさん :2001/07/17(火) 15:17
スタックアドレスの前後がレジスタに割り当てられるCPUがあるね
>>88 >SSE使っていいなら、prefetch命令を最内周ループの
>先頭に入れて、次のループ分をキャッシュに入れちゃうとか
とりあえず、ダミーで最内周ループ先頭に
mov al,[esi+4]
mov al,[edi+4]
って入れちゃえばいいんじゃない?
実際に入れてみるとちょっと速くなるみたい。
94 :
64 :2001/07/17(火) 17:35
>>92 P6の場合キャッシュ1ラインは32Bytesなので、32Bytes置きでOK。
よって、サイズにもよるけど、Yのループでやる方が良いかもね。
既に6クロックで1ドット処理出来てるなら 640x480ドット処理しても 100Mのマシンでも1秒50フレームは処理出来るじゃないか いったいなぜ不満が?
96 :
79 :2001/07/17(火) 17:56
そうか、そんなに早くなかったのか・・・・ 昔は読み書き回数 減らせばそれだけ早くなった んだけどなあ・・・というか最近最適化なんてやってないな
漏れがDirectDrawで半透明処理やってたときは、 ターゲットがMMXなP55Cだったけど、いかに1次キャッシュの上で 処理を完結させるかがキモだったような気もしたり。 バリバリ半透明合成やっても、20fpsくらいは出てたぞ〜 VTuneのトライアル版にだいぶお世話になったがの。
>>96 P6の場合、整数演算実行ポートとロード・ストア実行ポートが別々になっているので、
1次キャッシュにヒットするロード・ストアは有効に使った方がいい場合がある。
無理にレジスタのみで処理しようとすると、整数演算実行ポートばかりが飽和してしまい、
ロード・ストア実行ポートはガラ空きという、もったいない状況になる場合がある。
半透明合成で20FPSってぜんぜん自慢にならん(´д`;) ま、用途にもよるか。 # demoならOK。ADVゲームならOK。アクションなら×ってか。
100 :
64 :2001/07/17(火) 22:03
>>99 でもP55Cの時代だしね。
解像度にもよるけどまぁまぁなんじゃない?
101 :
デフォルトの名無しさん :2001/07/17(火) 22:14
MMX命令とは使えばもっとはやくなるんじゃないの?
>>101 MMXはテーブルにアクセスできないからね。
このアルゴリズムではMMXは使えない。
テーブルが簡単なものであれば、テーブルを使うのをやめて地道に計算→MMX化はできるけど、
テーブルが複雑なものであれば無理。
103 :
デフォルトの名無しさん :2001/07/17(火) 22:38
パレットではどーにもならん>MMX
L R1,0(,R1) LH R0,0(,R1) LTR R0,R0 BZ ABEND BCTR R0,0 STC *+5 MVC PARMSTR(0),2(R1) これを知っている人は"化石決定"です。
TLCS-12A ってCPU 知ってる?
106 :
デフォルトの名無しさん :2001/07/17(火) 23:34
P6では、この
>>86 -87のソースが最適化みたいね。
これ以上はないでしょう。
>>99 ADVに使おうと思ってた。逆にアクション向けだったら
重いセル合成などやらない方針にしてたと思う。
ちなみに出力先サーフェスは16bit(ビット配置はさまざま)だった。
16bitでアルファブレンドしてるんだけど 640x480x16で20fpsも出てくれません。 全画面ブレンドして60fpsは無理でしょうか? ブレンドも50%固定でなく、0〜100%でやってます。 CPUは400MHzです。
ageとこ。 ちなみに、当時のK6-2でもまともな速さで動いてたけど、 MMX器が1つしかないK6とかCyrix 686MXでは、トホホな感じだった。 VTuneでPentium最適化にしてしまったからのー。
>>106 やってる事が分かりやすいように、命令の順番を変えてませんし、
本当に早いか測ってませんが、ループの中身が、こんなのはどうですか?
P6のコストが、いまいち良く分からんので、自信はないけど、
P6に合わせて最適化すれば86のループよりは早くなりそうかと…。
(EBPはテーブル)
lopX:
mov eax, dwrod ptr [esi+000h]
mov ebx, dwrod ptr [edi+000h]
rol eax, 8
mov ecx, eax
mov edx, ebx
and eax, 000ff00ffh
and ebx, 0ff00ff00h
and ecx, 0ff00ff00h
and edx, 000ff00ffh
or ebx, eax
or ecx, edx
rol ebx, 8
movzx eax, cx
movzx edx, bx
shr ebx, 16
shr ecx, 16
mov al, byte ptr[ebp+eax]
mov bl, byte ptr[ebp+ebx]
mov cl, byte ptr[ebp+ecx]
mov dl, byte ptr[ebp+edx]
mov byte ptr[edi+000h], al
mov byte ptr[edi+001h], bl
mov byte ptr[edi+002h], cl
mov byte ptr[edi+003h], dl
add esi, 4
add edi, 4
dec nDivided_Width
jnz short lopX
>>106 うーん、ちょっと見た限りでは
>>86 -87もいいけど
>>79 も同じくらいの速さは出るようにに思うんだがなあ
特にAthlonだと 79の勝ちのように思う
おお、
>>111 が Athlonではダントツの結果だね 79のさらに半分で計算出来る
さらに微妙な所を狙うなら後半のループの部分で
add はフラグが変化するから
LEA esi,[ esi+4]
mov al, byte ptr[ebp+eax]
mov bl, byte ptr[ebp+ebx]
mov cl, byte ptr[ebp+ecx]
mov dl, byte ptr[ebp+edx]
dec nDivided_Width
mov byte ptr[edi+000h], al
mov byte ptr[edi+001h], bl
mov byte ptr[edi+002h], cl
mov byte ptr[edi+003h], dl
LEA edi,[ edi+4]
jnz short lopX
と速めにJNZが使うフラグを確定させてやった方がいいかも
114 :
64 :2001/07/18(水) 10:25
>>113 decとjnzを離すとP6だとフラグストールしないか?
>>114 この場合間に入れた演算命令がLEAだけだから
>依存関係を解消するために必要以上に離れていると
>前の命令の結果は現在の命令側のμop が必要とし
>た時点で、もはやディスパッチ・バッファに残っていない
>可能性がある。
だけど
>基本的な目安は、依存関係が解消されるだけの時間を
>取りながらも、バッファから結果データが失われるほど長
>時間ではない程度
だから、 LEA1個くらいなら大丈夫なんでは?
116 :
76 :2001/07/18(水) 11:03
>>111 方針は
>>76 と似てるけど、レジスタを巧くばらけて使ってるから速いんだな
半分って事は 、1ピクセル3クロック程度?
27命令が 12クロックか・・・
>>117 そうだね。キャッシュをヒットさせる条件で2.5倍くらい差が出たよ
Athlonは整数演算もアドレス演算もパイプラインが3つあるから途中の
同種命令が固まってあっても苦にならないんだろうね
PenII/IIIは整数演算ユニットは2つで、シフトは片方でしか実行出来ない
から、 それ用に命令の順番を変更してみたらどうよ?
119 :
デフォルトの名無しさん :2001/07/18(水) 12:34
PenII/IIIは命令デコードの段階でパイプ詰まり起こしてます
>>118 Athlonだと1ピクセル当たり何クロックで処理できるの?
121 :
デフォルトの名無しさん :2001/07/19(木) 02:36
PenIIでの最適化ってどんなやつ? 2個ってことは、ペアリングとか?
とりあえず、出来る限りチェーンを断ち切りましたが、 後半部分が、上手く動かない可能性が高いので、 論理和、シフト、ゼロ拡張付きロード、「add esi, 4」、 「sub nDivided_Width, 001h」の位置を適当に入れ替えてみたり、 前回のように、1バイトずつストアする方法に戻してみたりして下さい。 (今回もEBPはテーブル) lopX: mov edx, dwrod ptr [esi+000h] mov ecx, 000ff00ffh mov ebx, 0ff00ff00h mov eax, dwrod ptr [edi+000h] and ecx, edx and edx, ebx and ebx, eax shl ecx, 8 and eax, 000ff00ffh shr ebx, 8 or ecx, eax or edx, ebx movzx eax, cx shr ecx, 16 add edi, 4 movzx ecx, byte ptr[ebp+ecx] movzx ebx, dx shr edx, 16 movzx ebx, byte ptr[ebp+ebx] shl ecx, 16 movzx edx, byte ptr[ebp+edx] shl ebx, 8 movzx eax, byte ptr[ebp+eax] shl edx, 24 or ebx, ecx or edx, eax add esi, 4 or edx, ebx sub nDivided_Width, 001h mov byte ptr[edi-004h], edx jnz short lopX
>>122 コードが正しいとして、(dwrod は修正 byte ptrは取った)
Athlon で前回3.6クロック/ピクセルから 3.5クロック/ピクセルに改善されました
PentiumIIで計測してみたら
1次キャッシュに完全にヒットさせた状態で
>>86 が3.55クロック/ピクセル
>>122 が3.75クロック/ピクセル
くらい。
640×480ピクセル、ピクセルデータランダム、テーブルデータランダムだと、
>>86 が11.66クロック/ピクセル
>>122 が11.76クロック/ピクセル
くらいだった(ビデオメモリに転送せず、メインメモリ上で処理した場合)。
うちのPentiumIIでは次のような単純なコードが一番速かったよ。 mov edx, Width lopX: movzx ecx, byte ptr [esi] shl ecx, 8 movzx eax, byte ptr [edi] mov al, [tbl+eax+ecx] mov [edi], al movzx ecx, byte ptr [esi+1] shl ecx, 8 movzx eax, byte ptr [edi+1] mov al, [tbl+eax+ecx] mov [edi+1], al movzx ecx, byte ptr [esi+2] shl ecx, 8 movzx eax, byte ptr [edi+2] mov al, [tbl+eax+ecx] mov [edi+2], al movzx ecx, byte ptr [esi+3] shl ecx, 8 movzx eax, byte ptr [edi+3] mov al, [tbl+eax+ecx] mov [edi+3], al add esi, 4 add edi, 4 sub edx, 4 jne lopX
126 :
デフォルトの名無しさん :2001/07/19(木) 12:46
やっぱ、分岐も投機実行するからフラグの心配は意味なしだ
127 :
:2001/07/19(木) 13:15
Winでマシン語プログラミングなんかできるのか? 「EXEファイルはOSは理解するがCPUは理解しない」 APIでしか交信できない .dll .h .lib .drv .VxD .sys .ocx
>>127 さぁ、どうなんだろうね?難しい質問だね。
答えが分かったら是非教えてね。
>>128 禿げしく同意。難しい日本語の質問だね。
>>123 どこかミスってましたか?(^^;;;
確認してなかったので、申し訳ないです。m(_ _)m
と言う訳で、またまた確認してませんが、P6用に修正しました。
マニュアルしか読んでないので、本当に早いのか疑問はありますが…。
ループの外は何も考えてないので、適当に、その外に吸収してやって下さい。
(またまたEBPがテーブル)
mov edx, 0ff00ff00h
and edx, dwrod ptr [ edi+000h ]
mov ebx, 0ff00ff00h
and ebx, dword ptr [ esi+000h ]
shr edx, 8
mov eax, 000ff00ffh
or edx, ebx
sub nDivided_Width, 1
jz short lopX_END
lopX:
and eax, dwrod ptr [ esi+000h ]
mov ecx, 000ff00ffh
add esi, 004h
and ecx, dwrod ptr [ edi+000h ]
movzx ebx, dx
shl eax, 8
movzx ebx, byte ptr [ ebp+ebx ]
shr edx, 16
or ecx, eax
movzx edx, byte ptr [ ebp+edx ]
movzx eax, ecx
shl ebx, 8
movzx eax, byte ptr [ ebp+eax ]
shr ecx, 16
shl edx, 24
movzx ecx, byte ptr [ ebp+ecx ]
or eax, edx
add edi, 004h
shl ecx, 16
mov edx, 0ff00ff00h
or eax, ebx
and edx, dwrod ptr [ edi+000h ]
mov ebx, 0ff00ff00h
or ecx, eax
and ebx, dword ptr [ esi+000h ]
shr edx, 8
mov eax, 000ff00ffh
mov dwrod ptr [ edi-004h ], ecx
or edx, ebx
sub nDivided_Width, 1
jnz short lopX
(続き) lopX_END: and eax, dwrod ptr [ esi+000h ] mov ecx, 000ff00ffh and ecx, dwrod ptr [ edi+000h ] movzx ebx, dx shl eax, 8 movzx ebx, byte ptr [ ebp+ebx ] shr edx, 16 or ecx, eax movzx edx, byte ptr [ ebp+edx ] movzx eax, ecx shl ebx, 8 movzx eax, byte ptr [ ebp+eax ] shr ecx, 16 shl edx, 24 movzx ecx, byte ptr [ ebp+ecx ] or eax, edx shl ecx, 16 or eax, ebx or ecx, eax mov dwrod ptr [ edi+000h ], ecx
>>127 全角アルファベットあたりがかなり難解。
133 :
64 :2001/07/19(木) 20:33
sagaって来たね(藁 そろそろ終了か!?
PentiumIIでは
>>122 より
>>86 が若干速いみたいに書いたけど、
コードのアライメントによって微妙に変わってくるね。当たり前だけど。
PentiumIIではほとんど同じ速度みたい。
>>130 も
>>122 や
>>86 より速いということはなかった。
135 :
デフォルトの名無しさん :2001/07/26(木) 16:49
JMP _NATUYASUMI;
>>127 マジで言ってるのかなー?
CPUはマシン語以外理解できないと思うんだけど。
137 :
デフォルトの名無しさん :2001/07/26(木) 21:54
hage
sage
139 :
デフォルトの名無しさん :2001/08/23(木) 16:50
もうネタないかな?
だれかアセンブラで高級言語作って!