我正在尝试编写此处找到的 F# reduce 函数的 C# 版本(以 C# 样式):
https://github.com/quantalea/AleaGPUTutorial/tree/master/src/fsharp/examples/generic_reduce
更具体到我的问题,以这个函数为例:
let multiReduce (opExpr:Expr<'T -> 'T -> 'T>) numWarps =
let warpStride = WARP_SIZE + WARP_SIZE / 2 + 1
let sharedSize = numwarps * warpStride
<@ fun tid (x:'T) ->
// stuff
@>
我主要是一个 F# 人,我不太确定应该如何在 C# 中编写这些函数。对于 C# 版本,multiReduce 函数将是类成员。因此,如果我想对 F# 代码进行更直接的翻译,我会从我的 MultiReduce 成员返回一个 Func。
另一种选择是“展平” multiReduce 函数,这样我的 C# 成员版本就会有两个额外的参数。所以...
public T MultiReduce(Func<T,T,T> op, int numWarps, int tid, T x)
{
// stuff
}
但我不认为这在所有情况下都适用于 AleaGPU 编码,因为 F# 版本中引用的表达式是一个设备函数。您需要嵌套函数结构才能将某些变量的分配与函数的实际调用分开。
我看到的另一种方法是创建一个 MultiReduce 类并将 opExpr 和 numWarps 作为字段,然后将引用中的函数作为类成员。
那么像这样的高阶函数一般是如何在 AleaGPU-C# 中实现的呢?我不认为在任何地方都返回 Func<..> 是件好事,因为我在 C# 编码中看不到这点。AleaGPU 是一个可以接受的特例吗?
一个基本的 AleaGPU C# 实现如下所示:
internal class TransformModule<T> : ILGPUModule
{
private readonly Func<T, T> op;
public TransformModule(GPUModuleTarget target, Func<T, T> opFunc)
: base(target)
{
op = opFunc;
}
[Kernel]
public void Kernel(int n, deviceptr<T> x, deviceptr<T> y)
{
var start = blockIdx.x * blockDim.x + threadIdx.x;
var stride = gridDim.x * blockDim.x;
for (var i = start; i < n; i += stride)
y[i] = op(x[i]);
}
public void Apply(int n, deviceptr<T> x, deviceptr<T> y)
{
const int blockSize = 256;
var numSm = this.GPUWorker.Device.Attributes.MULTIPROCESSOR_COUNT;
var gridSize = Math.Min(16 * numSm, Common.divup(n, blockSize));
var lp = new LaunchParam(gridSize, blockSize);
GPULaunch(Kernel, lp, n, x, y);
}
public T[] Apply(T[] x)
{
using (var dx = GPUWorker.Malloc(x))
using (var dy = GPUWorker.Malloc<T>(x.Length))
{
Apply(x.Length, dx.Ptr, dy.Ptr);
return dy.Gather();
}
}
}