REBOL(R2&R3)でのプロファイリングの課題は3つあります。タイミング、ループ、メモリ使用。
タイミング:
一部のOSでは、デフォルトのタイミングは正確ではありません(Windowsなど)。これは、基本的にテストを許容可能なタイミングマージンにスケーリングするより大きなループを作成することで大幅に軽減できます。また、Windows用に作成したクロノライブラリ( chrono-lib.r )のように、より優れたタイマーを作成することもできます。
また、ブロックのテストを容易にするタイムラプス機能も含まれています(基本的には、Windows用のより正確なDT)。
ループ:
これは明らかなことです。時間を計るとき、OS /マルチタスクのオーバーヘッドの一部を取り除くために、コードを数回実行し、繰り返しを分割して平均を求めます。多くの場合、これで十分なアイデアが得られます。
しかし、rebolでは、ループはそれ自体でかなりコストがかかります。反復関数には、無視できない独自のオーバーヘッドがあります。したがって、コードをループする前に、使用しているループが最適であることを確認してください。特に、ネイティブのイテレータ関数を使用していることを確認してください。ループは、プロファイリングしようとしているコードよりも遅くなる可能性があります。R2では、ほとんどの状況でFOREACHとLOOPがより高速なループです。FOREACHは、ループを開始する前に少し拘束力のあるオーバーヘッドがありますが、私の広範なテストでは、これは1回だけ発生し、数秒の実行には関係がないため、計り知れない影響を及ぼしました。
R3では、R2のメズループの一部がネイティブになったために速度が向上したイテレータ機能があります。そのため、それらを確認する必要があります。
メモリー:
これは、物事が予測しにくくなる場所です。REBOLs GCは、プロファイリング時に非常に煩わしいものです。遅いだけでなく、予測不可能であり、実際に最適化することはできません。また、REBOLのメモリフットプリントが増えると遅くなります(実際、人々が認識しているよりもはるかに多くなります)。
いくらですか?どれどれ:
これは、メモリ使用量と実行速度をベンチマークするために必要なスクリプトです。それは私に一方と他方の関係を示すことができるグラフィックを生成します。ガベージコレクターをオフにするオプションが1つあります...そして、それがプロファイリングにどのように影響するかがわかります...グラフィックの有無は非常にわかりやすくなっています。
rebol [
title: "profiling tests with graphics"
author: "Maxim Olivier-Adlhoch"
purpose: "given a few metrics and code blocks, will plot out the time and memory use for each repetition."
]
;----
; test metrics
loops: 2000
repetitions: 500
;----
; test options
turn-off-garbage-collector?: false
;----
; output gfx prefs
mem-color: red
time-color: sky
bg-color: black
gfx-size: 600x400
margins: 100x100
lw: 2 ; line-width
label-color: white * .85
border-color: gray * 0.5
slices: 10 ; how many labels on the edges of the graphics.
;----
; globals
plot-data: []
;----
;- functions
platform: does [
select [
1 AMIGA
2 OSX
3 WIN32
4 LINUX
] system/version/4
]
;----
; make sure timer resolution is decent on all platforms
;----
either ( platform = 'WIN32 ) [
either exists? %libs/windows-chrono.r [
do %libs/windows-chrono.r
][
ask "download and save windows-chrono from rebol.org ? ^/^/ * press ENTER to confirm^/ * press ESCAPE to halt."
make-dir %libs/
write %libs/windows-chrono.r read http://www.rebol.org/download-a-script.r?script-name=windows-chrono.r
print "windows chrono downloaded"
]
][
; on other platforms, the OS timer resolution tends to be better, we can just use delta-time
time-lapse: :delta-time
]
; time-lapse: :delta-time
;----
; TEST CODE INITIALISATIONS
blk: make block! repetitions
print "===================="
print "running tests"
;----
; SETUP TEST OPTION(S)
if turn-off-garbage-collector? [
recycle/off
]
;--------------------------------------------
; PERFORM AND PLOT TESTS
;--------------------------------------------
repeat i repetitions [
;--------------------------------------------
; PUT YOUR LOOP INIT CODE HERE:
;--------------------------------------------
tmp: last append/only blk copy []
time: time-lapse [
loop loops [
;--------------------------------------------
; PUT YOUR TEST CODE HERE:
;--------------------------------------------
; here we just accumulate RAM...
append tmp make string! 1000
]
]
memory: stats
append plot-data reduce [ time memory]
prin "."
]
;-------------------------
; extract plot data scale
;-------------------------
time-x: 0:00
stat-y: 0
foreach [time stat] plot-data [
time-x: max time-x time
stat-y: max stat-y stat
]
time-scale: (gfx-size/y / to-decimal time-x )
mem-scale: gfx-size/y / stat-y
print ""
?? time-scale
?? mem-scale
;-------------------------
; build gfx
;-------------------------
;-------
; lines
i: 0
mem-line: compose [line-width lw pen (mem-color ) line () ]
time-line: compose [line-width lw pen (time-color ) line () ]
foreach [time ram] plot-data [
time: to-decimal time
;?? time
;print time * time-scale
append mem-line margins + to-pair reduce [ x: to-integer (i / (repetitions - 1) * gfx-size/x) to-integer ( ram * mem-scale )]
append time-line margins + to-pair reduce [ x to-integer ( time * time-scale )]
i: i + 1
]
;------
;scales
scale-drw: compose [
line-width 1
pen (border-color) box (margins) (margins + gfx-size)
]
repeat i (slices + 1) [
ii: i - 1
append scale-drw reduce [
'pen mem-color
'text margins + to-pair reduce [ -50 (gfx-size/y - (ii / slices * gfx-size/y ) ) - 5 ]
rejoin [ to-string round/to (ii / slices * stat-y / 1'000'000) 0.01 " MB" ]
'pen time-color
'text margins + to-pair reduce [ gfx-size/x (gfx-size/y - (ii / slices * gfx-size/y ) ) - 5 ]
rejoin [ to-string round/to (1000 * ii / slices * to-decimal time-x) 0.1 "ms" ]
'pen border-color
'text margins + to-pair reduce [ ((ii / slices * gfx-size/x ) ) gfx-size/y + 10 ]
rejoin [ to-string to-integer( ii / slices * repetitions) ]
]
]
view layout compose/deep [
box (margins * 2 + gfx-size) bg-color effect [draw [
translate (0x1 * (margins * 2 + gfx-size)) scale 1 -1.0 (mem-line) (time-line)
reset-matrix
(scale-drw)
]]
]
このスクリプトを好きなだけコピーして編集してください。これは非常に単純なプロットメカニズムです...(テストしている関数の出力値の曲線のように)曲線を追加できると確信しています。
このスクリプトの上部に、GCを無効にするオプションがあることがわかります...ループ内にあるこのテストコードでは、RAMを割り当てるだけで、GCが全体的なパフォーマンスにどのように影響するかを確認できます。
これは、GCを有効にした実行です。
GCがプロセスを監視および中断し続けるため、実行が妨げられていることがわかります。
RAMが増加すると(赤い線)、実行は常に遅くなります。すべてのブロックが事前に割り当てられているため、これは内部メモリのコピーによるものではないことに注意してください。
これは、GCを無効にした実行です。
操作がどれほど直線的であるかがはっきりとわかります。
両方の実行で発生する不規則なジャンプは、通常のOSマルチタスクが原因です。
両方のテストの最高速度が同じであることに注意してください。約5ミリ秒...しかし、平均は、GCを有効にした実行でますます悪化し続けています。
このため、コードをテストするときは、コードによって生成された即時メモリがタスクをメモリ不足ですぐに実行しない限り、常にGCをオフにします。または、コードの重要なポイントでリサイクル/オフリサイクル/オンして、方程式から問題のこの部分を軽減することもできます。recycle / onが呼び出されると、しきい値を超えた場合、すぐにクリーンアップが実行されます。
メモリ割り当ての複雑さがGCに影響を与えることに注意してください。RAMの量だけでなく、構築される値の数も大きな影響を及ぼします。上記のプロファイラーを使用すると、指数関数的に遅いアルゴリズムを構築できます。これは、リアルタイムで開始すると、わずか300MBでほとんど使用できなくなります。
多くの複雑なオブジェクトをバインドして作成することは、rebolの速度のふりを完全に破壊する1つのことです。大規模なデータセットを作成する場合は、オブジェクト階層よりもブロック階層を使用する方が実際に適しています。
上記のスクリプトが、テストしたいもののいくつかをプロファイリングするのに役立つことを願っています。