2015-01-25 2 views
12

구조 평등 EqualsGetHashCode을 무시하고 IEquatable<>StructuralEqualityAttribute를 사용하는 것이 바람직하다 구현되는 경우가 F 번호 원인 권투 경우 참가 테스트인지하는된다. 그렇다면 = 운영자의 성능을 저하시키지 않고 수행 할 수 있습니까?IEquatable F에서 # = 연산자 성능 및 I는 궁금

하나의 정수를 포함하는 단순한 구조체의 경우 동일한 평등 검사를 1M 번 반복하는 루프를 실행했습니다. 이 25ms

  • 맞춤 IEquatable 리디렉션 == 오퍼레이터 20ms의 구조적 평등 약 110ms
  • = : I가 사용 루프 ... 맞춤 (타입 값 테스트)와

    • = 평등 초과 : 내가 언더에서 에선 0ms (옵티 마이저에 의해 삭제)

    : 3ms의

  • 사용자 지정 값을 직접 비교 == 운영자에게이 1ms tand를 사용하면 IEquatable<> 인터페이스를 성능 최적화로 사용하여 동등성을 확인할 때 복싱을 방지 할 수 있습니다. 이것은 C#에서 흔히있는 것처럼 보이지만 F #에서 그것에 대한 언급을 거의 찾을 수 없습니다. 또한 F # 컴파일러는 주어진 유형에 대해 = 연산자를 무시하려고 할 때 불평합니다.

    StructuralEquality 속성은 documented in the MSDN이며 EqualsGetHashCode 만 덮어 씁니다. 그래도 IEquatable<>의 명시 적 구현을 ​​방지합니다. 그러나 결과 형식이 IEquatable<MyType>과 호환되지 않습니다. 구조적으로 동일한 유형이 IEquatable<>을 구현하지 않는다면 이는 나에게 논리적으로 보이지 않습니다.

    는 F 번호 사양의 =의 성능에 메모 (3.0 사양에 8.15.6.2)가,하지만 난 그것을 무엇을 해야할지하지 않습니다

    주 : 실제로, 빠른 (그러나 의미 상당) 코드를 직접 호출 (=), 모든 기본 유형에 대한 및 해시를 비교하기위한 방출, 빠른 경로는 대부분의 배열

    을 비교하기 위해 사용되는

    주어진 "기본 유형"의 정의 이전에는이 ​​메모를 읽는 것이 유용하지 않은 것으로 나타났습니다. 이 내용이 유형을 참조합니까?

    나는 혼란 스럽다. 무슨 일 이니? 유형이 콜렉션 키 또는 빈번한 동등성 테스트에서 사용될 수 있다면 적절한 평등 구현은 어떻게 생겼을 까?

  • 답변

    8

    여기 내 제한된 경험을 바탕으로 수집 한 내용은 다음과 같습니다

    그러나, 결과 유형은 IEquatable<MyType>와 호환되지 않습니다.

    올바르지 않습니다. 결과 유형이 IEquatable<MyType>입니다. ILDasm에서 확인할 수 있습니다.예 :

    [<StructuralEquality;StructuralComparison>]  
    type SomeType = { 
        Value : int 
    } 
    
    let someTypeAsIEquatable = { Value = 3 } :> System.IEquatable<SomeType> 
    someTypeAsIEquatable.Equals({Value = 3}) |> ignore // calls Equals(SomeType) directly 
    
    아마도

    방금 ​​수행하는 경우 있도록, F 번호가 C#을 같은 암시 업 캐스팅을하지 않는 방법으로 혼동 :

    { Value = 3 }.Equals({Value = 4}) 
    

    이 실제로 같음 (OBJ)를 호출 것 대신 C#에서 오는 기대에 반하는 인터페이스 멤버의.

    궁금하네요하는 F #의 원인 권투의 경우 평등 테스트

    한 일반적이고 애 태우게하는 경우, 예를 들어에 정의 된 구조체입니다 예를 들어, C# 및 IEquatable<T>을 구현 :

    public struct Vector2f : IEquatable<Vector2f> 
    

    또는 유사 예 IEquatable<T>의 맞춤 구현 F 번호에 정의 된 구조체 :

    [<Struct;CustomEquality;NoComparison>] 
    type MyVal = 
        val X : int 
        new(x) = { X = x } 
        override this.Equals(yobj) = 
         match yobj with 
         | :? MyVal as y -> y.X = this.X 
         | _ -> false 
        interface System.IEquatable<MyVal> with 
         member this.Equals(other) = 
          other.X = this.X 
    

    = 연산자이 구조체의 두 인스턴스 비교 실제로 Equals(MyVal) 대신 Equals(obj)을 호출하므로 에서 복싱이 발생하고 두 값은과 비교됩니다 (그리고 캐스팅 및 언 박싱). 참고 : 나는 이것을 bug on the Visualfsharp Github으로보고했으며 분명히이 경우는 나중에 수정해야합니다.

    그리고 확실하게 도움이 될 IEquatable<T>으로 전송하면 좋겠지 만 그 자체로 복싱 작업입니다. 그러나 적어도 당신은이 방법으로 두 권투 중 하나를 구할 수 있습니다.

    나는 혼란 스럽다. 무슨 일 이니? 유형이 컬렉션 키 또는 빈번한 동등성 테스트로 사용될 수있는 경우 적절한 균등 구현은 어떤 모양입니까?

    나는 당신만큼 혼란 스럽습니다. F #은 매우 GC-happy (GC 튜플을 두려워하지 않고 구조체 레코드 나 DU를 지원하지 않음) 인 것처럼 보입니다. 심지어 기본 동작 :

    [<Struct>] 
    type MyVal = 
        val X : int 
        new(x) = { X = x } 
    
    for i in 0 .. 1000000 do 
         (MyVal(i) = MyVal(i + 1)) |> ignore;; 
    Réel : 00:00:00.008, Processeur : 00:00:00.015, GC gén0: 4, gén1: 1, gén2: 0 
    

    여전히 권투 및 과도한 GC 압력의 원인! 해결 방법은 아래를 참조하십시오.

    예를 들어 유형을 키로 사용해야하는 경우 어떻게 될까요? 사전? 음, 그것이 System.Collections.Generics.Dictionary이라면, F # 동등 연산자를 사용하지 않아도됩니다. 그러나이 연산자를 사용하는 F #에 정의 된 컬렉션은 분명히 권투 문제에 부딪 힐 수 있습니다.

    I가 최우선이 GetHashCode과 같음되는 경우가 있는지 (...) 궁금 IEquatable < 구현있어>를 StructuralEqualityAttribute 를 사용하는 것이 바람직하다.

    요점은 사용자 정의 동등성을 정의하는 것이고,이 경우 StructuralEqualityAttribute 대신 CustomEqualityAttribute을 사용합니다.

    그렇다면 = 연산자의 성능을 저하시키지 않고 수행 할 수 있습니까?

    업데이트 : 내가 기본 (=)를 피하고 직접 IEquatable (T) .Equals를 사용하는 것이 좋습니다. 이를 위해 인라인 연산자를 정의 할 수도 있고, 심지어는 (=) 재정의 할 수도 있습니다. 이것은 F #의 거의 모든 유형에 대한 권한을 가지며 나머지는 컴파일하지 않으므로 미묘한 버그에 빠지지 않습니다. I describe the approach in detail here.

    원래

    : F # 4.0을 시작으로 , 다음 (thanks latkin)을 수행 할 수 있습니다

    [<Struct>] 
    type MyVal = 
        val X : int 
        new(x) = { X = x } 
        static member op_Equality(this : MyVal, other : MyVal) = 
         this.X = other.X 
    
    module NonStructural = 
        open NonStructuralComparison 
        let test() = 
         for i in 0 .. 10000000 do 
           (MyVal(i) = MyVal(i + 1)) |> ignore 
    
    // Real: 00:00:00.003, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0 
    NonStructural.test() 
    

    NonStructuralComparison 모듈은 단순히 op_Equality를 호출하는 버전으로 기본 = 우선합니다. NoEqualityNoComparison 속성을 구조체에 추가하여 잘못 수행 된 기본값 인 =을 실수로 사용하지 않도록합니다.