gpt4 book ai didi

f# - F# 中的 IEquatable,= 运算符性能和结构平等

转载 作者:行者123 更新时间:2023-12-03 20:50:22 26 4
gpt4 key购买 nike

我想知道 F# 中的相等测试在哪些情况下会导致装箱,以及是否存在覆盖 Equals 的情况。和 GetHashCode并实现IEquatable<>比使用 StructuralEqualityAttribute 更可取.如果是这样,是否可以在不降低 = 性能的情况下完成?运算符(operator)?

对于保存一个整数的简单结构,我运行了一个循环,重复相同的相等性检查 1M 次。我使用...计时循环

  • =使用自定义(类型和值测试)相等:约 110 毫秒
  • =结构相等:20ms 到 25ms
  • 定制==重定向到 IEquatable 的运算符:1ms 到 3ms
  • 定制==直接比较值的运算符:0ms(由优化器删除)

  • 据我了解, IEquatable<>接口(interface)可以用作性能优化,以防止在检查相等性时装箱。这在 C# 中似乎很常见,但我在 F# 中几乎找不到提及它。此外,F# 编译器在尝试覆盖 = 时会报错。给定类型的运算符。
    StructuralEquality属性为 documented in the MSDN仅覆盖 EqualsGetHashCode .尽管如此,它确实阻止了 IEquatable<> 的显式实现。 .但是,结果类型与 IEquatable<MyType> 不兼容。 .如果结构上等价的类型不实现 IEquatable<>,这对我来说似乎不合逻辑?

    有一个关于 =的性能的注释在 F# 规范中(3.0 规范中的 8.15.6.2),但我不知道该怎么做:

    Note: In practice, fast (but semantically equivalent) code is emitted for direct calls to (=), compare, and hash for all base types, and faster paths are used for comparing most arrays



    之前给出的“基本类型”的定义似乎对阅读本说明没有用处。这是指基本类型吗?

    我很困惑。到底是怎么回事?如果类型可以用作集合键或在频繁的相等测试中,正确的相等实现会是什么样子?

    最佳答案

    以下是我根据我有限的经验收集的内容:

    However, the resulting type is incompatible with 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})
    这实际上会调用 Equals(obj) 而不是接口(interface)成员,这与来自 C# 的预期相反。

    I'm wondering in which cases equality tests in F# cause boxing


    一种常见且令人烦恼的情况是对于在例如定义的任何结构。 C# 和实现 IEquatable<T> , 例如:
    public struct Vector2f : IEquatable<Vector2f>
    或类似地,在 F# 中定义的任何结构,其自定义实现为 IEquatable<T> , 例如:
    [<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
    将此结构的两个实例与 = 进行比较运算符(operator)实际调用 Equals(obj)而不是 Equals(MyVal) ,导致在 上发生装箱正在比较的两个值 (然后类型转换和拆箱)。注意:我将此报告为 bug on the Visualfsharp Github . (2022 年更新:尽管社区做出了英勇的努力,但这个问题从未得到解决)。
    如果您认为转换到 IEquatable<T>明确地会有所帮助,它会,但它本身就是一个拳击操作。但至少您可以通过这种方式为自己节省两种拳击中的一种。

    I'm confused. What is going on? What would a proper equalityimplementation look like, if the type might be used as a collectionkey or in a frequent equality test?


    我和你一样困惑。 F# 似乎对 GC 非常满意。即使是默认行为:
    [<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 压力!请参阅下面的解决方法。
    如果类型必须用作例如 key 怎么办?一本字典?好吧,如果它是 System.Collections.Generics.Dictionary你很好,它不使用 F# 相等运算符。但是在 F# 中定义的任何使用此运算符的集合显然都会遇到装箱问题。

    I'm wondering (...) whether there are cases in which overriding Equalsand GetHashCode and implementing IEquatable<> is preferable to usingthe StructuralEqualityAttribute.


    关键是定义您自己的自定义相等,在这种情况下您使用 CustomEqualityAttribute而不是 StructuralEqualityAttribute .

    If so, can it be done without reducing the performance of the = operator?


    更新:我建议避免使用默认值 (=) 并直接使用 IEquatable(T).Equals。您可以为此定义一个内联运算符,甚至可以根据它重新定义 (=)。这对 F# 中的几乎所有类型都是正确的,而对于其余类型,它不会编译,因此您不会遇到细微的错误。 (2022 年更新:我不确定这是个好主意。)
    原文:
    从 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结构的属性只是为了确保您不会意外使用性能不佳的默认值 = .

    关于f# - F# 中的 IEquatable,= 运算符性能和结构平等,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28142655/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com