5

次のChez Schemeコードを考えてみましょう。

(インポート (chezscheme))

(define (list-enumerate ls val proc)
  (let loop ((ls ls) (return? #f) (val val))
    (if (または (null? ls)
            戻る?)
        値
        (call-with-values (lambda () (proc val (car ls)))
          (ラムダ (return? val)
            (ループ (cdr ls) return? val))))))

(define (list-index ls proc)
  (リスト列挙 ls
                  0
                  (ラムダ (i elt)
                    (if (proc elt)
                        (値 #ti)
                        (値 #f (+ i 1))))))

(定義 n 100000)

(データの定義 (iota n))

(time (list-index data (lambda (elt) (= elt (- n 1)))))

それを実行します:

~ $ scheme --script ~/scratch/_list-enumerate-allocation-test-chez-a.sps
(時間 (リスト インデックス データ ...))
    コレクションなし
    3 ミリ秒の CPU 経過時間
    4 ミリ秒経過リアルタイム
    8 バイト割り当て

うわー、8 バイトしか割り当てられていないと報告されています。

--programの代わりにオプションを使用して、もう一度実行してみましょう--script

~ $ スキーム --program ~/scratch/_list-enumerate-allocation-test-chez-a.sps
(時間 (リスト インデックス データ ...))
    コレクションなし
    3 ミリ秒の CPU 経過時間
    3 ミリ秒経過リアルタイム
    800000 バイトが割り当てられました

ええ、800000 バイトが割り当てられています。

違いは何ですか?

エド

4

1 に答える 1

8

これに対する Kent Dybvig からのメモは次のとおりです。


それは興味深い質問です。

REPL セマンティクスを使用する --script で実行すると、list-enumerate や list-index などのスクリプトで定義された変数が変更可能になり、インライン化を含むプロシージャ間の最適化が阻害されます。ただし、 --program を指定して実行すると、変数は不変になり、手続き間の最適化が可能になります。

この場合、 --program により、コンパイラは list-enumerate を list-index の本体にインライン化し、次に list-index の本体内のラムダ式を list-enumerate の本体にインライン化できます。最終結果は、call-with-values プロデューサー式内の​​条件式です。これにより、コンパイラーはコンシューマーのクロージャーを作成し、条件分岐の then 分岐と else 分岐に沿ったコードの重複を回避します。このクロージャは list-enumerate のループのたびに作成されるため、追加の割り当てオーバーヘッドが発生します。これは、最適化がしばしば行われる方法です。勝つことが多いですが、負けることもあります。良いニュースは、結局のところ、あなたのプログラムであっても、彼の費用を上回るメリットです. list-index の呼び出しをループに入れました (変更されたコードは以下にあります)。 --program を使用すると、コードの実行速度が約 30% 速くなることがわかりました。

ケント


(インポート (chezscheme))

(define (list-enumerate ls val proc)
  (let loop ((ls ls) (return? #f) (val val))
    (if (または (null? ls)
            戻る?)
        値
        (call-with-values (lambda () (proc val (car ls)))
          (ラムダ (return? val)
            (ループ (cdr ls) return? val))))))

(define (list-index ls proc)
  (リスト列挙 ls
                  0
                  (ラムダ (i elt)
                    (if (proc elt)
                        (値 #ti)
                        (値 #f (+ i 1))))))

(定義 n 100000)

(データ (時間 (iota n)) を定義)

(させて ()
(ルナロットを定義する
  (ラムダ (サンク)
    (ループさせて ([ii])
      (let ([x (サンク)])
        (if (fx=​​ i 1)
            バツ
            (ループ (fx- i 1)))))))

(時間
  (ルナロット 1000
    (ラムダ ()
      (list-index data (lambda (elt) (= elt (- n 1))))))))
于 2010-03-04T16:56:08.763 に答える