条件分岐最適化
www.initialt.org/takehiro-switch-case.PDF
xが0、1、2、3のどれかのとき
3のとき
2のとき
0か1のとき
で条件分岐させるときに
If x = 3 Then
ElseIf x = 2 Then
Else
End If
↑これが早いのか
↓今までどおり(昔ながら)のこれが早いのか
If x < 2 Then
ElseIf x = 2 Then
Else
End If
いまどきのCPUなら上のほうが早いっていう結果になっているけど
時間計測に使った関数は
GetTickCount
QueryPerformanceCounter
QueryPerformanceFrequency
Queryが付く関数はGetTickCount関数より正確らしい?
けど調べてもよくわからなかった
計算途中でCPUの周波数が変化しないように電源の管理で固定するように設定して
最初は最低クロックの0.8GHzに固定して計測
電源の管理画面は
コン
トロールパネル→システムとセキュリティ→電源オプション→プラン設定の変更→詳細な電源設定の変更→プロセッサの電源管理
百万回判定するのにかかった時間
GetTickCount関数で取得した値はミリ秒単位らしい
単位はよくわからないけどIfでの条件分岐は昔ながらの方が早いのがわかる
何回か試してもだいたい同じ結果になる
同じく百万回を
QueryPerformanceCounterとQueryPerformanceFrequencyを使って計測
開始と終了時間をQPCountで取得して
(終了 - 開始)の値をQPFrequencyで割った数値
単位は更に良くわからないけど昔ながらの方が早い
Select Caseでも測ってみた
Select Caseでも昔ながらの方が早いけど
一番早いのはIfの昔ながら
CPUクロックを最大値に固定
3.0GHzに固定して計測
計測した結果
それでもIfの昔ながらが一番早そう
さっき(午後9時47分)追記ここから
乱数取得の方法が間違っていたので修正して計測しなおした
3.0GHz固定でIFで百万回にかかった時間
78なら0.078秒
それにしても乱数っていってもきれいに4等分になるんだなあ
Int(Rnd * 4)ってあっているのかな?
追記ここまで
普段の設定
これで計測しても3.0GHzに固定した時と変わらない数値が出る
参照したところ
3.5 1/1000秒単位の時間を正確に取得-
Excel VBA ゲーム Tips
excel vba 関数の引数と戻り値を配列にする - yoshiya_naの日記
エクセル
VBAマクロ - 便利な関数 - 乱数の生成
標準モジュールの一番先頭に書く呪文
Declare Function GetTickCount Lib "kernel32" () As Long
Declare Function QueryPerformanceCounter Lib "kernel32" _
(lpPerformanceCount As Currency) As Boolean
Declare Function QueryPerformanceFrequency Lib "kernel32" _
(opFrequency As Currency) As Boolean
0から3までのランダムな整数の配列作成用
Function myRnd(ValAry() As Long) As Long()
Dim i As Long
Randomize
Dim Cnt As Long
Cnt = UBound(ValAry)
For i = 0 To Cnt
ValAry(i) = CStr(CInt(Rnd * 3))
ValAry(i) = Int(Rnd * 4)
Next
myRnd = ValAry()
End Function
GetTickCount関数でIf条件分岐百万回の計測
Sub testNewVsOld()
Dim myAry() As Long
Const Cnt As Long = 1000000
ReDim myAry(Cnt)
myAry = myRnd(myAry)
Dim itiZero As Long, ni As Long, san As Long
Dim myStart As Long, myTimeNew As Long, myTimeOld As Long
myStart = GetTickCount
For i = 0 To Cnt
If myAry(i) = 3 Then
san = san + 1
ElseIf myAry(i) = 2 Then
ni = ni + 1
Else
itiZero = itiZero + 1
End If
Next
myTimeNew = GetTickCount - myStart
itiZero = 0: ni = 0: san = 0
myStart = GetTickCount
For i = 0 To Cnt
If myAry(i) < 2 Then
itiZero = itiZero + 1
ElseIf myAry(i) = 2 Then
ni = ni + 1
Else
san = san + 1
End If
Next
myTimeOld = GetTickCount - myStart
MsgBox myTimeNew & "(今どきのIF)" & vbNewLine _
& myTimeOld & "(昔ながらのIF)" & vbNewLine & vbNewLine & _
itiZero & "(1or0の個数)" & vbNewLine & _
ni & "(2の個数)" & vbNewLine & _
san & "(3の個数)"
End Sub
QueryPerformanceCounterを使って計測
Sub testQPC()
Dim myAry() As Long
Const Cnt As Long = 1000000
ReDim myAry(Cnt)
myAry = myRnd(myAry)
Dim itiZero As Long, ni As Long, san As Long
Dim myStart As Currency, myEnd As Currency, Freq As Currency
Dim myTimeNew, myTimeOld
Call QueryPerformanceFrequency(Freq)
Call QueryPerformanceCounter(myStart)
For i = 0 To Cnt
If myAry(i) = 3 Then
san = san + 1
ElseIf myAry(i) = 2 Then
ni = ni + 1
Else
itiZero = itiZero + 1
End If
Next
Call QueryPerformanceCounter(myEnd)
myTimeNew = (myEnd - myStart) / Freq
Call QueryPerformanceFrequency(Freq)
Call QueryPerformanceCounter(myStart)
For i = 0 To Cnt
If myAry(i) < 2 Then
itiZero = itiZero + 1
ElseIf myAry(i) = 2 Then
ni = ni + 1
Else
san = san + 1
End If
Next
Call QueryPerformanceCounter(myEnd)
myTimeOld = (myEnd - myStart) / Freq
MsgBox myTimeNew & "(今どき)" & vbNewLine _
& myTimeOld & "(昔ながら)"
End Sub
IfとSelect CaseそれぞれをQueryPerformanceFrequencyと
QueryPerformanceCounterを使って計測
Sub testQPCSelectCase()
Dim myAry() As Long
Const Cnt As Long = 1000000
ReDim myAry(Cnt)
myAry = myRnd(myAry)
Dim itiZero As Long, ni As Long, san As Long
Dim myStart As Currency, myEnd As Currency, Freq As Currency
Dim myTimeNew, myTimeOld, mySelectNew, mySelectOld
Call QueryPerformanceFrequency(Freq)
Call QueryPerformanceCounter(myStart)
For i = 0 To Cnt
If myAry(i) = 3 Then
san = san + 1
ElseIf myAry(i) = 2 Then
ni = ni + 1
Else
itiZero = itiZero + 1
End If
Next
Call QueryPerformanceCounter(myEnd)
myTimeNew = (myEnd - myStart) / Freq
Call QueryPerformanceFrequency(Freq)
Call QueryPerformanceCounter(myStart)
For i = 0 To Cnt
If myAry(i) < 2 Then
itiZero = itiZero + 1
ElseIf myAry(i) = 2 Then
ni = ni + 1
Else
san = san + 1
End If
Next
Call QueryPerformanceCounter(myEnd)
myTimeOld = (myEnd - myStart) / Freq
Call QueryPerformanceFrequency(Freq)
Call QueryPerformanceCounter(myStart)
For i = 0 To Cnt
Select Case True
Case myAry(i) = 3
san = san + 1
Case myAry(i) = 2
ni = ni + 1
Case Else
itiZero = itiZero + 1
End Select
Next
Call QueryPerformanceCounter(myEnd)
mySelectNew = (myEnd - myStart) / Freq
Call QueryPerformanceFrequency(Freq)
Call QueryPerformanceCounter(myStart)
For i = 0 To Cnt
Select Case True
Case myAry(i) < 2
itiZero = itiZero + 1
Case myAry(i) = 2
ni = ni + 1
Case Else
san = san + 1
End Select
Next
Call QueryPerformanceCounter(myEnd)
mySelectOld = (myEnd - myStart) / Freq
MsgBox myTimeNew & "(今どき)" & vbNewLine _
& myTimeOld & "(昔ながら)" & vbNewLine _
& mySelectNew & "(今どきSelectCase)" & vbNewLine _
& mySelectOld & "(昔ながらSelectCase)"
End Sub
ダウンロード:いまどきのCPUのための条件分岐最適化テスト.xlsm