我能否在F#中避免这种类型转换?

4
这个问题的背景是我正在编写一个光线追踪器。
我有一个 "Surface" 类型,理想情况下我希望它是一个抽象基类,由 "NoSurface" 和 "Lambertian" 继承而来。
然而,当我拥有这个继承关系时,我遇到了以下问题:
    let closestIntersection : bool*LineParameter*Surface = 
    match allIntersectionsWithRealSolutions with 
        | [] -> (false,0.0f, NoSurface(0UL,NotHitable(),Material(Vector3.Zero)))
        | x::xs -> List.reduce (fun smallest current -> smallestIntersection smallest current) allIntersectionsWithRealSolutions

由于它将返回类型绑定到NoSurface,即使将类型指定为Surface,因此会引发编译器错误。

以下更正了此问题:

    let closestIntersection : bool*LineParameter*Surface = 
    match allIntersectionsWithRealSolutions with 
        | [] -> (false,0.0f, NoSurface(0UL,NotHitable(),Material(Vector3.Zero)) :> Surface)
        | x::xs -> List.reduce (fun smallest current -> smallestIntersection smallest current) allIntersectionsWithRealSolutions
closestIntersection

但是根据BenchmarkDotNet的测试结果,使用强制类型转换:>比将Surface定义为一个非抽象类并直接返回它的解决方案多花费了25ms的时间!

我能否以某种方式避免显式强制类型转换并获得所需的Surface抽象类层次结构?


尝试过“向上转型”了吗? - Just another metaprogrammer
你能提供一个代码示例吗?我对F#还比较新。 - Marc HPunkt
2
@FuleSnabel,我已经尝试过| [] -> (false,0.0f, (upcast NoSurface(0UL,NotHitable(),Material(Vector3.Zero)) : Surface)),你是对的!它更快了很多!大约25ms。谢谢!为什么会有这样的性能差异呢? - Marc HPunkt
我在本地尝试了一下,但无法重现您所看到的性能损失。这可能是因为我没有正确的类型定义。对于这样的问题,我建议使用像dotPeek这样的工具来查看您的代码编译成什么。有时需要进入IL才能看到它。 - Just another metaprogrammer
@FuleSnabel 当然可以!文件在这里 https://github.com/geoeo/Raytracer/blob/master/raytracer/Surface.fs - Marc HPunkt
显示剩余2条评论
1个回答

3
你可以尝试使用区分联合类型来解决同样的问题,这是更“函数式”的方式,因为你正在使用F#?不过我有点确信这种方法可能会慢一些。但是,如果你追求最大的性能,那么选择托管语言可能不是最好的开始。
这里找到一个很好的区分联合类型的解释。
type Intersectable =
    | Sphere of center:Point3 * radius:float
    | Triangle of v0:Point3 * v1:Point3 * v2:Point3
    | MeshTriangle of faceIndex:int * mesh:Mesh
    | Instance of Intersectable * Matrix.Matrix4x4
    | Plane of normal:Vec3 * pointOnPlane: Point3

共享的/通用的功能可以通过使用公共数据部分来处理,你可能有:

type Shape = {
    geometry: Intersectable
    material: Material }

感谢您提供类型定义。我正在考虑类似的东西。我可能会尝试这个分支。如果它更慢,我们就看看吧 ;) 不过我不同意之前的观点。这里有很多灰色地带。在我看来,光线追踪是一个非常实用的问题。所以我想尝试一下 F#。但仅仅因为我使用 F# 并不意味着我想运行糟糕/慢的代码。高阶函数和列表推导的能力真的很有帮助。 - Marc HPunkt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接