2012-03-01 2 views
1

키가있는 일부 좌표를 참조 할 때 모든 유형의 효율적인 패턴 일치를 허용하기 때문에 차별화 된 유니온 유형을 사용하려고했습니다.구분 된 유니온 유형에 대한 빠른 CustomEquality 및 CustomComparison 구현

[<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() 

내가 특정 정렬 순서를 적용해야합니다

은 때라도 코드 snipet를 생각해 보자. 예를 들면 Spot < Vol 항상 있습니다. 나는 sortKey 멤버 함수로이를 시행한다. 그래서 모두가 잘 작동

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

하지만 느린 :

AssetKey은 다시 매우 유사한 차별 노동 조합 유형입니다. 멀리 볼 수있는 한 sortKey 함수가 호출되면 전체 키가 다시 작성되며 특히 toString 함수가 다시 호출됩니다.

하나의 명백한 개선점은 솔루션보다 해킹 된 캐싱 레이어를 추가하는 것입니다.

추가 최적화는 문자열에 해시 키를 사용하는 것입니다. 하지만 여기서도 캐시를 추가해야 할 것입니다. 왜냐하면 해시 키를 캐시에 저장해야하기 때문에 다시 계산할 필요가 없기 때문입니다.

내가 구조체 또는 클래스를 사용하면 성능 최적화가 쉬울 것,하지만 내가 좋아하는 패턴 매칭의 유연성을 잃어 잘 수행하는 또 다른 방법이 될 것입니다 무엇

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

예를

하시나요? 일부 타이밍에서는 전체 성능의 30 % 이상이 sortKey 함수에서 손실됩니다.

의견과 제안을 보내 주셔서 감사합니다.

답변

2

간단한 최적화
방금 ​​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의 결과를 캐시 원하는 경우에, 당신은 당신이 지역의 필드를 저장할 수있는 몇 가지 구조를 사용해야 할 것입니다. 아마도 객체 유형 (클래스 또는 간단한 구조체로 표현됨)을 사용할 것입니다. 이 경우 유형
포장

, 당신은 여전히 ​​활성 패턴을 사용하여 일치하는 좋은 패턴을 얻을 수 있지만 (아마 나쁘지 않다) 모든 클래스에 대한 활성 패턴을 정의 할 필요합니다.다음은 예입니다

// 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) 

유형 AssetKey의 값을 감안할 때, 당신은 지금 캐시 된 문자열 표현을 얻을 수 k.Key를 작성 할 수 있습니다 할 수있는 활성 패턴을 사용하여에 패턴 일치 :

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

당신을 , Coord의 죄수 CoordRepr을 숨기기 위해 서명 파일을 사용하여 다음

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