なぜF#'s Seq.sortByよりもずっと遅いですがLINQのIEnumerable<T>.OrderBy拡張できますか?
質問
私は最近、筆記のコードを読データファイルから,大切に保管してくださのタプルやソートすべてを収集したデータによる最初の要素のタプル(tuple)型です。その後試験のことを配列番号.sortBy(配列になります。sortBy)は極めて遅いよIEnumerable.OrderBy.以下の二つのスニペットをコードするべきであるとの行動か:
(filename
|> File.ReadAllLines
|> Array.Parallel.map(fun ln -> let arr = ln.Split([|' '|], StringSplitOptions.RemoveEmptyEntries)
|> Array.map(double)
|> Array.sort in arr.[0], arr.[1])
).OrderBy(new Func(fun (a,b) -> a))
や
filename
|> File.ReadAllLines
|> Array.Parallel.map(fun ln -> let arr = ln.Split([|' '|], StringSplitOptions.RemoveEmptyEntries) |> Array.map(double) |> Array.sort in arr.[0], arr.[1])
|> Seq.sortBy(fun (a,_) -> a)
ファイルを含む100000ラインのキング、マイコンピュータは後者の版かの倍にな改善が得られた利用の場合配列になります。sortBy).アイデア、浮かぶのでしょうか。
解決
ショートパスフィルの実装構造の比較による鍵があります。
let sortBy keyf seq =
let comparer = ComparisonIdentity.Structural
mkDelayedSeq (fun () ->
(seq
|> to_list
|> List.sortWith (fun x y -> comparer.Compare(keyf x,keyf y))
|> to_array) :> seq<_>)
(もう)
let sort seq =
mkDelayedSeq (fun () ->
(seq
|> to_list
|> List.sortWith Operators.compare
|> to_array) :> seq<_>)
両です。比較のComparisonIdentity.構造.を比較しなし)
let inline GenericComparisonFast<'T> (x:'T) (y:'T) : int =
GenericComparisonIntrinsic x y
// lots of other types elided
when 'T : float = if (# "clt" x y : bool #)
then (-1)
else (# "cgt" x y : int #)
そのルートをこのオペレーターは完全にインラインで、このJITコンパイラの後に挿入し直接ダブルとの比較の指示のない追加のメソッドの呼び出し架を除く(必要な場合辺)にあり、委譲呼び出し.
のsortByを使用しcomparerなります追加の仮想メソッドの呼び出しでは基本的には同じです。
比較のOrderBy機能をうまく活用しながら仮想メソッドの呼び出しのための平等を EqualityComparer<T>.Default
ものの有意差はある種の場所を使用してバッファを作成しています。に比べれば、sortByすることである種のリストにない場所で使用さStableSortImplementationが発表されることになりますの統合ソート)およびそのコピーを作成して新しい配列の型になります。この追加コピーされるサイズの入力データがの原因に減速が異なるソートの実装 月 も影響を及ぼします。
とはいえこれらはすべて 推測.この地域が懸念されますの性能面でそれがで 概要 すぐに何が起きているのに。
ご希望の場合は見どのような影響を与え、仕分け-コピー変化として代替:
// these are taken from the f# source so as to be consistent
// beware doing this, the compiler may know about such methods
open System.Collections.Generic
let mkSeq f =
{ new IEnumerable<'b> with
member x.GetEnumerator() = f()
interface System.Collections.IEnumerable with
member x.GetEnumerator() = (f() :> System.Collections.IEnumerator) }
let mkDelayedSeq (f: unit -> IEnumerable<'T>) =
mkSeq (fun () -> f().GetEnumerator())
// the function
let sortByFaster keyf seq =
let comparer = ComparisonIdentity.Structural
mkDelayedSeq (fun () ->
let buffer = Seq.to_array seq
Array.sortInPlaceBy (fun x y -> comparer.Compare(keyf x,keyf y)) buffer
buffer :> seq<_>)
いくつかの合理的な割合speedupsのreplと非常に大きい(>百万円)入力配列がないため、大きくなりました。マイレージとして、は変更になる場合がございます。
他のヒント
X2の差があまりないです。
データ構造(例えば、入力されるICollection<T>
ために最適化)で小さな差が差のこのスケールを作ることができます。
とF#は、現在ベータ版(右の言語とライブラリを取得対最適化ではないので、あまりフォーカス)で、プラス(部分的なアプリケーションなどをサポートしている)のF#の機能の一般性はスピードを呼び出すに遅く、わずかにつながる可能性がある。もっと異なるを説明するには十分よります。