実行速度の改善方法No.03898
Kamonohasi さん 03/10/23 15:40
 
始めまして、CountSpencerと申します。

C/Migemo<http://www.kaoriya.net/>等を利用して日本語インクリメンタルサーチを
行うマクロ
を作ったのですが、実行速度が馬鹿みたいに遅いんです…汗
まだ秀丸マクロを始めたばかりで非効率なコードを書いているせいだと
思うんですが(一行ずつ切り出してる処理がまずいとは思うのですが)、
いろんなところで検索したのですが、うまい方法を見つけることが
出来ませんでした。
マクロはまだベータですのでライブラリに登録しておりませんが、
http://members.jcom.home.ne.jp/c-spencer/Program/M_For_H.zip
にアップしました。
御教授願えませんでしょうか。よろしくお願い致します。

CountSpencer<xxxxxxxxx@jcom.home.ne.jp>

[ ]
RE:03898 実行速度の改善方法No.03899
encodingshiftjis さん 03/10/23 22:42
 
これだけ細かい操作ができるのなら、各操作単位の
実行時間を計測できませんか。
← インクリメンタルサーチをユニットテストするマクロ
などを書いて。

[ ]
RE:03899 実行速度の改善方法No.03900
encodingshiftjis さん 03/10/23 22:50
 
インクリメンタルサーチを使わずに検索文字列を短くするには。

その分野のテキストでの最小文字数を体験で得ておく
英字&かな:3〜5文字
漢字:2〜3文字

この程度を入力してすぐ検索実行(再検索)でほとんどの単語は検索できます。

[ ]
RE:03899 実行速度の改善方法No.03902
Kamonohasi さん 03/10/24 23:13
 
>これだけ細かい操作ができるのなら、各操作単位の
>実行時間を計測できませんか。
>← インクリメンタルサーチをユニットテストするマクロ
>などを書いて。
言われてみるまで秀丸マクロでmessageとinsert以外でデバッグとか
テストなんてすっかり失念しておりました。
実行速度はよく調べもしないでDLLからtimeGetTimeとか呼ばなきゃいけない
のかなぁってやはとちりしておりましたが、tickcountなんてそのもの
ずばりがあったんですねぇ。すみませんでした。
計測の結果処理の15%が一行ずつの切り出し、80%がDLLを呼び出しての検索、
残りがハイライト等の雑多な処理に使われていました。
一番の障害はDLLなのですが、そちらの方は二三試してみたいことはあるのですが、
依然としてマクロだけの処理に数秒かかっているんです。
そこでお聞きしたいのですが、
・行頭からnバイト目からmバイトの長さの文字列を選択する
って処理を皆さんならどう書きます?
いまはrightを使って1文字ずつ動いてるんですが…。

[ ]
RE:03900 実行速度の改善方法No.03903
Kamonohasi さん 03/10/24 23:20
 
>英字&かな:3〜5文字
>漢字:2〜3文字
現在のところ二文字目が入力された段階で検索をかけていて、マクロのところに
最小検索開始文字数の設定ができるようにしてみたのですが、
これって、確かに検索開始から終了までの総実行時間は短くなるのですが、
下方検索なんで、途中に候補が見つかる場合なんかは、ユーザが入力し終わってから
候補にたどり着く(or存在しないことがわかる)までの時間がかえって長く
なる場合もあるんです。落としどころってどの辺なんでしょうかね?

[ ]
RE:03902 実行速度の改善方法No.03904
山紫水明 さん 03/10/25 09:06
 
 Kamonohasiさん,こんにちは。

》・行頭からnバイト目からmバイトの長さの文字列を選択する
》って処理を皆さんならどう書きます?
》いまはrightを使って1文字ずつ動いてるんですが…。

 この部分だけに反応します,というかこの部分だけしかできません(^^;
 1行文字数が一定の長さを越えないという条件つきですが,一時的に桁折りを
長くして,実行後元に戻すという方法もあるようです。
 ただし,どれだけスピードアップになるかについては検証していません。

#w = width;
config "w1000";
$s = gettext( n, y, n + m, y );
message $s;
config "w" + str(#w);

     では, (^^)/~
                                        山紫水明

[ ]
RE:03898 実行速度の改善方法No.03905
あべのり さん 03/10/25 20:26
 

>C/Migemo<http://www.kaoriya.net/>等を利用して日本語インクリメンタルサーチを
>行うマクロ
>を作ったのですが、実行速度が馬鹿みたいに遅いんです…汗
面白いマクロですね。

>まだ秀丸マクロを始めたばかりで非効率なコードを書いているせいだと
>思うんですが(一行ずつ切り出してる処理がまずいとは思うのですが)、
>いろんなところで検索したのですが、うまい方法を見つけることが
>出来ませんでした。
>マクロはまだベータですのでライブラリに登録しておりませんが、
>http://members.jcom.home.ne.jp/c-spencer/Program/M_For_H.zip
>にアップしました。

とりあえずざーっとソースを見てみました。気になった点が二つ。
1.MatchEx内でいちいちMigemoによる正規表現の生成とその正規表現のコンパイル
を行っている。
2.正規表現検索にBREGEXP.DLLを用いてる。
1.に関しては検索文字列が変わるわけではないので同じ検索文字列で検索する時は
一回すればいいと思います。2.に関してはBREGEXPがJRE32より遅いと聞いたことが
ある気がするので……(ホントかどうかは調べたことがないのですが。)

先ほどちょっとやってみたのですが、上を改良すると少しは早くなった気がしました。
多分……

[ ]
RE:03904 実行速度の改善方法No.03906
Kamonohasi さん 03/10/25 23:29
 
山紫水明さんありがとうございます。

>#w = width;
>config "w1000";
>$s = gettext( n, y, n + m, y );
>message $s;
>config "w" + str(#w);
おお、gettextが2バイト文字を二文字と認識しているのを
すっかり失念しておりました。
rightを使ってちまちま動くより早くできそうです。
ありがとうございます。

[ ]
RE:03905 実行速度の改善方法No.03907
Kamonohasi さん 03/10/25 23:31
 
>面白いマクロですね。
ありがとうございます。

>1.MatchEx内でいちいちMigemoによる正規表現の生成とその正規表現のコンパイル
>を行っている。
あ、申し訳ありません。書いていませんでしたが、ここは気づいていてバッファリン
グを試みているんですが、どうも不安定になっちゃうんですよねぇ。
MathchExの方でやるとうまくいくのにMatchの方でやるとたまにおかしな
ことをしてくれちゃって…(汗
もともとC++の手習いのために作り出したので現在試行錯誤の途中です。
もしよろしければ、そこのコードを教えて頂けませんでしょうか?

>2.正規表現検索にBREGEXP.DLLを用いてる。
JRE32は秀丸から呼び出すにしろ、括弧等が多い複雑な正規表現を渡すとどうもエ
ラーになってしまうらしいんです。
秀丸から呼び出した場合は少なくともある程度以上に長い正規表現を渡すと括弧が多
すぎって怒られてしまいました。
数文字溜めてから検索をかければパターンは短くできますが、
C/Migemo用の辞書を編集したり、短い単語を検索したい時にエラーってのは困りもの
です。
私も最初は外部DLL呼ばずに、searchdownでやろうとしたのですが、
正規表現が大きくなりすぎて、変数の上限をオーバーしたり、括弧の問題で怒られた
りで、
現在の形になりました。
BREGEXP.DLLを田楽から呼ばずに直接呼べば多少違うのかも知れませんが……
ちょっとまだ敷居が高いのと、効果がどの程度か?ってことなんです。

一行ずつ切って検索する以外に良い方法無いでしょうかねぇ……。
一時ファイルに書き出して検索ってのも考えたのですが、ファイルサイズが大きくて、
しかも、カーソルから近いところに候補があるとロスがバカにならないんです。

[ ]
RE:03907 実行速度の改善方法No.03908
Kamonohasi さん 03/10/25 23:47
 
うっ。あべのりさん申し訳ありません、書いてる途中で間違えて投稿してしまいまし
た。
書いてる途中で、Matchでの同一検索文字列に対する正規表現の生成を抑制するとこ
ろで、
自分のミスに気づきました。
馬鹿なことに初回の呼び出し時に退避変数にコピーしただけであのソースで言うとこ
ろの"result"
にパターンを入れていませんでした(汗
マクロの方で改行のみの行は飛ばす処理をしていたので、候補の一番目が空行でない
最初の行にある場合だけ
検索から漏れるなんてへんな状態になって混乱しておりました。
ただ、もしよろしければあべのりさんがどの様なコードを書いたのか教えて頂けませ
んでしょうか。
この掲示板かメール<xxxxxxxxx@jcom.home.ne.jp>にお願いします。
よろしくお願いします

[ ]
RE:03908 実行速度の改善方法No.03909
あべのり さん 03/10/26 01:11
 
> >2.正規表現検索にBREGEXP.DLLを用いてる。
本当にこれが原因になるかどうか知らないのであんまり固執しても仕方ないかもしれ
ませんが……

> JRE32は秀丸から呼び出すにしろ、括弧等が多い複雑な正規表現を渡すとどうもエ
>ラーになってしまうらしいんです。
これはJRE32の方なのか秀丸自体の問題なのかどっちなんでしょうねぇ〜。僕のDLLは
JRE32(ってかHMJRE)を直接叩いているので秀丸の方なら問題ないのですが……

> 一行ずつ切って検索する以外に良い方法無いでしょうかねぇ……。
手風琴さんという方が他の方法を考えていらっしゃいます。
http://hpcgi2.nifty.com/masema/bbs2/wforum.cgi
に書き込みがあります。

> 一時ファイルに書き出して検索ってのも考えたのですが、ファイルサイズが大きく
>て、
> しかも、カーソルから近いところに候補があるとロスがバカにならないんです。
難しいですねぇ。結局上の方法が平均的には最も早い気もしますが。やってみないと
わからないな。

他の方法としていったん全てDLL内のメモリに読み込ませる、という方法も考えてみ
たのですが、弱点は上と同じですね。開始直後に全ての文字列を読み込む時のロスが
大きすぎる……。

>ただ、もしよろしければあべのりさんがどの様なコードを書いたのか教えて頂けま
>せんでしょうか。
>この掲示板かメール<xxxxxxxxx@jcom.home.ne.jp>にお願いします。
>よろしくお願いします

↓です。ちょっと恥ずかしい(笑)
http://www.ms.u-tokyo.ac.jp/~abenori/tmp/migemosearch.zip
main.cpp以外は以前書いた者の流用で無駄が大分あると思います。ので、多分もうち
ょっと早くなるはず……。開発にはVC++ .NETを使いましたので、他のコンパイラで
はそのままではコンパイルが通らないかもしれません。

[ ]
RE:03909 実行速度の改善方法No.03910
Kamonohasi さん 03/10/26 16:49
 
>手風琴さんという方が他の方法を考えていらっしゃいます。
おお…。
似たような時期に似たようなことを考えるってあるんですねぇ偶然ってすごい! と、
びっくりしました。
手風琴さんの手法の方が正しいアプローチだと思いますが…。
私の手に負えない(笑)

>↓です。ちょっと恥ずかしい(笑)
すごい!……。実行時間が半分から三分の一に減ってる…。
しかも、"const_cast"は全く思いつかなかった。
なんでこんなことって思ってよくよく調べたら、私の状態じゃあ"dict"ディレクトリ
がないところで検索ができない(汗
"wsprintf"なんかも本を読んだときはいまいち使い道をイメージできなかったんです
が、こういうときに使えば楽なんですねぇ。
>main.cpp以外は以前書いた者の流用で無駄が大分あると思います。ので、多分もう
>ちょっと早くなるはず……。
開発にはVC++ .NETを使いましたので、他のコンパイラではそのままではコンパイル
が通らないかもしれません。
hmjreなんて失礼ながら存在をしりませんでした。
hmjre.dllを直接呼び出すバージョンを書いてみます。
できあがったらまたこちらで報告させていただきたいと思います。
当方のはBCC 5.6でコンパイル確認しかできません (汗

すごい勉強になります。本当にありがとうございます。

[ ]
RE:03910 実行速度の改善方法No.03911
あべのり さん 03/10/26 17:04
 
>手風琴さんの手法の方が正しいアプローチだと思いますが…。
>私の手に負えない(笑)
実現したら多分遙かによく動きそうですよね。
でもどうすればいいのかさっぱり……

>しかも、"const_cast"は全く思いつかなかった。
因みに関数の宣言にconstはいらないみたいです。例えば
int func(char *str);
みたいな関数も秀丸から呼び出せる模様。

>hmjreなんて失礼ながら存在をしりませんでした。
>hmjre.dllを直接呼び出すバージョンを書いてみます。
今度出る秀丸4.00では標準で使用されるらしいです。JRE32の上位互換となっており、
呼び出す際にはJRE32と思って書けばいいみたいです。

>できあがったらまたこちらで報告させていただきたいと思います。
楽しみにしてます♪

[ ]
RE:03911 実行速度の改善方法No.03912
手風琴 さん 03/10/26 17:49
 
どうも、手風琴です。
(なんだか恐縮です。)

>>手風琴さんの手法の方が正しいアプローチだと思いますが…。
>>私の手に負えない(笑)
>実現したら多分遙かによく動きそうですよね。
>でもどうすればいいのかさっぱり……
>

自分で読み返してみてもちょとわかりにくい文章でしたね。しかも誤字があるし。
改めて説明しますと、Megemoの正規表現は A|B|C|D・・・ みたいに|の連続に
なってますので、これを|のところで切ってAとBとCとDに分解して1個ずつ検索を
かけるという事です。
もちろん切る作業はDLL内で行ないます。

んで、実際の検索処理は下のマクロのような感じで行ないます。
参考になれば幸いです。


##n = dllfunc("MegemoQuery", $$query);
##hittopx = 0x7FFFFFFF;
##hittopy = 0x7FFFFFFF;
##hitendx = 0x7FFFFFFF;
##hitendy = 0x7FFFFFFF;
// カーソル位置からファイル終端までを検索範囲に設定
##x = x;
##y = y;
gofileend;
beginsel;
moveto ##x, ##y;
endsel;

while (1) {
    // GetQueryはMegemoQueryで生成した正規表現を8KB以下にカットして返す。
    $$regular = dllfunc("GetQuery");
    if ($$regular == "") break;
    searchdown2, $$regular, regular, inselect;
    if (!result) break;
    // ヒットした位置が前回のヒットした位置より手前なら、その位置をヒッ
    ト位置にする。
    if ((seltopx < ##hittopx && seltopy == ##hittopy) || (seltopy < ##
    hittopy)) {
        ##hittopx = seltopx;
        ##hittopy = seltopy;
        ##hitendx = selendx;
        ##hitendy = selendy;
    }
    // 2回目からはヒット位置までを検索すればよい
    moveto ##hittopx, ##hittopy;
    beginsel;
    moveto ##x, ##y;
    endsel;
}
if (##hittopx != 0x7FFFFFFF) {
    moveto ##hittopx, ##hittopy;
    beginsel;
    moveto ##hitendx, ##hitendy;
    endsel;
}

[ ]
RE:03903 実行速度の改善方法No.03913
encodingshiftjis さん 03/10/26 21:06
 
高機能なマクロで一発でし止めるのも快感ですが
単純なキー操作を機関銃のように繰り出すのもエディタの快感です。

プラクティス(現場のやり方)とソフト機能の役割分担の
バランス設計は難しい。人それぞれのテイストがありますから。

findword; // その他、コマンド一覧、検索系、単語の検索...
とか
getsearch; // 選択範囲またはカーソル位置の単語
localgrep searchbuffer; // タグジャンプの元を作る

でも味わいはあります。

[ ]
RE:03911 実行速度の改善方法No.03915
Kamonohasi さん 03/10/26 22:18
 
すみません!
一つ上の取り消した投稿、アドレスを間違えてました(汗

>因みに関数の宣言にconstはいらないみたいです。例えば
>int func(char *str);
>みたいな関数も秀丸から呼び出せる模様。
おお。ほんとだ…。

>今度出る秀丸4.00では標準で使用されるらしいです。JRE32の上位互換となっており、
>呼び出す際にはJRE32と思って書けばいいみたいです。
そうでしたか。ここしばらく秀丸のチェックを怠ったつけがこんなところに…。

hmjreのJre2Closeを呼び出すのを遅らせた場合どういう
副作用があるのかよくわかりませんがとりあえず呼び出せました。
ほとんどあべのりさんのやつを書き換えただけですが、
とりあえず、できました。
http://members.jcom.home.ne.jp/c-spencer/Program/M_For_H_(1.10b).zipです。
特にマクロ部分なんてもう原形をとどめていません(汗

[ ]
RE:03912 実行速度の改善方法No.03916
Kamonohasi さん 03/10/26 23:45
 
手風琴さんこんばんは。よろしくお願いします。

試しに無い知恵を絞ってGetQuery部分の処理の方を考えたのですが…
こんなのどうでしょうか?(わかりにくくて申し訳ありません)
/************************************************/
一文字ずつ切り出すのが基本の場合?

・前後のグルーピングの "(" と ")" を取り除く

・括弧()[]が現れるまでは"|"と候補の連続
・[括弧ならクラスなので、中に()は存在せず、[]も無いので、次の]まで連続

もし、 (括弧なら対応する)括弧まで切り出す
切り出せなくなるまで再帰
 ・一つ前の"|"までの文字をセットにして「接頭文字列」に追加する。これには"("
をつける
 ・何回目の再帰か、カウントする
・切り出せなくなったらネストの回数だけ)括弧を付けて返す

この時点でグルーピングは簡略化されているので、
もし秀丸の限界値を越えていなければこれが、"GetQuery"の戻り値に出来る
もし、越えていた場合は、[]の中身か"|"で区切られた部分をいくつかに分割して
元のに挿入した項をを分割した個数だけ作る。
/************************************************/
ちょっと無駄に候補の正規表現が増えることが容易に予測できますが(汗)

[ ]
RE:03916 実行速度の改善方法No.03917
手風琴 さん 03/10/27 02:23
 
手風琴です。こちらこそよろしくお願いします。


えーと、文章で説明するのは苦手なので実際のコードを貼り付けます。

テストしたところ()や[]の中の最長は716バイトだったので、()や[]の中が8KBを
超えた場合の事は考慮していません。
辞書のバージョンアップによって長くなる可能性はありますが、8KBを超えるほ
ど極端に伸びることはないと思われるのでこれで十分だと判断しました。



// queryを8KB以下の部分文字列に切り出す。
// 引数にNULLを渡すと次の部分文字列を取得できる。
// 切り出せる部分文字列がなくなったらNULLを返す。
char* CutQuery(char *query)
{
    static unsigned char *p;
    char   *start;
    int    openParent = 0;     // 開いている(の数
    int    openBracket = 0;    // 開いている[の数
    char   *cutPos = NULL, *prevCutPos = NULL;

    if (query) {
        // 全体をくくる()を取り除く
        p = query + 1;
        *(p + strlen(p) - 1) = '\0';
    }

    if (!*p) return NULL;

    for (start = p; *p; ++p) {
        if ((0x81 <= *p && *p <= 0x9f)
         || (0xe0 <= *p && *p <= 0xfc)) {
                ++p;
        }
        else if (*p == '(') ++openParent;
        else if (*p == ')') --openParent;
        else if (*p == '[') ++openBracket;
        else if (*p == ']') --openBracket;
        else if (*p == '|' && !openParent && !openBracket) {
            prevCutPos = cutPos;
            cutPos = p;
            if (cutPos - start > 8192) {
                cutPos = prevCutPos;
                break;
            }
            else if (cutPos - start == 8192) {
                break;
            }
        }
    }

    if (*p) {
        *cutPos = '\0';
        p = cutPos + 1;
    }

    return start;
}

[ ]