Функции высшего порядка в 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#. Если, конечно, у него нет отдельной поддержки.