トップページ⇒tech
140コメント60KB

アセンブラに関するマジ質問だけど

■ このスレッドは過去ログ倉庫に格納されています
0001デフォルトの名無しさんNGNG
今、半透明ってやつをやってるんだけど。256色の。
どうにも速度が出ないんだよね・・・・
下に俺のソースの抜粋書くから、
わかる人は教えてくれないかな・・・?
0002デフォルトの名無しさんNGNG
あらかじめ、半透明のカラーテーブル(256*256)を
64KB境界に合わせておいて、
mov EBX, [Table]
というふうにEBXに入れておく。
で、下が<半透明書き込み部分>
mov BL, [ESI] //転送元をBLに
mov BH, [EDI] //転送先のBHに
mov AL, [EBX] //書き込む色をALに
mov [EDI],AL
inc EDI
あとは横×縦のループでこれを囲む。
0003デフォルトの名無しさんNGNG
アセンブラスレなら、既にいくつかあっただろう・・
0004デフォルトの名無しさんNGNG
ちなみに、かかるクロック数は、上から
1,1,5,2、1。
mov AL, [EBX] でストールが発生しちゃってるんだよね・・・
かといって、普通にADD使うと遅そうだし。
0005デフォルトの名無しさんNGNG
スマン。新スレ立てることはなかったな。
上のほう見てなかったんで。

もしわかる人いたらsageでいいから頼む・・・
0006デフォルトの名無しさんNGNG
パーシャルレジスタストールかな
データを3個くらい並列処理したら?
0007デフォルトの名無しさんNGNG
>>2
これってもろパーシャルレジスタストールじゃない?
P6系ではBLとBHを別々に書き込んで、EBXで合わせて使うと
ストールが発生したはず。
そのまま素直にシフト+加算で計算した方がいいよ。
00087NGNG
ダブった。
0009デフォルトの名無しさんNGNG
なるほど・・・・今、調べてきてみた。
下位だけ書き換えちゃいけなかったのね。
0010デフォルトの名無しさんNGNG
>>6
>>7
どうもありがとう。
並列は、ループでECX,EDX使ってるから
ちょっと無理そう。(EDI,ESIは各矩形のポインタ)
ADD+シフトでやってみるよ。
0011デフォルトの名無しさんNGNG
でも、

普通にシフトと足し算でやるにしても
結局下位8にロードして、左8シフト。さらに下位8にロードして
ポインタを足すわけでしょう?

結局このストールがおきるんじゃないのかな?
0012デフォルトの名無しさんNGNG
実行結果。
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クロックまであがったよ。
まだまだ実用的じゃないみたいだけど大きな進歩だね。
本当にありがとう。
0013デフォルトの名無しさんNGNG
スマ。間違い。上のは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
0014デフォルトの名無しさんNGNG
>>11
パーシャルレジスタストールはP6のアウトオブオーダー実行コアの
命令スケジューラの制限じゃないかな?
パーシャルレジスタアクセスを行うとP6の命令スケジューラがマヒするんだと思う。
Pentium4ではパーシャルレジスタストールは無いらしい。
0015デフォルトの名無しさんNGNG
>>13
mov EBX, [ESI]
mov EAX, [EDI]
and EBX, 255
and EAX, 255
は
movzx EBX, byte ptr [ESI]
movzx EAX, byte ptr [EDI]
でいいんじゃないかな。
0016デフォルトの名無しさんNGNG
情報ありがとう!

>>14
なるほどね・・・・
てことは、Pentium4では、最初のが最速になるってことか。
本当に高速化を目指すならCPUごとに条件分岐すれば一番なのか・・・

>>15
おっと、盲点・・・・・恥ずかしい所を見せたか(^^;
0017デフォルトの名無しさんNGNG
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
 // 外側ループ終了
  }
0018デフォルトの名無しさんNGNG
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。
0019デフォルトの名無しさんNGNG
とりあえず、ループ展開しとけ。
byte 単位でメモリアクセスしているのを dword 単位のアクセスにできるだろう。
0020デフォルトの名無しさんNGNG
ばっかみたい!
DirectXならハードウェアで半透明やってくれるじゃん。
0021デフォルトの名無しさんNGNG
クロックが8とか6とか意っているけれど、
何で計測しているのですか?
0022>16NGNG
CPU 毎にインストールするバイナリを変える、だろ。
0023デフォルトの名無しさんNGNG
>>20

煽りにしてもベタでつまらんな。
0024sageNGNG
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だと、
ライトキャッシュ効かなくなって遅くなることあるよ。
0025sageNGNG
↑追加修正

当然気づいたとは思うけど、

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マニュアル読め。
0026デフォルトの名無しさんNGNG
情報その他ありがとう!

>>18
なるほど。勉強になる・・・・
まともにうごかないのはcllがポインタだからかな?

>>19
ループの展開?
4ドット同時に処理せよということかな?

>>20
DIRECT3Dならね・・・

>>21
ネットで見つけた、誰かのプログラムなんだけど
どうも不安定っぽい。

>>22
そうだね(^^;
0027デフォルトの名無しさんNGNG
>>24
>>25
有力な情報ありがとう。
やはり自分はまだまだ未熟だな・・・
やっぱり問題はループにあったのか。
cp+jlって意外と遅かったのね。

とりあえず、現時点のことを全部やったらFPSが52まで出るようになったよ。
みんなありがとう。
0028名無しだよもんNGNG
testでジャンプするよりも
JECXZを使えばもっとはやいんじゃないの?
0029名無しだよもんNGNG
スマン。JECXZじゃなくて、LOOPな
0030デフォルトの名無しさんNGNG
ってことは。
今のところ、これが最高速?
もうこれ以上はあがらないのかな?



  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
// 外側ループ終了
0031デフォルトの名無しさんNGNG
>>29
LOOPは複雑命令だから遅い。
0032デフォルトの名無しさんNGNG
ってことは、LOOPはあるだけ無意味ってことなのか(w
0033shigeNGNG
今田にアセンブラなんてやってる奴いるんだな。
0034デフォルトの名無しさんNGNG
何人かがアセンブラで最適化した分の利益を,我々は使わせていただいているんだ。
ってことに気付け。
0035デフォルトの名無しさんNGNG
>>30
てか、これでもう最適なのか?
順番変えれば、ペアリングとかできそうじゃない?
0036デフォルトの名無しさんNGNG
くそぉ
ネタにネタレスしてやるっ!!!!
>>33
Perlででもアルファブレンディングのルーチン書いて
死んでやがれヴォケ
0037デフォルトの名無しさんNGNG
>>33
まあ普通は無駄だけど、MMXとかSIMD使う分には価値があるよ。
0038shigeNGNG
>>36
ハァ?何で俺がPerlみたいな糞言語やらなきゃならねーんだよ。
Ruby以外の言語は全部糞だっていつも言ってるだろヴォケ。
0039デフォルトの名無しさんNGNG
有意義なスレだと思ったんだが、結局はこうか。
0040デフォルトの名無しさんNGNG
>>30
最適ではない。
004136NGNG
>>38
悪かった。わざと言ったんだ。
じゃあ言い直すぜ!

Rubyででもアルファブレンディングのルーチン書いて
死んでやがれヴォケ
0042デフォルトの名無しさんNGNG
フリーでソース付きの8086〜Pentium向けのアセンブラってありませんか?
あんまり大きくない奴で。
0043デフォルトの名無しさんNGNG
あんまり大きくない奴で。 <サイズが。
0044#6411NGNG
素朴なギモンだが、ターゲットがDirectDrawで、
出力先はフレームバッファだったりしないかな?
だとすると、抜本的な高速化は別の方法があると思われ。

ここ2年DirectX触れてないから、用語忘れた。…鬱だ…
00451NGNG
>>4
本当?
今回は、LOCKを避けるために配列変数から配列変数への転送だけど
それについてはちょっと興味あるな。
なんせ、LOCK使うと遅くなるからサーフェスはほとんどつかわないつもりだったんで。
004636NGNG
>>42
nasm
0047いつでもどこでも名無しさんNGNG
>>30
何で1バイトづつやってるの?
0048デフォルトの名無しさんNGNG
>>26
>>>21
>ネットで見つけた、誰かのプログラムなんだけど
>どうも不安定っぽい。
計測プログラムが気になる。URLキボン。
0049デフォルトの名無しさんNGNG
>>47
最適化前だから。
0050デフォルトの名無しさんNGNG
>>2の64KByte境界にあわせるってあるけど、こんなことってできるの?
00511NGNG
>>47
>>49
レジスタももう埋まってて使えないし
どうやって並行処理するの?
MMX使うとかしかないのかな?

>>50
unsigned char* test;
unsigned char dummy[256*256*2];
test = (unsigned char *) (((unsigned)dummy + 65535) & (-65536))
っていう方法で。もしかしてちと強引なのかな?
00521NGNG
>>48
ttp://www.yo.rim.or.jp/~sawara/opt/jk9905.html

WatcomC用ってなってたから、VC++にちょっと改造したけど。
0053デフォルトの名無しさんNGNG
http://www.intel.co.jp/jp/developer/design/pentium/manuals/index.htm
0054デフォルトの名無しさんNGNG
何クロックとかって懐かしいね。でも、クロック計算できるのはPentiumまでよ。
P6以降は、μOP命令に変換されて、空いているスロットを使ってOOO実行とか
しちゃうから、クロックの計算は出来ないでしょう。
#V-Tuneでも、クロックは出ないはず。

>>28
Loopは、今や遅い命令です。

dec ecx
jnz @@@

の方が速いです(ただしP6の話。AMDでは使った方が速い)
また、P6はフラグストールとかって面倒なもんがあるんで、
cmpして判定しないと逆に遅くなったりします。

>>30
あとは、Loop Unrollして、Incとかはなるべく[esi + 01]とかに置き換えた方が良いよ。
0055デフォルトの名無しさんNGNG
勉強になるのでage

最適化好きな人にはここがおすすめだよ。
ttp://homepage1.nifty.com/herumi/adv/adv40.html
00561NGNG
>>54
なるほどね。だからクロック数が不安定だったのね。
数値が実行ごとにずれたりしてたから。
Loop Unrollって?
あと、Incなしで [esi + xx]のほうが速いんだ。
上にそっちだと遅いって人がいたけど、マシンによるのかな?

>>53
>>55
情報サンクス。
今から印刷してじっくりと見てみるよ。
0057デフォルトの名無しさんNGNG
inc esiよりlea esi,[esi+1]をペアリングの空きに突っ込む方が吉
leaはフラグが変わらんから分岐の直前でもいいぞ
0058デフォルトの名無しさんNGNG
>>57
そうだね。
それより
>>30
で気になるのが、movzxって遅くなかったっけ?
xor eax, eax
mov eax, byte ptr[edi]
としてペアリングした方が大抵速くなかったっけ?
0059デフォルトの名無しさんNGNG
>>58
どうでもいいつっこみだけど
xor eax, eax
mov al, byte ptr[edi] ね。

おそいっていうかmovzxやmovsxはペアリングができないんだよ。
xorとmovに分解すればペアリングできる機会が増えるってことで。

いつ出るかなーと思っていたがなかなか出ないので
リンクage
http://www.csl.sony.co.jp/person/fnami/pentopt.htm
http://www.csl.sony.co.jp/person/fnami/asm.htm
0060デフォルトの名無しさんNGNG
・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
命令を使用するとよい。
0061デフォルトの名無しさんNGNG
今はP6系の最適化をしてるんでしょ?
なんでP5系のペアリングの話が出てくるんだ?
今時P5最適化なんてナンセンスじゃない?

Loop Unrollして、Incを使わず [esi + 1]で処理するというのは
依存性チェーンを断ち切って、P6のOOO実行性能を高めるっていう話でしょ?
0062デフォルトの名無しさんNGNG
あと、P6系の最適化リファレンスマニュアルはこっちね。
http://www.intel.co.jp/jp/developer/design/pentiumIII/manuals/index.htm
これを読めばほとんど全部書いてある。ここで人に聞くより、
まずはこれを全部読むことから始めた方が近道だよ。
0063デフォルトの名無しさんNGNG
>今はP6系の最適化をしてるんでしょ?
あ、そうだったのか

> なんでP5系のペアリングの話が出てくるんだ?
>今時P5最適化なんてナンセンスじゃない?
たしかにな(笑)
0064デフォルトの名無しさんNGNG
現在のメインストリームは、P6なので、ターゲットを
P6にした方が良いと思うよ。
movzx, movsxは、P6では1μOP。がんがん使いましょう。
あと、最適化マニュアル読めばわかるけど、命令のデコーダが
1Clockで3つの命令をデコードできるんだけど、最初のデコーダのみが
4μOPまでの命令をデコードできて、残りの2つのデコーダは1μOPまで
(要するに超単純な命令)しかでコードできないから、なるべく
μOP命令数で、4-1-1になるように命令を配置した方がいいよ。
なるべくRSを一杯にしといた方が有利だからね > P6
0065デフォルトの名無しさんNGNG
P6の整数演算ユニットは2個だぞ
シフトのレイテンシは大きいぞ
油断大敵!
0066デフォルトの名無しさんNGNG
P6のシフトのレイテンシが大きい?また嘘ばっかり。
0067デフォルトの名無しさんNGNG
Loop Unrollって何?
0068デフォルトの名無しさんNGNG
>>67
>>62のアーキテクチャ最適化リファレンス・マニュアル読んだか?
0069デフォルトの名無しさんNGNG
pdfをテキストかhtmlに変換するツール知らない?
00701NGNG
おお、どんどん有力な情報が。
ようやくアンロールが理解できたよ。
いくつくらいに展開すりゃいいんだろ。
8?

>>64
あー、だから3つの命令でも例のプログラムで1クロックって出たり
したのか・・・・・納得。バグだと思ってたよ。
4−1−1ね。やってみる。ペアリングとかはもう気にする時代じゃないんだね。
0071デフォルトの名無しさんNGNG
>>P6系所有者
2のコードだけど、マニュアルを読む限り、

xor EBX,EBX
mov EDX, [Table]
 :
 :
mov BL, [ESI]
mov BH, [EDI]
mov AL, [EDX+EBX]
mov [EDI],AL
inc EDI

にすれば、パーシャルレジスタストールしないで、
ループ出来そうなんだけど、どうなの?
0072デフォルトの名無しさんNGNG
>>71
mov BH, [EDI]
の行がなければストールしない。
この行があるからストールする。
0073デフォルトの名無しさん NGNG
>>51
EBPは?
0074デフォルトの名無しさんNGNG
こういうのはだめかい?

  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
0075デフォルトの名無しさんNGNG
もう一息!
0076デフォルトの名無しさんNGNG
ありゃ、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
  ・・・繰り返し・・
007776NGNG
それから、tblが固定アドレスなんだったら tbl[EDX] とか [EDX+tbl]と書いた方が
早くなる可能性がありそうな気がする 固定じゃないんだったらEBPを使ったらどう?
007876NGNG
さらに8bitで3:3:2とかのパレット固定だったら その場で計算させた方が速い場合もあるよ
0079デフォルトの名無しさんNGNG
  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;//読み捨て
0080デフォルトの名無しさん NGNG
76の勝ち!
008164NGNG
せっかく書いたのに、改行が多すぎますって言われた。
0082デフォルトの名無しさんNGNG
複数にわければOKじゃない?
00831NGNG
みんなすげえなあ・・・・
最初、上のソースが何やってるかさっぱりだったよ。

とりあえず1行ずつ追っていって、なんとかどういう意味なのかわかったよ。

>>74
4つ平行か。
EAXをいちいち読むよりこっちのほうが早いんだ?

>>76
これはどうやってループにするの?
79のソースみたいにスタックを使えばいいのかな?
ためしにやってみたけど、なんか遅かった・・・おかしいな?

>>79
スタックを使ったカウンタのやり方サンクス〜
0084デフォルトの名無しさん NGNG
>>69
ttp://www.keiyu.com/doc/pdftxt.htm
ttp://www.geocities.com/taketakohei/pdf2txt.html
英語なら
ttp://www.ra.informatik.uni-stuttgart.de/%7Egosho/pdftohtml/index.html
008579NGNG
>>74 は XLATB 直後に ror EAX,8 がどうもパーシャルレジスタストール
  する可能性有り

>>76のコードは 4つ一度にロードしてるけど、
  EDXを連続して操作してるから思ったより
 早くないかもしれないね。

あ、でも>>79は push Width/2 に変えてね
008664NGNG
分割行きます。

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
008764NGNG
後半いきます。

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
008864NGNG
割と適当に書いて、動かしてないから、動かないかも(藁
コメント以下の数字:が、P6のμOP数です。
もっと詰められるけど、まぁこんな感じでUnrollして
4-1-1配置にすれば良いという参考までに。
あとは、ループの先頭を16Bytesにアラインする、
テーブルをキャッシュにプリロードする、
SSE使っていいなら、prefetch命令を最内周ループの
先頭に入れて、次のループ分をキャッシュに入れちゃうとか
まぁそんなところで。
0089デフォルトの名無しさんNGNG
>>85
そうね。一見速そうに見える>>76のコードは実は遅い。
なぜかというと、上から下まで同一レジスタが生きているので処理順序を強制され、
アウトオブオーダー&レジスタリネームがほとんど生きない。

この半透明アルゴリズムでボトルネックはどこかというと、この
movzx ECX, byte ptr [EDX]
64KBのテーブルを読む部分。
64KBのテーブルをランダムにアクセスするので、1次キャッシュをミスする可能性が高く、
ここで処理が滞る事が多い。
だから、ここで処理が滞っても、アウトオブオーダーで次のドットを先に処理できるように、
コードを工夫した方がいい。
0090デフォルトの名無しさんNGNG
みんな、もっとスタックフレームを使おうぜ。

今後はますます平行処理出来る事をCPUにどう知らせるかが高速化の
決め手になるんだからさ
0091デフォルトの名無しさんNGNG
スタックアドレスの前後がレジスタに割り当てられるCPUがあるね
0092デフォルトの名無しさんNGNG
>>88
>SSE使っていいなら、prefetch命令を最内周ループの
>先頭に入れて、次のループ分をキャッシュに入れちゃうとか

とりあえず、ダミーで最内周ループ先頭に
mov al,[esi+4]
mov al,[edi+4]
って入れちゃえばいいんじゃない?
実際に入れてみるとちょっと速くなるみたい。
0093デフォルトの名無しさん NGNG
>>73
ESPもあるぞ
009464NGNG
>>92
P6の場合キャッシュ1ラインは32Bytesなので、32Bytes置きでOK。
よって、サイズにもよるけど、Yのループでやる方が良いかもね。
0095デフォルトの名無しさんNGNG
既に6クロックで1ドット処理出来てるなら 640x480ドット処理しても
100Mのマシンでも1秒50フレームは処理出来るじゃないか

いったいなぜ不満が?
009679NGNG
そうか、そんなに早くなかったのか・・・・
昔は読み書き回数 減らせばそれだけ早くなった
んだけどなあ・・・というか最近最適化なんてやってないな
0097#6411NGNG
漏れがDirectDrawで半透明処理やってたときは、
ターゲットがMMXなP55Cだったけど、いかに1次キャッシュの上で
処理を完結させるかがキモだったような気もしたり。
バリバリ半透明合成やっても、20fpsくらいは出てたぞ〜
VTuneのトライアル版にだいぶお世話になったがの。
0098デフォルトの名無しさんNGNG
>>96
P6の場合、整数演算実行ポートとロード・ストア実行ポートが別々になっているので、
1次キャッシュにヒットするロード・ストアは有効に使った方がいい場合がある。
無理にレジスタのみで処理しようとすると、整数演算実行ポートばかりが飽和してしまい、
ロード・ストア実行ポートはガラ空きという、もったいない状況になる場合がある。
0099デフォルトの名無しさんNGNG
半透明合成で20FPSってぜんぜん自慢にならん(´д`;)
ま、用途にもよるか。
# demoならOK。ADVゲームならOK。アクションなら×ってか。
010064NGNG
>>99
でもP55Cの時代だしね。
解像度にもよるけどまぁまぁなんじゃない?
0101デフォルトの名無しさんNGNG
MMX命令とは使えばもっとはやくなるんじゃないの?
0102デフォルトの名無しさんNGNG
>>101
MMXはテーブルにアクセスできないからね。
このアルゴリズムではMMXは使えない。
テーブルが簡単なものであれば、テーブルを使うのをやめて地道に計算→MMX化はできるけど、
テーブルが複雑なものであれば無理。
0103デフォルトの名無しさんNGNG
パレットではどーにもならん>MMX
0104デフォルトの名無しさんNGNG
L R1,0(,R1)
LH R0,0(,R1)
LTR R0,R0
BZ ABEND
BCTR R0,0
STC *+5
MVC PARMSTR(0),2(R1)

これを知っている人は"化石決定"です。
0105デフォルトの名無しさんNGNG
TLCS-12A ってCPU 知ってる?
0106デフォルトの名無しさんNGNG
P6では、この>>86-87のソースが最適化みたいね。
これ以上はないでしょう。
0107#6411NGNG
>>99 ADVに使おうと思ってた。逆にアクション向けだったら
重いセル合成などやらない方針にしてたと思う。
ちなみに出力先サーフェスは16bit(ビット配置はさまざま)だった。
0108デフォルトの名無しさんNGNG
16bitでアルファブレンドしてるんだけど
640x480x16で20fpsも出てくれません。
全画面ブレンドして60fpsは無理でしょうか?
ブレンドも50%固定でなく、0〜100%でやってます。
CPUは400MHzです。
0109#6411NGNG
>>108 手で最適化してみたいのなら、下記から「最適化マニュアル」手繰ってちょ。
http://www.intel.co.jp/jp/developer/design/pentium/manuals/
アルファブレンドとかパッキングに関しては、下記から登録して、
Imageとかそのあたりを読み漁ってもらえれば。ただしぜんぶ英語。
http://developer.intel.com/software/idap/

漏れの経験では、P55C-266にて、640x480x16の
全画面クロスフェードが25fps超えてた。(DX5の時代)
当時のKlamath-266では、40fpsくらい逝ってた記憶も。
どうしても速くならなかったら、ソースの一部示せ。

なお、DirectDrawオンリーでなければ、最近のマシンでは60fps楽勝だ。
0110#6411NGNG
ageとこ。
ちなみに、当時のK6-2でもまともな速さで動いてたけど、
MMX器が1つしかないK6とかCyrix 686MXでは、トホホな感じだった。
VTuneでPentium最適化にしてしまったからのー。
0111デフォルトの名無しさんNGNG
>>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
0112デフォルトの名無しさんNGNG
>>106
うーん、ちょっと見た限りでは>>86-87もいいけど
>>79 も同じくらいの速さは出るようにに思うんだがなあ
 特にAthlonだと 79の勝ちのように思う
0113112NGNG
おお、>>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が使うフラグを確定させてやった方がいいかも
011464NGNG
>>113
decとjnzを離すとP6だとフラグストールしないか?
0115112NGNG
>>114
 この場合間に入れた演算命令がLEAだけだから
>依存関係を解消するために必要以上に離れていると
>前の命令の結果は現在の命令側のμop が必要とし
>た時点で、もはやディスパッチ・バッファに残っていない
>可能性がある。
 だけど
>基本的な目安は、依存関係が解消されるだけの時間を
>取りながらも、バッファから結果データが失われるほど長
>時間ではない程度
だから、 LEA1個くらいなら大丈夫なんでは?
011676NGNG
>>111 方針は >>76と似てるけど、レジスタを巧くばらけて使ってるから速いんだな

半分って事は 、1ピクセル3クロック程度?

27命令が 12クロックか・・・
0117デフォルトの名無しさんNGNG
>>113
Athlonだとそんなに>>111が速い?
うちのPentiumIIだと>>111より>>86の方が若干速いみたいよ。
0118112NGNG
>>117 そうだね。キャッシュをヒットさせる条件で2.5倍くらい差が出たよ
Athlonは整数演算もアドレス演算もパイプラインが3つあるから途中の
同種命令が固まってあっても苦にならないんだろうね

PenII/IIIは整数演算ユニットは2つで、シフトは片方でしか実行出来ない
から、 それ用に命令の順番を変更してみたらどうよ?
0119デフォルトの名無しさんNGNG
PenII/IIIは命令デコードの段階でパイプ詰まり起こしてます
0120デフォルトの名無しさんNGNG
>>118
Athlonだと1ピクセル当たり何クロックで処理できるの?
0121デフォルトの名無しさんNGNG
PenIIでの最適化ってどんなやつ?
2個ってことは、ペアリングとか?
0122111NGNG
とりあえず、出来る限りチェーンを断ち切りましたが、
後半部分が、上手く動かない可能性が高いので、
論理和、シフト、ゼロ拡張付きロード、「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
0123112NGNG
>>122 コードが正しいとして、(dwrod は修正 byte ptrは取った)
Athlon で前回3.6クロック/ピクセルから 3.5クロック/ピクセルに改善されました
0124デフォルトの名無しさんNGNG
PentiumIIで計測してみたら
1次キャッシュに完全にヒットさせた状態で
>>86 が3.55クロック/ピクセル
>>122 が3.75クロック/ピクセル
くらい。

640×480ピクセル、ピクセルデータランダム、テーブルデータランダムだと、
>>86 が11.66クロック/ピクセル
>>122 が11.76クロック/ピクセル
くらいだった(ビデオメモリに転送せず、メインメモリ上で処理した場合)。
0125124NGNG
うちの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
0126デフォルトの名無しさんNGNG
やっぱ、分岐も投機実行するからフラグの心配は意味なしだ
0127 NGNG
Winでマシン語プログラミングなんかできるのか?
「EXEファイルはOSは理解するがCPUは理解しない」
APIでしか交信できない
.dll
.h
.lib
.drv
.VxD
.sys
.ocx
0128デフォルトの名無しさんNGNG
>>127
さぁ、どうなんだろうね?難しい質問だね。
答えが分かったら是非教えてね。
0129#6411NGNG
>>128 禿げしく同意。難しい日本語の質問だね。
0130111NGNG
>>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
0131111NGNG
(続き)

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
0132デフォルトの名無しさんNGNG
>>127
全角アルファベットあたりがかなり難解。
013364NGNG
sagaって来たね(藁
そろそろ終了か!?
0134124NGNG
PentiumIIでは>>122より>>86が若干速いみたいに書いたけど、
コードのアライメントによって微妙に変わってくるね。当たり前だけど。
PentiumIIではほとんど同じ速度みたい。
>>130も>>122や>>86より速いということはなかった。
0135デフォルトの名無しさんNGNG
JMP _NATUYASUMI;
0136デフォルトの名無しさんNGNG
>>127
マジで言ってるのかなー?
CPUはマシン語以外理解できないと思うんだけど。
0137デフォルトの名無しさんNGNG
hage
0138デフォルトの名無しさんNGNG
sage
0139デフォルトの名無しさんNGNG
もうネタないかな?
0140デフォルトの名無しさんNGNG
だれかアセンブラで高級言語作って!
■ このスレッドは過去ログ倉庫に格納されています