2024/02/08(木) [n年前の日記]
#1 [basic] アセンブラを少しだけ勉強中
FreeBASIC の Put() について、アセンブラで書かれてる部分がどういう処理をしているのか気になったので、少しだけx86アセンブラについて勉強中。
_fbc/src/gfxlib2/x86/gfx_put_trans_mmx.s at master - freebasic/fbc
C言語版のソースでは、画像バッファを1ドットずつ見ていって、透明色と遭遇したら書き込み処理をしない、といった処理をしていたけれど。アセンブラ版では条件分岐が見当たらなくて変だなと…。どうやら、二つのレジスタを比較して、値が一致したら片方のレジスタの全bitを1に、値が一致しなかったら全bitを0にする命令(pcmpeqd)を使っている模様。そこから先は、AND、NAND、OR を使って書き込むべき値を求めているのだろう。たぶん。よく分からんけど。
昔、OpenGL 関係で GLSL を勉強した際、「条件分岐が入るとそれだけで処理が遅くなる」「できるだけ数値計算で値を求めるようにせよ」という話を見かけた記憶があるのだけど。FreeBASIC の Put() もそういうことをしているのだろうか。命令数は増えるけど、条件分岐が入るルーチンより、まだ速いのかもしれない。たしか今時のCPUは、条件分岐が入って実際に分岐すると、片方の分岐先の処理結果を捨てるので無駄になる、という話をどこかで聞いた記憶もあるし…。
_fbc/src/gfxlib2/x86/gfx_put_trans_mmx.s at master - freebasic/fbc
C言語版のソースでは、画像バッファを1ドットずつ見ていって、透明色と遭遇したら書き込み処理をしない、といった処理をしていたけれど。アセンブラ版では条件分岐が見当たらなくて変だなと…。どうやら、二つのレジスタを比較して、値が一致したら片方のレジスタの全bitを1に、値が一致しなかったら全bitを0にする命令(pcmpeqd)を使っている模様。そこから先は、AND、NAND、OR を使って書き込むべき値を求めているのだろう。たぶん。よく分からんけど。
昔、OpenGL 関係で GLSL を勉強した際、「条件分岐が入るとそれだけで処理が遅くなる」「できるだけ数値計算で値を求めるようにせよ」という話を見かけた記憶があるのだけど。FreeBASIC の Put() もそういうことをしているのだろうか。命令数は増えるけど、条件分岐が入るルーチンより、まだ速いのかもしれない。たしか今時のCPUは、条件分岐が入って実際に分岐すると、片方の分岐先の処理結果を捨てるので無駄になる、という話をどこかで聞いた記憶もあるし…。
◎ C言語で書きたくなってきた :
C/C++ で書いた処理を即座にアセンブラで表示してくれるページを利用して勉強中。
_Compiler Explorer
x86 gcc を選んで、最適化オプションの -O 3 等をつけて結果が変わる様子を眺めていたけれど、かなり変わるものなのだなと感心してしまった。最適化をしないと、なんでもかんでもスタックに置いて、スタック上の値を使おうとするので、絶えずメモリアクセスが入って遅くなりそうだけど、最適化を有効にすると極力レジスタに入れてどうにかしようとしてくれる。また、ループ内の命令数も激減するように見える。たしかに速くなりそう…。
コンパイル結果を眺めているうちに、FreeBASIC のライブラリをC言語を使って書けないものかと思えてきた。C言語で書けたら gcc の最適化も使えるだろうから、アセンブラで書かずにC言語で書いたとしても、そこそこの速度が出てくれるのではないかしらん。
それはさておき。C言語のソース上でポインタの型をちゃんと指定しても、アセンブラ上では結局のところ、2だの4だのを足してアドレスを変化させてることにも気づいた。+= N を ++ にしても、出てくるアセンブラの結果は同じ。FreeBASIC のソース上で、どうにかして += 1 と書かないと速くならないのかなと思ってたけど、あまり関係ないのかもしれない。…いや、関係あるか。+= xd といった感じで、変数を足していたところが定数になってくれるのだから、変数1つ分 ―― スタックだかレジスタだかを1つ使わずに済むよな…。実際に試したら少しは速くなったし…。
_Compiler Explorer
x86 gcc を選んで、最適化オプションの -O 3 等をつけて結果が変わる様子を眺めていたけれど、かなり変わるものなのだなと感心してしまった。最適化をしないと、なんでもかんでもスタックに置いて、スタック上の値を使おうとするので、絶えずメモリアクセスが入って遅くなりそうだけど、最適化を有効にすると極力レジスタに入れてどうにかしようとしてくれる。また、ループ内の命令数も激減するように見える。たしかに速くなりそう…。
コンパイル結果を眺めているうちに、FreeBASIC のライブラリをC言語を使って書けないものかと思えてきた。C言語で書けたら gcc の最適化も使えるだろうから、アセンブラで書かずにC言語で書いたとしても、そこそこの速度が出てくれるのではないかしらん。
それはさておき。C言語のソース上でポインタの型をちゃんと指定しても、アセンブラ上では結局のところ、2だの4だのを足してアドレスを変化させてることにも気づいた。+= N を ++ にしても、出てくるアセンブラの結果は同じ。FreeBASIC のソース上で、どうにかして += 1 と書かないと速くならないのかなと思ってたけど、あまり関係ないのかもしれない。…いや、関係あるか。+= xd といった感じで、変数を足していたところが定数になってくれるのだから、変数1つ分 ―― スタックだかレジスタだかを1つ使わずに済むよな…。実際に試したら少しは速くなったし…。
◎ 参考ページ :
[ ツッコむ ]
#2 [basic] FreeBASICでライブラリファイルを作成する方法を調べていた
FreeBASICのライブラリをC言語で書きたいと思ったものの、解説ページが見当たらなくて。まずはその前に、FreeBASICでライブラリファイル(lib*.a)を作成する手順について試してみようかなと思い立った。
手順については以下のページが参考になった。ありがたや。
_静的ライブラリ - ProPgStaticLibraries
_Static Libraries - FreeBASIC Wiki Manual | FBWiki
FreeBASICコンパイラ(fbc)に、「-lib」をつけるだけでいいらしい。圧倒的に簡単…。
後は、その関数/サブルーチンがどんな引数を要求して、どんな値を返すのか、そのあたりを書いたヘッダーファイル(.bi)を書いて、実際に使いたいファイルの最初のほうで #include "hoge.bi" を書いてやればいい。簡単過ぎる…。
手順については以下のページが参考になった。ありがたや。
_静的ライブラリ - ProPgStaticLibraries
_Static Libraries - FreeBASIC Wiki Manual | FBWiki
FreeBASICコンパイラ(fbc)に、「-lib」をつけるだけでいいらしい。圧倒的に簡単…。
- 関数/サブルーチンだけを書いた .bas を用意する。
- fbc -lib hoge.bas でコンパイル。
- libhoge.a が作成される。
後は、その関数/サブルーチンがどんな引数を要求して、どんな値を返すのか、そのあたりを書いたヘッダーファイル(.bi)を書いて、実際に使いたいファイルの最初のほうで #include "hoge.bi" を書いてやればいい。簡単過ぎる…。
◎ gccでライブラリファイル化したい :
どうしてライブラリファイルの形にしたいのかと言うと、そのライブラリファイルだけでも gcc でコンパイルしたいから。
FreeBASIC 32bit版は、gas と呼ばれる、GNUアセンブラ(as.exe)を使って実行形式を作るのだけど、最適化はかからないから処理が遅い実行形式が生成されてしまう。しかし、gcc の最適化を有効にしてコンパイルするだけで、同じソースでも数倍の速さになってくれる。
しかし、gcc はどの環境にも入ってるわけではないので、gccが入ってる環境でライブラリファイルだけでも作ることができれば、そのライブラリファイルを配布することで処理が速い版を使えるのではないかなと…。
ただ、Windows 32bit版、64bit版、Linux版 の lib*.a を用意しなきゃいけないあたりが少し面倒臭いかも。
FreeBASIC 32bit版は、gas と呼ばれる、GNUアセンブラ(as.exe)を使って実行形式を作るのだけど、最適化はかからないから処理が遅い実行形式が生成されてしまう。しかし、gcc の最適化を有効にしてコンパイルするだけで、同じソースでも数倍の速さになってくれる。
しかし、gcc はどの環境にも入ってるわけではないので、gccが入ってる環境でライブラリファイルだけでも作ることができれば、そのライブラリファイルを配布することで処理が速い版を使えるのではないかなと…。
ただ、Windows 32bit版、64bit版、Linux版 の lib*.a を用意しなきゃいけないあたりが少し面倒臭いかも。
[ ツッコむ ]
以上、1 日分です。