【发布时间】:2015-03-26 15:30:23
【问题描述】:
我正在尝试编写此处找到的 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();
}
}
}
【问题讨论】:
-
我不知道 Alea 库,但 F# 代码使用引号,所以最接近 C# 的东西是使用
Expression<Func<...>>创建的表达式树,但这将非常有限。因此,要么该库在 C# 中有一些 other 方法,要么可能很难复制......
标签: c# design-patterns f# higher-order-functions aleagpu