の例では、それをすべて言う:ローカルにバインドされていないと、コアの関数はどのように速く実行されますか?
user> (time (dotimes [i 10000000] (inc i)))
"Elapsed time: 413.948711 msecs"
nil
user> (time (let [new-inc inc] (dotimes [i 10000000] (new-inc i))))
"Elapsed time: 1034.722729 msecs"
nil
の例では、それをすべて言う:ローカルにバインドされていないと、コアの関数はどのように速く実行されますか?
user> (time (dotimes [i 10000000] (inc i)))
"Elapsed time: 413.948711 msecs"
nil
user> (time (let [new-inc inc] (dotimes [i 10000000] (new-inc i))))
"Elapsed time: 1034.722729 msecs"
nil
私はプリミティブの引数に適用される場合は特に、コンパイラは、inc
のような特定のコア機能をインライン化と信じています。
inc
を通常の関数(たとえば、高次関数に渡し、let
などのエイリアス)として使用すると、パフォーマンスが低下する可能性があります。余分なオーバーヘッドは余分な関数呼び出しを作成することから来ています、おそらくボクシングのコストも1つ以上の引数です。
これはClojureの制限ではなく、コンパイラがまだその最適化では洗練されていないという事実を反映しています。 Clojureの将来のバージョンでは、このようなことがもっとうまくいくと期待できます。
mikeraがインライン展開について言及した内容を追加するだけです。 inc
は関数にバインドされたvarです。 meta
の文字列がinc
、つまり(meta #'inc)
である場合、:inliner
のキーがあり、コンパイラがincのコードを使用して関数のコードをインライン化するために使用できることがわかります。inc var。 let
を使用してローカルにバインドすると、関数オブジェクトをローカルスコープの新しい名前にバインドするだけで、関数オブジェクトにはインライン情報がありません。その情報を持つinc
varであったため、インライン化できません。
これはまさに正しいことです。 'inc'はインラインであり、' i'はプリミティブなlongであることが知られているので、コンパイラは実際には 'clojure.lang.Numbers'のメソッドをバイパスして1バイトコード演算で1を加えます。 – amalloy