.net - 在 System.Numerics.Vector<T> 中使用 F# 度量单位

标签 .net f# vectorization simd units-of-measurement

我很难将 F# 度量单位与 System.Numerics.Vector<'T> 结合使用类型。让我们看一个玩具问题:假设我们有一个数组 xs类型 float<m>[]出于某种原因,我们想对它的所有分量进行平方,从而得到一个类型为 float<m^2>[] 的数组。 .这对标量代码非常有效:

xs |> Array.map (fun x -> x * x) // float<m^2>[]

现在假设我们想通过在 System.Numerics.Vector<float>.Count 中执行乘法来向量化这个操作。使用 SIMD 的大小块,例如像这样:
open System.Numerics
let simdWidth = Vector<float>.Count
// fill with dummy data
let xs = Array.init (simdWidth * 10) (fun i -> float i * 1.0<m>)
// array to store the results
let rs: float<m^2> array = Array.zeroCreate (xs |> Array.length)
// number of SIMD operations required
let chunks = (xs |> Array.length) / simdWidth
// for simplicity, assume xs.Length % simdWidth = 0
for i = 0 to chunks - 1 do
    let v = Vector<_>(xs, i * simdWidth) // Vector<float<m>>, containing xs.[i .. i+simdWidth-1]
    let u = v * v                        // Vector<float<m>>; expected: Vector<float<m^2>>
    u.CopyTo(rs, i * simdWidth)          // units mismatch

我相信我明白为什么会发生这种情况:F# 编译器怎么知道,是什么 System.Numerics.Vector<'T>.op_Multiply什么算术规则适用?它实际上可以是任何操作。那么它应该如何推导出正确的单位呢?

问题是 : 使这项工作的最佳方法是什么?我们如何告诉编译器适用哪些规则?

尝试 1 : 从 xs 中删除所有度量单位信息稍后再添加:
// remove all UoM from all arrays
let xsWoM = Array.map (fun x -> x / 1.0<m>) xs
// ...
// perform computation using xsWoM etc.
// ...
// add back units again
let xs = Array.map (fun x -> x * 1.0<m>) xsWoM

问题:执行不必要的计算和/或复制操作,出于性能原因无法实现对代码进行矢量化的目的。此外,在很大程度上违背了开始使用计量单位的目的。

尝试 2 : 使用内联IL改变Vector<'T>.op_Multiply的返回类型:
// reinterpret x to be of type 'b
let inline retype (x: 'a) : 'b = (# "" x: 'b #)
let inline (.*.) (u: Vector<float<'m>>) (v: Vector<float<'m>>): Vector<float<'m^2>> = u * v |> retype
// ...
let u = v .*. v // asserts type Vector<float<m^2>>

问题:不需要任何额外的操作,但使用了一个不推荐使用的特性(内联 IL)并且不是完全通用的(仅关于度量单位)。

有没有人对此有更好的解决方案* ?

*请注意,上面的示例确实是一个演示一般问题的玩具问题。实际程序解决了一个更为复杂的初值问题,涉及多种物理量。

最佳答案

编译器很擅长弄清楚如何应用乘法的单位规则,这里的问题是你有一个包装类型。在你的第一个例子中,当你写 xs |> Array.map (fun x -> x * x) ,您是根据数组的元素而不是直接在数组上描述乘法。

当您有 Vector<float<m>> ,单位附在 float而不是 Vector所以当你尝试乘以 Vector s,编译器不会将该类型视为具有任何单位。

鉴于该类公开的方法,我认为使用 Vector<'T> 没有简单的解决方法。直接但有包装类型的选项。

像这样的东西可以给你一个单位友好的向量:

type VectorWithUnits<'a, [<Measure>]'b> = 
    |VectorWithUnits of Vector<'a>

    static member inline (*) (a : VectorWithUnits<'a0, 'b0>, b : VectorWithUnits<'a0, 'b1>) 
        : VectorWithUnits<'a0, 'b0*'b1> =
        match a, b with
        |VectorWithUnits a, VectorWithUnits b -> VectorWithUnits <| a * b

在这种情况下,单位附加到向量,乘法向量按预期工作,单位行为正确。

问题是现在我们可以在 Vector<'T> 上有单独和不同的度量单位注释。并在 float本身。

您可以将具有度量单位的特定类型的数组转换为一组 Vector使用:
let toFloatVectors (array : float<'m>[]) : VectorWithUnits<float,'m>[]  =
    let arrs = array |> Array.chunkBySize (Vector<float>.Count)
    arrs |> Array.map (Array.map (float) >> Vector >> VectorWithUnits)

然后回来:
let fromFloatVectors (vectors : VectorWithUnits<float,'m>[]) : float<'m>[] =
    let arr = Array.zeroCreate<float> (Array.length vectors)
    vectors |> Array.iteri (fun i uVec ->
        match uVec with
        |VectorWithUnits vec -> vec.CopyTo arr)
    arr |> Array.map (LanguagePrimitives.FloatWithMeasure<'m>)

一个骇人听闻的替代方案:

如果放弃泛型 'T你可以做一个 float Vector通过一些相当可怕的拳击和运行时转换,表现得很好。这滥用了测量单位是在运行时不再存在的编译时构造这一事实。
type FloatVectorWithUnits<[<Measure>]'b> = 
    |FloatVectorWithUnits of Vector<float<'b>>

    static member ( * ) (a : FloatVectorWithUnits<'b0>, b : FloatVectorWithUnits<'b1>) =
        match a, b with
        |FloatVectorWithUnits a, FloatVectorWithUnits b ->
            let c, d = box a :?> Vector<float<'b0*'b1>>, box b :?> Vector<float<'b0*'b1>>
            c * d |> FloatVectorWithUnits

关于.net - 在 System.Numerics.Vector<T> 中使用 F# 度量单位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34568672/

相关文章:

f# - FRP 中的行为和事件有什么区别?

f# - 在 F# 中不可变

python - 提取包含另一个数组元素的端点数组行的矢量化方法

python - NumPy 中切片的不同起始索引

c# - XmlReader - 自关闭元素不会触发 EndElement 事件?

c# - 运行时的 Expression.Lambda 和查询生成,最简单的 "Where"示例

f# - 无法为 Microsoft.FSharp.Core.Unit 生成自动编码器。请传递一个额外的编码器

c++ - 由于 cout,GCC 4.8.2 自动矢量化失败

c# - 使用c#从代理服务器下载文件

.net - 如何将类似 Prolog 的推理引擎嵌入到 .NET 应用程序中?