【问题标题】:Higher order functions in AleaGPU C#AleaGPU C# 中的高阶函数
【发布时间】: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&lt;Func&lt;...&gt;&gt; 创建的表达式树,但这将非常有限。因此,要么该库在 C# 中有一些 other 方法,要么可能很难复制......

标签: c# design-patterns f# higher-order-functions aleagpu


【解决方案1】:

高阶函数在 C# 中并不像在 F# 中那样普遍。虽然有很多接受函数作为参数的例子,但C#代码很少返回函数作为结果。我想这部分是因为代码非常难看(Func&lt;T,U&gt; 无处不在),部分是因为 C# 程序员通常不习惯函数式风格,而更倾向于 OO 方式。

特别是,C# 中没有自动柯里化/部分应用程序。你可以把它想象成你所有的 F# 函数总是有元组参数。事实上,如果您从 F# 中调用多参数 C# 方法,这就是它的外观。

我还必须注意,您代码中的函数不是,实际上是“高阶”。它既不接受也不返回任何函数。相反,它接受并返回 quotations,这根本不是一回事。函数,粗略地说,是对一段代码的引用,但quotation是一个数据结构。它们看起来很相似,但它们是完全不同的动物。

C# 也有它自己的引用,由类型 System.Linq.Expressions.Expression&lt;T&gt; 表示(其中 T 必须是委托类型)。但是,它们与 F# 引用相同。从 F# 方面,您可以(排序)使用 C# 引用,但不能反过来。
F# 和 C# 引用都有其优点和缺点。特别是,C# 支持编译,F# 不支持。 F#支持拼接,C#不支持。

这让我想到了下一点:你可能需要拼接。因为您在返回的报价正文中使用了opExpr,不是吗?
而且 C# 没有对它的开箱即用支持。是的,理论上可以将拼接实现为库函数,但由于某种原因,没有事实上的标准,定期维护的实现。一方面,我们不得不自己动手。它也是开源的,而且非常简单,所以feel free to use it

现在,在说了以上所有内容之后,我想表达一个疑问,即您完全可以使用 C# 来实现这一点。我真的不知道 AleaGPU 是如何工作的,但它看起来希望你返回一个 F# 引用,然后它可能会编译成 GPU 代码。如果是这种情况,因为 C# 和 F# 引用是两个不同的东西,您可能无法将 C# 引用返回到 AleaGPU 来代替 F#。当然,除非它有单独的支持。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-07
    相关资源
    最近更新 更多