4

最近、Rebolで改行をカウントするための最速/最も効率的な方法について質問しました。特定の状況でどのアプローチが最適かを確認する必要があります。

いくつかのシナリオ例:短いテキスト、改行の数が少ない。短いテキスト、多くの改行; ミディアム/ロングテキスト、多くの改行(コード); ミディアム/ロングテキスト、改行数が少ない(記事)。

私が持っているいくつかの不確実性は次のとおりです。次々に実行した場合、2番目のテストは最初のテストによって汚染されますか?シナリオごとに異なる機能を必要とする場合(最適化)と、1つの機能がすべてに適合する場合(利便性)には、どの程度の違いがありますか?Rebol2.7.8とRebol3の両方のベンチマークが必要です。

これが私がテストしたい特定の関数のセットですが、より一般的な答えもありがたいです:

reduce [
    "@rebolek"
    func [text [string!] /local i][
        parse text [(i: 1) any [thru newline (++ i)]] i
    ]

    "@darius"
    func [text [string!] /local tmp][
        tmp: sort join text "^/"
        1 + subtract index? find/last tmp "^/" index? find tmp "^/"
    ]

    "@endo64"
    func [text [string!]][
        (length? text) - (length? remove-each v text [v = #"^/"])
    ]

    "@BrianH"
    function [text [string!]] [
        i: 1
        find-all text newline [++ i]
        i
    ]

    "@MaxV"
    func [text [string!]][
        write %.mytext text
        text: read/lines %.mytext
        delete %.mytext
        length? text
    ]
]
4

2 に答える 2

6

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つのことです。大規模なデータセットを作成する場合は、オブジェクト階層よりもブロック階層を使用する方が実際に適しています。

上記のスクリプトが、テストしたいもののいくつかをプロファイリングするのに役立つことを願っています。

于 2013-02-21T21:13:08.007 に答える
4

R3の高解像度時間の変更で、Carl Sassenrathは、Rebol 3の改善された(Rebol 2よりも)タイマーについて説明しましたstats/timer。数マイクロ秒の解像度を持つタイムスタンプを取得できます。時間デルタを測定するには、delta-time以下に示すように使用しますが、CPU周波数の変化を超えてデルタを取得する際の精度については、ブログ投稿を参照してください。

そこにある重要なポイントのいくつか:

  • 起動時に、初期化の前に、0:00:00.0に設定されます
  • 周波数分割の前に(Win32で)高解像度のタイマーティックを使用してデルタを計算します

といくつかの例:

合計起動時間を正確に示すスクリプト:

Rebol [file: %boot-time.r]
print stats/timer

SINE関数のタイミングの簡単な例:

delta-time [loop 100 [sine 123]]
于 2013-02-21T23:15:46.640 に答える