2012-03-01 11 views
1

いくつかの座標をキーで参照するために、すべての種類の効率的なパターンマッチングを可能にするため、識別された共用体を使用したいと考えました。識別された共用体型のために速いCustomEqualityとCustomComparisonを実装する

はfollwoingコードsnipetを考えてみましょう:

[<CustomEquality; CustomComparison>] 
type Coord = 
| Spot of AssetKey 
| Vol of AssetKey * DateTime option 
| Rate of Currency         
..... 

    member this.sortKey = 
     match this with 
     | Spot(key)        -> (0 , key.toString) 
     | Vol(key)        -> (1 , key.toString) 
     | Vol(key, Some(t))      -> (2 , key.toString + t.ToShortString()) 
     | Rate(cur)        -> (3 , cur.toString) 
     ...... 

    interface IComparable with 
     member this.CompareTo(obj) = 
      match obj with 
      | :? Coord as other -> compare this.sortKey other.sortKey 
      | _ -> invalidArg "obj" "not a Coord type" 

    override this.Equals(obj) = 
      match obj with 
      | :? Coord as other -> this.sortKey = other.sortKey 
      | _ -> false 

    override this.GetHashCode() = this.sortKey.GetHashCode() 

私は特定のソート順序を強制する必要があります。例えば、スポット< Vol。 sortKeyメンバ関数でそれを強制します。

AssetKeyのは、再び非常に類似した判別共用体型です:

[<StructuralEqualityAttribute; StructuralComparisonAttribute>] 
type AssetKey = 
| Equity of string 
| EquityIndex of string 
..... 

だから、これはすべてがうまく動作しますが、それは遅いです。私が見る限り、sortKey関数が呼び出されると、キー全体が再び構築されます。特にtoString関数が再び呼び出されます。

明白な改善点は、ソリューションよりもハックの多いキャッシングレイヤーを追加することです。

さらに最適化するには、文字列にハッシュキーを使用する方法があります。しかし、ここでもハッシュキーをキャッシュする必要があるため、キャッシングを追加する必要があります。すべてを再計算する必要はありません。

私は構造体やクラスを使用する場合、パフォーマンスの最適化が容易になるだろうが、その後、私はよく行われている別のアプローチ、どうなるか例えば

match c with 
| Coord.Vol(ak, _) when ak = assetKey -> true 
| _ -> false 

のようなパターンマッチングの柔軟性を失いますか?いくつかの私のタイミングでは、全体のパフォーマンスの30%以上がsortKey関数で失われています。

ご協力ありがとうございます。あなたは簡単に行うことができ

答えて

2

シンプルな最適化
1つの基本的な最適化は、あなただけのCoordのタイプに基づいて決定を行うことができるときtoStringを呼び出さないようにすることです。代わりにsortKeyを構築するのに書くことができ:

// Separate functions that return tag and key, so that we don't 
// have to call 'toString' if we can decide based just on the Tag 
member this.Tag = 
    match this with 
    | Spot _ -> 0 | Vol(_, None) -> 1 
    | Vol _ -> 2 | Rate _ -> 3 
member this.Key = 
    match this with 
    | Spot(key) | Vol(key, None) -> key.toString | Rate cur -> cur.toString 
    | Vol(key, Some t) -> key.toString + t.ToShortString()) 

interface IComparable with 
    member this.CompareTo(obj) = 
     match obj with 
     | :? Coord as other -> 
      let c = compare this.Tag other.Tag 
      // Try comparing based on the tag first - if the tags 
      // are the same, then get Key and compare based on the key 
      if c <> 0 then c else compare this.Key other.Key 
     | _ -> invalidArg "obj" "not a Coord type" 

あなたはtoStringの結果をキャッシュしたい場合、あなたは地元のフィールドを格納することができますいくつかの構造を使用する必要があるだろう。私はおそらくオブジェクト型(クラスまたは単純な構造体として表されている)を使用します。その場合、タイプ
ラッピング

、あなたはまだアクティブなパターンを使用して、素敵なパターンマッチングを得ることができるが、それは(おそらく悪いわけではない)すべてのクラスのアクティブパターンを定義する必要があり。ここでは一例です:タイプAssetKeyの価値を考えると

// This type will not be used directly - it is an internal implementation 
// hidden from the users that will be accessed using active patterns 
[<RequiresQualifiedAccess>] 
type AssetKeyInternal =  
    | Equity of string  
    | EquityIndex of string 
    override x.ToString() = ... 

// Public type with active patterns for pattern matching 
type AssetKey(key:AssteKeyInternal) = 
    let str = lazy key.ToString() // Lazily cached string 
    member x.Key = str.Value  // Evaluated when accessed for the first time 

    member x.Value = key // Returns the internal representation 

// Define active patterns working over AssetKey type 
let (|Equity|EquityIndex|) (k:AssetKey) = 
    match k.Value with 
    | AssetKeyInternal.Equity(e) -> Equity(e) 
    | AssetKeyInternal.EquityIndex(e) -> EquityIndex(e) 

、あなたは今、キャッシュされた文字列表現を取得するためにk.Keyを書くことができますし、パターンマッチは、アクティブなパターンを使用して、その上に次のことができます。

match k with 
| Equity k -> ... 
| EquityIndex i -> ... 
1

あなたはCoordReprを非表示にする署名ファイルを使用して、Coordの詐欺そして

type CoordRepr = 
| Spot of AssetKey 
| Vol of AssetKey * DateTime option 
| Rate of Currency    

let sortKey = function 
| Spot(key) -> 1,key.ToString() 
| Vol(key,None) -> 2,key.ToString() 
| Vol(key,Some(v)) -> 2,key.ToString() + v.ToShortDateString() 
| Rate(key) -> 3,key.ToString() 

type Coord(repr) = 
    let sortKey = sortKey repr 
    member __.Repr = repr 
    member __.SortKey = sortKey 
    override __.Equals(that) = 
     match that with 
     | :? Coord as c -> sortKey = c.SortKey 
     | _ -> false 
    override __.GetHashCode() = sortKey.GetHashCode() 
    interface System.IComparable with 
     member __.CompareTo(that) = 
      match that with 
      | :? Coord as c -> compare sortKey c.SortKey 
      | _ -> failwith "invalidArg" 

let Spot k = Coord(Spot k) 
let Vol(k,v) = Coord(Vol(k,v)) 
let Rate(k) = Coord(Rate(k)) 

let (|Spot|Vol|Rate|) (c:Coord) = 
    match c.Repr with 
    | Spot k -> Spot k 
    | Vol(k,v) -> Vol(k,v) 
    | Rate k -> Rate k 

のようなものをやって検討することもでき構造体、sortKeyなど

+0

こんにちは、ありがとうございます。私は最初のバージョンを実装し、それは約100倍高速です。私の実装は次のとおりです。 – Daniel

関連する問題