我正在尝试编写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。
另一个选择是&#34;展平&#34; 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&lt; ..&gt;因为我没有在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();
}
}
}
答案 0 :(得分:1)
高阶函数在C#中几乎不像在F#中那样普遍存在。虽然有很多接受函数作为参数的例子,但C#代码很少将函数作为结果返回。我想这部分是因为代码非常丑陋(Func<T,U>
无处不在),部分原因是因为C#程序员通常不习惯功能风格,更倾向于OO方式。
特别是,C#中没有自动currying /部分应用。您可以将其视为所有F#函数始终具有tupled参数。事实上,如果你从F#调用它,那么多参数C#方法会是什么样子。
我还必须注意,代码中的函数不是,事实上,&#34;更高阶&#34;。它既不接受也不返回任何功能。相反,它接受并返回引用,这完全不是一回事。粗略地说,函数是对一段代码的引用,但quotation是一种数据结构。它们看起来很相似,但它们是完全不同的动物。
C#也有自己的引用,由类型System.Linq.Expressions.Expression<T>
表示(其中T必须是委托类型)。但是,它们不与F#引用相同。从F#侧,您可以(sorta)使用C#引用,但不能反过来
F#和C#引用都有自己的优点和缺点。特别是,C#支持编译,F#不支持。 F#支持拼接,C#没有。
这让我想到了下一点:你可能需要拼接。因为您在退回的报价单中使用了opExpr
,所以不是吗?
C#并没有开箱即用的支持。是的,理论上可以将拼接实现为库函数,但由于某种原因,没有事实上的标准,定期维护的实现。举个例子,我们不得不自己动手。它也是开源的,非常简单,所以feel free to use it。
现在,已经说了以上所有内容,我想表达一个疑问,你可以根本使用C#。我真的不知道AleaGPU是如何工作的,但看起来它希望你返回一个F#引用,然后它可能会编译成GPU代码。如果是这种情况,因为C#和F#引用是两个不同的东西,你可能无法将一个C#引用返回到AleaGPU而不是F#one。当然,除非它有单独的支持。