Функции высшего порядка в AleaGPU C#

Я пытаюсь кодировать версии C# (в стиле C#) функций F# сокращения, найденных здесь:

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#, я бы возвратил Func из моего члена MultiReduce.

Другим вариантом было бы "сплющить" функцию 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();
        }
    }
}

1 ответ

Функции высшего порядка не столь распространены в C#, как в F#. Хотя существует множество примеров принятия функций в качестве аргументов, код C# редко возвращает функции в качестве результатов. Я думаю, это отчасти потому, что код выходит очень некрасивым (Func<T,U> везде) и отчасти потому, что программисты на C# обычно не привыкли к функциональному стилю и больше тяготеют к ОО-способам.

В частности, в C# нет автоматического карри / частичного применения. Вы можете думать об этом, как будто все ваши функции F# всегда имеют кортежные параметры. Фактически, именно так будет выглядеть многопараметрический метод C#, если вы вызываете его из F#.

Я также должен отметить, что функция в вашем коде, на самом деле, не является "высшим порядком". Он не принимает и не возвращает никаких функций. Вместо этого он принимает и возвращает цитаты, что совсем не одно и то же. Функция, грубо говоря, ссылка на фрагмент кода, но цитата - это структура данных. Они похожи, но они совершенно разные животные.

В C# тоже есть свои цитаты, представленные типом System.Linq.Expressions.Expression<T> (где T должен быть типом делегата). Однако это не то же самое, что цитаты F#. Со стороны F# вы можете (как бы) использовать цитату из C#, но не наоборот.
Цитаты F# и C# имеют свои сильные и слабые стороны. В частности, C# поддерживает компиляцию, F# - нет. F# поддерживает сращивание, C# - нет.

Что подводит меня к следующему пункту: вам, вероятно, нужно сращивание. Потому что вы используете opExpr в теле возвращенной цитаты, не так ли?
И C# не имеет встроенной поддержки для него. Да, теоретически возможно реализовать сплайсинг как библиотечную функцию, но по какой-то причине не существует стандарта де-факто, регулярно поддерживаемой реализации. Нам, например, пришлось катиться самостоятельно. Это тоже открытый исходный код, и довольно простой, так что не стесняйтесь его использовать.

Теперь, сказав все вышеизложенное, я хочу выразить сомнение в том, что вы могли бы использовать C# для этого вообще. Я действительно не знаю, как работает AleaGPU, но похоже, что он ожидает, что вы вернете цитату F#, которую он, вероятно, скомпилирует в код GPU. Если это так, потому что цитаты C# и F# - это две разные вещи, вы, вероятно, не сможете вернуть цитату C# в AleaGPU вместо F#. Если, конечно, у него нет отдельной поддержки.

Другие вопросы по тегам