XREA.COM Logo XREA.COM Ad

旧雑記サルベージ

97管理人 ★:06/09/06 17:43 ID:???
2006年02月02日
■AutoHotkeyスクリプトの高速化
SendMessageには、コマンドで送る方法とDllCallで送る方法がある。
どちらが速いのか。
コマンドの方では、「ahk_id %hwnd%」の解釈が行われるが、遅くならないのか?
以下のスクリプトで試してみた。
Process,Exist
pid:=ErrorLevel
DetectHiddenWindows,On
WinGet,myhwnd,id,ahk_pid %pid% ahk_class AutoHotkey
SetBatchLines,-1

tc:=A_TickCount
Loop,20000{
    DllCall("SendMessage",UInt,myhwnd,UInt,0x0,UInt,0,UInt,0,UInt)
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,20000{
    SendMessage,0,0,0,,ahk_id %myhwnd%
}
l2:=A_TickCount-tc

結果は、1079対172でコマンドの方が圧倒的に速かった。

次に、コマンドの方でLastFoundWindowを利用したらどうなるかを調べた。
tc:=A_TickCount
Loop,200000{
    SendMessage,0,0,0,,ahk_id %myhwnd%
}
l1:=A_TickCount-tc
DetectHiddenWindows,On
IfWinExist,ahk_id %myhwnd%
{
DetectHiddenWindows,Off
    tc:=A_TickCount
    Loop,200000{
        SendMessage,0,0,0
    }
    l2:=A_TickCount-tc
}
234対125で、LastFoundWindowを使った方が高速なことがわかった。

しかし、わざわざIfWinExistを1行増やすと、その分遅くなるはずである。
以下のようにして、内側で何回ウィンドウ操作を行ったら、IfWinExistを使った分の元を取れるかを計測した。
tc:=A_TickCount
Loop,100000{
    SendMessage,0,0,0,,ahk_id %myhwnd%
    SendMessage,0,0,0,,ahk_id %myhwnd%
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,100000{
    IfWinExist,ahk_id %myhwnd%
    {
        SendMessage,0,0,0
        SendMessage,0,0,0
    }
}
l2:=A_TickCount-tc
内側が1回の時、828対859で、IfWinExistを使った方が遅くなった。
しかし、2回にすると、1578対1375でIfWinExistを使った方が速くなる。

次に、DllCallの気になる部分を調べる。
引数の型は「Int」のようにするのと「"Int"」のようにするのがあるが、どっちが速いか。
tc:=A_TickCount
Loop,20000{
    DllCall("SendMessage",UInt,myhwnd,UInt,0x0,UInt,0,UInt,0,UInt)
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,20000{
    DllCall("SendMessage","UInt",myhwnd,"UInt",0x0,"UInt",0,"UInt",0,"UInt")
}
l2:=A_TickCount-tc
1047対296で、文字列として渡した方が圧倒的に速くなる。

次に、モジュール名を省略しなかったらどうなるか。
tc:=A_TickCount
Loop,100000{
    DllCall("SendMessage","UInt",myhwnd,"UInt",0x0,"UInt",0,"UInt",0,"UInt")
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,100000{
    DllCall("User32.dll\SendMessageA","UInt",myhwnd,"UInt",0x0,"UInt",0,"UInt",0,"UInt")
}
l2:=A_TickCount-tc
1500対1360で、できるだけ詳しく指定した方がほんの少し速くなる。

返り値を得ないとき、最後の返り値の型指定を省略したらどうなるか。
tc:=A_TickCount
Loop,100000{
    DllCall("User32.dll\SendMessageA","UInt",myhwnd,"UInt",0x0,"UInt",0,"UInt",0,"UInt")
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,100000{
    DllCall("User32.dll\SendMessageA","UInt",myhwnd,"UInt",0x0,"UInt",0,"UInt",0)
}
l2:=A_TickCount-tc
1470対1359で、返り値の型を省略した方がほんの少し速かった。

高速化したDllCallと、SendMessageコマンドで、返り値を変数に格納する速度を計測する。
tc:=A_TickCount
Loop,10000{
    r:=DllCall("User32.dll\SendMessageA","UInt",myhwnd,"UInt",0x0,"UInt",0,"UInt",0)
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,10000{
    SendMessage,0,0,0,,ahk_id %myhwnd%
    r=%ErrorLevel%
}
l2:=A_TickCount-tc
141対94で、やっぱりコマンドの方が速かった。

「var2:=var1」による代入と「var2=%var1%」による代入ではどっちが速いか。
tc:=A_TickCount
Loop,100000{
    SendMessage,0,0,0,,ahk_id %myhwnd%
    r=%ErrorLevel%
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,100000{
    SendMessage,0,0,0,,ahk_id %myhwnd%
    r:=ErrorLevel
}
l2:=A_TickCount-tc
875対860で、:=の方がごくわずかに速かった。

定数を代入する場合はどうか。
tc:=A_TickCount
Loop,1000000{
    r=100
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,1000000{
    r:=100
}
l2:=A_TickCount-tc
391対390で、ほとんど変わらない。

加算する場合はどうか。
r=0
tc:=A_TickCount
Loop,1000000{
    r+=1
}
l1:=A_TickCount-tc
r=0
tc:=A_TickCount
Loop,1000000{
    r:=r+1
}
l2:=A_TickCount-tc
937対1188で、「+=」の方が速い。

変数をいくつも足すような場合はどうか。
x=200
tc:=A_TickCount
Loop,100000{
    r=100
    r+=%x%
    r+=%x%
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,100000{
    r:=100+x+x
}
l2:=A_TickCount-tc
足し算の場合、1回の時は985対1015で「:=」の方が遅かった。
しかし、2回足した場合、1547対1188で「:=」の方が速い。

文字列の連結はどうか。
VarSetCapacity(r,100000)
r=
tc:=A_TickCount
Loop,100000{
    r=%r%x
}
l1:=A_TickCount-tc
VarSetCapacity(r,100000)
r=
tc:=A_TickCount
Loop,100000{
    r:=r . "x"
}
l2:=A_TickCount-tc
47対13312で「=」の方が圧倒的に速い。

複雑な式の場合はどうか。
x=200
y=400
z=10
tc:=A_TickCount
Loop,100000{
    r=100
    r+=%x%
    r*=%y%
    r/=%z%
}
l1:=A_TickCount-tc
tc:=A_TickCount
Loop,100000{
    r:=(100+x)*y/z
}
l2:=A_TickCount-tc
266対468で、やはり「:=」は遅かった。

まとめると、
・SendMessageはDllCallでやるよりコマンドでやった方が速い
・同じウィンドウを2回以上操作する場合、毎回「ahk_id %hwnd%」で指定するより、IfWinExistを使ってLastFoundWindowに保持されるようにした方が速い
・DllCallは引数や返り値の型指定を「"」で囲んだ方が速い
・DllCallは関数名の後の「A」やモジュール名を省略しない方が若干速い
・返り値がIntやBoolだったり不要な場合は、返り値の型を省略した方が速い
・変数に変数を代入する場合、「var2=%var1%」による代入より「var2:=var1」による代入の方が速い
・変数に定数を加算する場合、「:=var+1」より「var+=1」の方が速い
・変数に定数+変数を代入する場合、「var=100」「var+=%x%」のようにする方が速い
・定数と2つ以上の変数を足す場合、「var:=100+x+y」の方が速い
・文字列の連結は「var:=var . "str"」より「var=%var%str」とする方が速い
・括弧などを使った複雑な式の場合、「:=式」とするより「var*=」等を使った方が速くなるときがある
131KB
名前: E-mail:
ファイル:
0ch BBS 2005-10-08