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


HSPTV!掲示板


未解決 解決 停止 削除要請

2012
0108
dekamegaDLLでの乱数文字列生成の高速化12解決


dekamega

リンク

2012/1/8(Sun) 11:05:43|NO.44188

最近C++(Vidual C++ 2010 Express)でDLLにチャレンジしだしたのですが、
思ったよりも速度が出ないので、
ベテランの皆様にアドバイスをいただきたく書きこませていただきます。

以下のような命令で
ランダムな16進数の文字列型配列をペアで作っているのですが、
自分の環境だと15〜16ミリ秒かかってしまいます。
単純な数値の処理だとDLLで10倍くらいの速度になるのですが、
似たような処理をHSP側で実装した場合と比べ、2倍程度の速度にしかなっていないようです。
何か無駄な処理、遠回りな処理をしている部分があれば、
教えていただけると助かります

static void MAKE_DEF_GINE( void ) { // MAKE_GINE 命令の処理 // var1 array_M, var2 array_F, int arrynum, int gine_length // 渡された配列array_F,array_Mにそれぞれarrynumの要素を確保し // 各要素にgine_length bitのランダム16進数文字列を作る // // PVal *pvalarray_M; pvalarray_M = code_getpval(); // 配列array_Mの取得 PVal *pvalarray_F; pvalarray_F = code_getpval(); // 配列array_Fの取得 int arrynum=code_getdi(1); //1次元要素数のarrynumの取得 int gine_length=code_getdi(1); //初期化する値の取得 if (arrynum<=0) puterror( HSPERR_DIVIDED_BY_ZERO ); // 0以下ならばエラー(要素数0なので) if (gine_length<=0) puterror( HSPERR_DIVIDED_BY_ZERO ); // 0以下ならばエラー(要素数0なので) exinfo->HspFunc_dim( pvalarray_M, HSPVAR_FLAG_STR, 0, arrynum, 0, 0, 0 );//配列array_Mの要素を初期化 exinfo->HspFunc_dim( pvalarray_F, HSPVAR_FLAG_STR, 0, arrynum, 0, 0, 0 );//配列array_Fの要素を初期化 HspVarProc *vpSTR; //代入のためにHspVarProc構造体を用意 vpSTR = exinfo->HspFunc_getproc( HSPVAR_FLAG_STR ); srand((unsigned)time(NULL));//乱数初期化 for( int i = 0; i < arrynum; i ++ ) { char* ginestr1 = (char*)hspmalloc( (gine_length + 1) * sizeof(char) );//作業用の文字列メモリ確保 char* ginestr2 = (char*)hspmalloc( (gine_length + 1) * sizeof(char) );//作業用の文字列メモリ確保 for ( int ii = 0; ii < gine_length/4; ii ++ ) {//4文字づつランダム文字列生成 strcat(ginestr1,ToSTR(rand()%65536,4));//4文字づつ加算 strcat(ginestr2,ToSTR(rand()%65536,4));//4文字づつ加算 } for ( int ii = 0; ii < gine_length%4; ii ++ ) {//1文字づつランダム文字列生成 strcat(ginestr1,ToSTR(rand()%16,1));//1文字づつ加算 strcat(ginestr2,ToSTR(rand()%16,1));//1文字づつ加算 } pvalarray_M->offset = i;//オフセットiを指定 vpSTR->Set( pvalarray_M,vpSTR->GetPtr(pvalarray_M), ginestr1);//pvalarray_Mのオフセットiにginestr1を書きこむ hspfree( ginestr1 );//作業用メモリ解放 pvalarray_F->offset = i;//オフセットiを指定 vpSTR->Set( pvalarray_F,vpSTR->GetPtr(pvalarray_F), ginestr2);//pvalarray_Fのオフセットiにginestr2を書きこむ hspfree( ginestr2 );//作業用メモリ解放 } //) } //整数value1をdigit1ケタの16進数文字に変換 char* ToSTR(const int value1,const int digit1) { char a[256] ={'\0'};//文字列初期化 sprintf(a,"%0*x",digit1,value1); char* b= a;//操作した文字列のポインタ取得 return (b); }



この記事に返信する


dekamega

リンク

2012/1/8(Sun) 11:08:16|NO.44189

>>自分の環境だと15〜16ミリ秒かかってしまいます。

上記はarrynum=32 gine_length=1024の時の処理速度になります



レノス

リンク

2012/1/8(Sun) 17:23:19|NO.44201

ベテランではないですし速度のこともよくわからないですが、、

作業バッファ ginestr1, 2 をループのごとに確保・解放する必要はないと思います。
hspmalloc, hspfree は for の外に出して、ループの最初に ginestr1,2 を空 ([0] = '\0') にした方が速そうです。

2^N の剰余 ( % 16 や % 65536 ) は (2^N - 1) の & ( & 15 や & 65535 ) に同値ですが、
そちらの方が微妙に速いです (これはコンパイラが既にやってそうですが)。

あと、ToSTR 関数がローカル変数 a へのポインタを返却しているのが気になります。
strcat が ToSTR() の値を受け取る時点で a は既に破棄されている、かもしれませんし。



MillkeyWeler

リンク

2012/1/8(Sun) 19:57:14|NO.44209

処理速度的に、sprintf を他の関数に変えてみよう。
ベンチマークするとわかると思うけど、sprintf は結構時間食いますので。



ザーメン

リンク

2012/1/9(Mon) 05:47:21|NO.44216

乱数生成機をメルセンヌツイスターやxor128に変えてみては?



check

リンク

2012/1/9(Mon) 13:10:00|NO.44221

いや、最近のCRTのrandは昔の様な線分乱数じゃなくて、
もっと高度なアルゴリズムを採用しているらしいぞ。
不安ならxorshiftあたりに変えてもいいと思うけどな。

そしてレノス氏が指摘しているが、これは本来ならまともに動作しないのではないか?
ローカル変数のポインタを返したところで、それは関数を抜けたら無効になっている可能性が非常に高い。



dekamega

リンク

2012/1/9(Mon) 13:15:24|NO.44224

皆様色々なアドバイスいただきほんとうにありがとうございました。

>>レノスさん
mallocとかの考え方がとりよくわかっていなかったので参考になりました
HSPではあまり意識しない考え方だったのですが、速度を意識するときには重要ですね

>>MillkeyWelerさん
どうやらsprintf がボトルネックだったようです
よく理解していなかったchar型について勉強を深める機会になりました
数値を自前で16で割ったり余りをだして、1つづつ書きこんでいく形で解決が出来ました

>>ザーメンさん
この処理を高速で多数回実行する必要があったので、
乱数の初期化もかなりもんだいになっていたので助かりました。
DLL側での実装は自分にはできなかったのですが、
HSP側で実数乱数を作って、DLLにSeed値として渡すことで問題が解決しました。

みなさんのアドバイスを参考にした結果、
平均実行速度が1ミリ秒以下になり、劇的な改善となりました。
重ねてお礼申し上げます



dekamega

リンク

2012/1/9(Mon) 13:17:10|NO.44226

>>checkさん

そうなんでしょうか??
実行した感じ、意図したとおり動いているように思えます
乱数文字列をHSP側で受け取って、sqliteでDBに登録できているのですが・・・



dekamega

リンク

2012/1/9(Mon) 13:20:09|NO.44227

ついでといってはあれなのですが、
C++では関数で作った文字列を返すときは
どのような手順で行うのが一般的なのでしょうか



check

リンク

2012/1/9(Mon) 13:34:02|NO.44228

こうか。

#include <string.h> #define SAMPLE_STRING "サンプル文字ですよ" // buf : 文字列を受け取るバッファ, size : バッファのサイズ int getstring(char* buf, int size) { if (buf == NULL) return strlen(SAMPLE_STRING); if (size < strlen(SAMPLE_STRING)) return -1; strncpy(buf, SAMPLE_STRING, size); return 0; }

あらかじめ必要量を確保したバッファを関数に渡して、
そのバッファに書き込む、というのが一般的だな。
必要なバッファのサイズを取得するにはbufにNULLを指定すればいい。

もしくはstd::stringを使うという手もあるな。



MillkeyWeler

リンク

2012/1/9(Mon) 14:03:39|NO.44229

基本的には、文字列配列を扱ったことなかったけど、スレ主さんと同じかなー。

つーか、DLL 自体が関数なんだが。
DLL 内部に対しての関数なのか?
それとも、DLL から HSP への返却なのか?



レノス

リンク

2012/1/9(Mon) 16:18:32|NO.44230

> 実行した感じ、意図したとおり動いているように思えます
解放されても、変数の領域はしばらくは破壊されないので、
幸運(不運?)なことに意図した通りの動作だったのでしょう (メモリバグの特徴)。
しかしそうならない可能性も十分あるので、修正すべきです。

std::string 返した方が楽ですが、やや重い(かもしれない)ので、今は不適。

> if (size < strlen(SAMPLE_STRING))
ここは <=



dekamega

リンク

2012/1/12(Thu) 16:50:50|NO.44254

皆さんありがとうございました。
Char型についてしばらく自分でも勉強してきました。
HSPとはちがって、メモリを予め確保、とか
コピーしたら末尾に'\0'入れるとか、
文字列中の特定のindexをポインタで指定とか
C++の文脈のようなものをちょっとづつですが理解できたきたような気がします。
まだコード書くたびに1行づつエラーが出てしまいますが
なんとか作っていけるメドがつきました。
質問したコードを含む処理コードが、以前は全体で1ループ223msかかっていたのですが、
現在は60msくらいになりました。
処理系の1tick毎にこのコードを5万回程度ループさせる必要があるので、
現在は10msを切れるよう、HSP側のコードをDLLに移植中です。
500,000ms=500秒なので、1tick10分を切れると、
自分がやりたいことがかなり現実的になってくる感じです。



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