人気ブログランキング | 話題のタグを見る
- 北の空からみなみへ -
exblog staff

コマンドプロンプトの1行ベンチマーク
2018年 03月 11日
先の1行FizzBuzzを反復実行して、所要時間から処理速度を判別しようとしたら、老化脳では「どのくらい?」という程度感覚が掴めなかった。
(また、処理出力をNULリダイレクトで隠してしまえばいいが、表示させてしまうと、100行×1000試行=10万行の彼方に開始時刻が流れ去っていて確認に困った)

なので、一行FizzBuzzの反復試行ルーチンのさらに前後に手を入れて、開始時間を表示し→計算反復の実行→終了時間を表示し所要時間を表示する。ように書き加えてみた。
(例によって後で再度使い回す場合のために、備忘録として記録)

説明文を簡便化するために、実行する本体を仮に FIZZBUZZとおこう(代入だね)。

実行土台となる CMD.EXE は、スイッチ /v:on で遅延変数展開ONでコマンドプロンプトを起動しておく。
(現在時刻変数 time の一行内の時間差から、所要時間を算出するのだから、これは必須。)
このところ、砂場遊びでは次の一行をコマンドプロンプト起動直後に入れるのがルーチン化している。(自動でこうなるようにできるが、それは禁じ手=職場PCなので)

color F0&cmd/v:on/k prompt $g ^&title SandBox

FIZZBUZZの1,000回実行コードはこんな感じ。

for /L %z in (1,1,1000) do FIZZBUZZ
これを画面に表示させずNULへ流すには、括弧で括ってリダイレクトアウト。
(for /L %z in (1,1,1000) do FIZZBUZZ)>nul

この前に現在時刻を取得し、深夜0時からのミリ秒数計算のコードをおく。(バッチ内でない限り、set/a で代入するための計算結果はコンソール表示されてしまうので、まとまるように書く)

バッチ出力を最低限の改行で済ませるために、コードは set/p&nul="改行なし表示出力" の形式を多用。
投稿ボックス内で自動改行されると空白などが分かりにくいから横長表示で。
(ついでにマウスオン解説表示も多用)
この上には表示や計算結果や解説が表示されます。
set/p&nul=start=%tilme%[
start= 01:23:45.67[5025670]
set/p&nul=start=%tilme%[
&set stime=%time%
変数 stime(=start time) に時刻を代入(※実のところ1行処理なら無用=後で解説)
set stime=%time%
&set/a start
変数 start にミリ秒変換した開始時刻を代入(表示もされる)
set/a start
=1000*(!stime:~0,2!*3600+(1!stime:~3;2!-100)60+(1!stime:~6,2!-100)
start= 01:23:45.67[5025670] "時刻文字列"を秒数に
!stime:~0,2!*3600+(1!stime:~3;2!-100)60+(1!stime:~6,2!-100)
)+1!stime:~-2!0-1000
start= 01:23:45.67[5025670]
+1!stime:~-2!0-1000
&echo.]
start= 01:23:45.67[5025670]
echo.]
&

後半もほとんど同じコードですね。
この上には表示や計算結果や解説が表示されます。
&set etime=!time!
変数 etime(=end time) に時刻を代入完了「直後」の時刻
set etime=!time!
&set/p&nul=end=!etime![
start= 02:08:45.67[7725670] average=270ms
set/p&nul=end=!etime![
&set/a end
変数 end にミリ秒変換した処理完了時刻を代入(表示もされる)
set/a end
=1000*(!etime:~0,2!*3600+(1!etime:~3;2!-100)60+(1!etime:~6,2!-100)
end= 02:08:45.67[[7725670] average=270ms "時刻文字列"を秒数に
!etime:~0,2!*3600+(1!etime:~3;2!-100)60+(1!etime:~6,2!-100)
)+1!etime:~-2!0-1000
end= 02:08:45.67[7725670] average=270ms
+1!etime:~-2!0-1000
&set/p&nul=] average=
end= 02:08:45.67[7725670] average=270ms
set/p&nul=] average=
&anp;set/a ltime=(!end!-!start!)/1000
end= 02:08:45.67[7725670] average=270ms(計算結果は改行なしで表示される)
set/a ltime=(!end!-!start!)/1000
&echo.ms
end= 02:08:45.67[7725670] average=270ms
echo.ms

あとは、前半コード+FIZZBUZZコード+後半コード のサンドイッチで出来上がり。
ということで職場で私が触れる範囲のPCやWSでベンチマークを実行してみました。(結果はmore欄で)

ですが、この投稿にまとめていて気づいたのです。
遅延変数展開下の環境では、変数参照に !変数!(遅延展開=実行時に動的変数として展開)と %変数%(時即時展開=コマンドライン改行の瞬間に一行内の変数は確定)とが、「どちらも」使えるのでした。

つまり、サンドイッチに挟まなくても、
(処理) & (終了時刻「!time!」・開始時刻「%time%」で所要計算)
みたいにすれば、より簡明な記述ができたのですね。。。。orz...

さらにループ回数を柔軟に設定できるように書き換えると、こうなります。
(実地検証でこけまして修正)
(if not defined $count set/a $count=1000)&(for /L %z in (1,1,!$count!) do FIZZBUZZ)>nul&set etime=!time!&set/p<nul=%time%-!etime!/!$count!Loop Average = &set/a ltime=(1000*(3600*!etime:~0,2!+60*(1!etime:~3,2!-100)+1!etime:~6,2!-100)+1!etime:~-2!0-1000-(1000*(3600*%time:~0,2%+60*(1%time:~3,2%-100)+1%time:~6,2%-100)+1%time:~-2%0-1000))/!$count!&echo ms
1日を跨ぐ場合には、ltime が負数になるので、86400000を加え86400000で剰余を取るなどして正数化したミリ秒数を 試行回数 $countで割る。このように扱う必要もあるけど、そこまで使うケースはおそらくない。
(if not defined $count set/a $count=1000)&(for /L %z in (1,1,!$count!) do cmd/q/v:on/c"for /L %a in (0,1,6) do for %A in (1 2 Fi 4 Bu Fi 7 8 Fi Bu 11 Fi 13 14 FizzBu) do if %a11==6%A (exit) else if %A gtr a (echo.%Azz) else set/a $=15*%a+%A&echo.")&set etime=!time!&set/p<nul=%time%-!etime!/!$count!Loop Average = &set/a ltime=((1000*(3600*!etime:~0,2!+60*(1!etime:~3,2!-100)+1!etime:~6,2!-100)+1!etime:~-2!0-1000-(1000*(3600*%time:~0,2%+60*(1%time:~3,2%-100)+1%time:~6,2%-100)+1%time:~-2%0-1000))+86400000)%86400000/!$count!&echo ms


じっこうけっか と 小さな説
# by bucmacoto | 2018-03-11 22:09 | &Tips;&code; | Comments(1)