HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2009
0321
hspailマシン語で平方根を求めたい24未解決


hspail

リンク

2009/3/21(Sat) 10:49:31|NO.24012

こんにちは。
マシン語で平方根を求めるという単純なプログラムを作ったのですが、callfuncのところでエラーが出てしまいます。
原因がHSP側にあるのかC側にあるのかもわからないためここで質問させていただきました。

<HSP側>

#uselib "kernel32" #func global VirtualProtect "VirtualProtect" var,int,int,int dim fncode, 5 VirtualProtect fncode, 5*4, $40, varptr(ret) fncode(0) = $042444dd, $1cdd5151, $0000e824, $59590000, $000008c2 prm.0 = 2.0 ret = callfunc(prm, varptr(fncode), 1) dialog ret
<C側>

#include <windows.h> #include <math.h> double WINAPI opt(double n) { return sqrt(n); }

どなたかご教示くださいませんか。よろしくお願いします。



この記事に返信する


KA

リンク

2009/3/21(Sat) 16:27:19|NO.24014

この内容では確認する気も起こりません。
他人が再現できる、最低限の情報は書いてください。

あまりこの辺は詳しくありませんが、ぱっと見て気がついた点。

 1:どこにマシン語が???

 2:<C側>と有るが、どうやって呼び出しているの??
   (EXE?DLL? そのファイル名は?)
   ↑↑↑↑↑↑ ここが不明 ↑↑↑↑↑↑

 3:<HSP側> 何か変です。



hspail

リンク

2009/3/21(Sat) 17:22:35|NO.24019

説明不足、大変申し訳ありませんでした。反省します。

1.
fncode(0) = $042444dd, $1cdd5151, $0000e824, $59590000, $000008c2
の部分です。

2.
DLLなどから呼び出しているのではなく、コンパイルしたオブジェクトファイルから取り出しています。
↓のHPを参考にさせてもらっています。
http://yokohama.cool.ne.jp/chokuto/urawaza/mcn/vc_cmdline.html

3.どこが変か指摘していただけませんか?

よろしくお願いします。



レノス

リンク

2009/3/22(Sun) 02:02:53|NO.24037

エラーがでる場合は、エラー番号を書いてください。

callfunc の p1 の配列変数は、int 型限定で、double 型は使用できません。
intを2つ連続で使ったら( 64バイト )通るかもしれません……試してませんが。

また、機械語を配列に展開する方法では、ライブラリ関数を使うとちょっと面倒です。
その機械語には sqrt() を呼び出すために、それへの関数ポインタが含まれますが、
関数ポインタが決まるのは実行時です。
そのため、実行時に、機械語の関数ポインタを poke などで正しい値に上書きする必要があります。
※ もちろん、sqrt がインライン展開されるか、マクロで定義されている場合は問題ないです。



hspail

リンク

2009/3/22(Sun) 06:56:51|NO.24041

なんだか難しそうですね。

>エラーがでる場合は、エラー番号を書いてください。
実行してみればわかると思いますが、
--------------------------
Error 1 in line 9
システムエラーが発生しました。
--------------------------
になります。



通りすがり

リンク

2009/3/23(Mon) 12:49:21|NO.24058

…とりあえず、sqrtの内容がどこにあるのか考えてみようか。
その機械語は単にsqrtを呼び出すだけで、
リンクしてないもんだからsqrtの場所が分からないと文句をつけられているのだと思う。
まあ、C言語側のソースで標準ライブラリの関数を使うのは止めようか。
FPUやらFSQRTでググると幸せになれるかもしれん。



vaaf

リンク

2009/3/23(Mon) 17:24:16|NO.24074

msvcrt.dllのsqrtを#funcすればよろし



船丸

リンク

2009/3/26(Thu) 10:55:42|NO.24161

失礼かもしれませんが、何故マシン語でsqrtをしたいのでしょうか。



hspail

リンク

2009/3/26(Thu) 11:38:02|NO.24165

>失礼かもしれませんが、何故マシン語でsqrtをしたいのでしょうか。
いえいえ、
今ソフトを作っていて、計算する部分の速度が全然足りないのです。
なので、計算も交えてマシン語で処理しようと思っております。

sqrtだけだったら、マシン語とHSPでは速度変わらないのかな〜?



ほげ

リンク

2009/3/26(Thu) 13:21:16|NO.24171

sqrtだけマシン語化は無意味だと思うよ。
callfuncもそれなりに時間かかるし。

どんな処理をさせたいのかわからないから何とも言えないけど、
マシン語化するなら計算処理を全部まとめて、
計算結果も配列とかでまとめて取得すると速いよ。



じった

リンク

2009/3/26(Thu) 14:02:31|NO.24172

ダイナミックに高速化を図りたいなら、時には柔軟な発想が役に立つこともあります。

hspailさんの目的にかなうかどうか解りませんが、
  計算の元となる数値にある程度の制限がある場合、
   『あらかじめ計算した値を テーブルで持っておく』なんて手もありますよ。



check

リンク

2009/3/26(Thu) 14:28:26|NO.24176

平行根は俺たちもいくらか暗記しただろう
sqrt(2)だったら1.41421356とか



s

リンク

2009/3/26(Thu) 14:30:48|NO.24177

にやにやしにな・・・ですかw



SYAM

リンク

2009/3/26(Thu) 14:45:22|NO.24179

HSPは遅いもの……とだけ考えないで、HSPで作られたプログラムが、いつどこで遅くなっているのかを考えないといけません。

「HSPが遅い」というのは、いちいち命令を翻訳しながら実行していることがネックになっているわけですから、もし高速化したいならまず翻訳の回数を減らすことを考えるべきなのです。
HSPのsqrt命令を、C言語のsqrt()関数を呼び出すように置き換えても、翻訳する手間自体はほとんど変わっていません。
もし自作した C 側のプログラムを HSP自体を作ったのと同じ環境でコンパイルしていたなら、最終的に実行される機械語としては、実はまったく同じものを呼び出しているなんてことになってしまいます。
当然、全体の実行時間にはまったく影響しません。…というか、余計な手続きを踏んで遅くなってる可能性のほうが高いです。

仮に、速度的に優秀なライブラリをリンクしたDLLを用意して、sqrt()だけが速くなったとしても、元が一瞬の話ですからおそろしく小さな差にしかなりません。それが体感できるほどの回数だけsqrt()関数を使うのであれば、"sqrt()" や "loop"を翻訳するのにかかる時間ごとばっさりなくす方が、よほど大きいのです(ほげさんの仰ってることがそれです)。



hspail

リンク

2009/3/26(Thu) 19:12:26|NO.24183

皆さんありがとうございます。

>じったさん
範囲が広いのでテーブルはできないっぽいです。

今作っているプログラムはpsetで円を描くという物で、さらにそれを並べてブラシのように線を引くという物です。
そのプログラムにsqrtは必須です。

sqrtはループの中にあり、だいたい100~10000回ほどループさせるようになっています。
pget & pget も使っているので、それも速度低下の一因となっているようですが、
QueryPerformanceCounter等を駆使して計測してみたところ、sqrtと計算の部分も無視できませんでした。
(pset & pget の半分くらい時間がかかっている)

そこで計算部分だけでもマシン語で処理しようとしますが、小数を扱えず(引数&返値)困っています。
ポインタで何とかできるのかなーと思い、Cを勉強中です。


それと、pget & pset を peek & poke を使いVRAMに直接取得・書き込みする物に変えてみたのですが、
速度は同等または低下してしまいました。

HSP3になって、pget & pset は遅くなったというものをどこかで読みましたが、
これもマシン語でどうにかできないかな〜、なんて思っちゃってます。


他に高速化する手段はないでしょうか?

長文失礼しました。よろしくお願い致します。



GENKI

リンク

2009/3/26(Thu) 22:37:40|NO.24184

速度が気になるならいっそ描画処理までをCでプラグイン作ればいいんじゃないでしょうか。

> 今作っているプログラムはpsetで円を描くという物で、さらにそれを並べてブラシのように線を引くという物です。

bufferに1個だけ円書いてgcopyじゃダメなのかな。
この辺が関連かな…?
http://hspdev-wiki.net/?%BE%AE%A5%EF%A5%B6%2F%B2%E8%C1%FC%BD%E8%CD%FD%2F%A5%DA%A5%A4%A5%F3%A5%C8%A5%DA%A5%F3
http://hspdev-wiki.net/?Paint%2Fline



e

リンク

2009/3/26(Thu) 23:38:15|NO.24185

ったくスレ主は遅い遅いって文句をいうだけしか能がないのか。



hspail

リンク

2009/3/27(Fri) 08:47:49|NO.24188

>GENKIさん
ありがとうございます。
検討してみます。

>e
あなたおもしろい人ですねwww
文句なんて言ってませんよ。



k

リンク

2009/3/27(Fri) 10:23:45|NO.24189

>今作っているプログラムはpsetで円を描くという物で、さらにそれを並べてブラシのように線を引くという物です。
そのプログラムにsqrtは必須です。

確かにsqrtのテーブル化は難しいですが
SIN COS だったらテーブル化は容易なので
円関係のことは
SIN COS に置き換えるということを考えてみてもいいかもです



hspail

リンク

2009/3/27(Fri) 10:35:35|NO.24190

>kさん
ありがとうございます。
SINとCOSですね、がんばってみます。

それとループの中でatanも使っているのでテーブル化できるか考えてみます。
ですが問題は計算の元となる数値に小数が混じっているということですね。



SYAM

リンク

2009/3/27(Fri) 12:05:14|NO.24195

足しになるもんかわからないですが、もうひとつ。
もし、

 ●円の中心からの距離と半径の大小関係を比較

…をするような処理の場合、

 ●円の中心からの距離の2乗と、半径の2乗の大小関係を比較

…しても、もちろん結果が一緒です。
文字にするとなんだか後者の方が面倒そうに見えますが、実際に計算をしてみると…圧倒的に後者の方が速いですね。



通りすがり

リンク

2009/3/27(Fri) 12:44:09|NO.24198

昔の賢い人たちの考えたアルゴリズムを使えば、sqrt無しで円を描画できるらしいです
もちろん、三角関数も不要です
http://dencha.ojaru.jp/programs_07/pg_graphic_09a1.html



hspail

リンク

2009/3/27(Fri) 20:59:33|NO.24210

>通りすがりさん
ありがとうございます。難しそうですがやってみます。

>SYAMさん
ありがとうございます。
たとえば↓のような感じでしょうか(今作っているプログラムとは違います)
変わらないか少し遅くなっているようなんですが、私の解釈が間違っているのでしょうか?

#uselib "kernel32" #func QueryPFreq "QueryPerformanceFrequency" var #func QueryPCount "QueryPerformanceCounter" var dim lgint, 4 #define _start QueryPFreq nFreq : QueryPCount nBefore #define _goal QueryPCount lgint(2) : dwTime = strf("%%.3fmSec", 1000.*(lgint(2)-nBefore)/nFreq) #module // DrawCircle 位置X, 位置Y, 半径 #deffunc DrawCircle double mx, double my, double r sx = mx - r sy = my - r dx = mx + r dy = my + r x = sx y = sy repeat repeat if x > dx : break // 中心から何ピクセルはなれているか dis = sqrt( (x-mx)*(x-mx) + (y-my)*(y-my) ) // ↓↓↓ ここ ↓↓↓ ;if dis <= r { if (dis*dis) <= (r*r) { pset x, y } x++ loop if y > dy : break x = sx : y++ loop return #global _start DrawCircle 100, 100, 100 _goal dialog dwTime end
-----------------------------
あとはマシン語で小数の計算ができれば何とかなりそうなのですが、なかなか難しいです。

http://hsp.tv/play/pforum.php?mode=all&num=22638
↑でのYOYOさんの小数の戻り値を取得するモジュールを改良してどうにかならないかと研究中です。
YOYOさんのモジュールはDLLの関数用でcallfuncには対応していないみたいです。

以下YOYOさんの書き込み

YOYO 2007/7/27(Fri) 01:28:25|NO.9784 整数と小数では返り値を渡す仕組みが異なるのでプラグインかマシン語で取り出す仕組みが必要です。 強引ですが、関数の返した小数を取り出すモジュールを作ってみました。 関数が返す少数はdoubleとfloatどちらでも大丈夫です。 #module #uselib "kernel32.dll" #func VirtualProtect "VirtualProtect" int, int, int, int #deffunc getdouble if code == 0 { code=$0424448b,$04c218dd,$00000000 VirtualProtect varptr(code), length(code)*4, $40, varptr(res) fret=0.0 } prm = varptr(fret) res = callfunc(prm, varptr(code), 1) return fret #global BASS_ChannelBytes2Seconds handle,pos getdouble //getdoubleは小数を返す命令の直後に呼び出してください。 mes refdval



SYAM

リンク

2009/3/27(Fri) 22:04:49|NO.24211

r を 2 乗するのはループの外側で1度だけでいいですし、
半径の 2 乗というのは、 sqrt()関数に与えているものがそれです。

つまり、そもそもsqrt()関数を呼ぶ必要さえないってことです。



hspail

リンク

2009/3/28(Sat) 07:12:17|NO.24216

すみません、昨日寝る直前に気付きましたw
if ( (x-mx)*(x-mx) + (y-my)*(y-my) ) <= (r*r) {
この例だとあまり速くくならないみたいですね。

今作っているプログラムでは変数 dis を何回か使っているので
他の変数を2乗する手間があり、高速化は図れませんでした。

>GENKIさん
>速度が気になるならいっそ描画処理までをCでプラグイン作ればいいんじゃないでしょうか。
ということなのでプラグインの作り方も少しずつ学んでいこうと思います。


それとVRAMを直接いじくるとpset, pgetよりも高速に動作すると思っていたのですが、どうやら違うみたいですね。
(HSP開発wikiからコピーさせていただきました。ありがとうございます。)

#uselib "kernel32" #func QueryPFreq "QueryPerformanceFrequency" var #func QueryPCount "QueryPerformanceCounter" var dim lgint, 4 #define _start QueryPFreq nFreq : QueryPCount nBefore #define _goal QueryPCount lgint(2) : dwTime = strf("%%.3fmSec", 1000.*(lgint(2)-nBefore)/nFreq) randomize // pset _start redraw 0 repeat 10000 pset rnd(640), rnd(480) loop redraw 1 _goal time_pset = dwTime // VRAMにpoke _start mref vram, 66 repeat 10000 ; 描画先座標より、vramのindexを取得 ; ※vramの先頭が画面の左上からではなく、右下からB,G,R...となるので注意。 index = (ginfo(12) * ginfo(13)) - ( (rnd(480) + 1) * ginfo(12) - rnd(640)) ; vramのRGBを書き換え。 poke vram, index * 3, ginfo(18) // B要素 poke vram, index * 3 + 1, ginfo(17) // G要素 poke vram, index * 3 + 2, ginfo(16) // R要素 loop redraw _goal time_vram = dwTime dialog "PSET : "+time_pset+"\nVRAM : "+time_vram

さすがにペイントソフトやら何やらで、一本の線を描くのに何秒もかかっていたらうざいので
もう少しねばってみたいと思います。
もしよろしければヒントを下さるとありがたいです。
(特にpset, pgetの高速化、マシン語での小数の扱い方等)



ONION software Copyright 1997-2021(c) All rights reserved.