【发布时间】:2016-04-06 17:43:55
【问题描述】:
我很难将 F# 度量单位与 System.Numerics.Vector<'T> 类型结合使用。让我们看一个玩具问题:假设我们有一个float<m>[] 类型的数组xs,并且出于某种原因我们想要对其所有组件进行平方,从而得到一个float<m^2>[] 类型的数组。这与标量代码完美配合:
xs |> Array.map (fun x -> x * x) // float<m^2>[]
现在假设我们想通过使用 SIMD 在System.Numerics.Vector<float>.Count 大小的块中执行乘法来向量化这个操作,例如:
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)并且不是完全通用的(仅与测量单位有关)。
有没有人对此有更好的解决方案*?
*请注意,上面的示例确实是一个玩具问题,用于演示一般问题。实际程序解决了一个更复杂的初值问题,涉及多种物理量。
【问题讨论】:
标签: .net f# vectorization simd units-of-measurement