如何在保持语法的同时使这种功能样式的代码更具性能

时间:2013-12-03 23:39:11

标签: c# f# functional-programming ocaml

我想写这个操作

Matrix.Multiply(source,target)
target.Add(offsets)

这种数学风格

target = Matrix * source + offsets;

然而,我也关注表现。 (事实上​​,上面的操作将在紧密的循环中运行。)我不想为每个矩阵向量乘法创建一个新向量,而为向量加法创建另一个新向量。

如何在C#或F#(或您知道的任何功能语言)中实现上述操作,以便同时实现样式和性能?

抱歉这个天真的问题。我确信这种愿望是如此根本。

4 个答案:

答案 0 :(得分:10)

一种众所周知的技术,例如在Repa高性能数值数组库的核心,是在基本的“整数二维数组”结构之上定义一个更高级别的技术延迟您要执行的操作的数据结构,以及最终执行这些操作的解释/规范化函数,应用它认为有利可图的任何优化。

具体来说,在您的示例中,我认为大多数当前答案都错过了这一点,您希望A * B + C不能重写为

add(multiply(A,B), C)

或OO styl ë

A.multiply(B).add(C)

但是

multiply_and_add(A,B,C)

其中multiply_and_add是一个specialez,低级操作,它同时执行两个操作并且更加优化。同样,众所周知,对于任意矩阵A, B, C, D,产品A * B * C * D可能更有效地计算为A * ((B * C) * D)而不是A * (B * (C * D))),具体取决于操作数维度。因此,纯粹的本地方法(在整数数组上表示每个操作)不起作用,您需要表达式 - 全局重写以获得最佳效率 - 正如C ++中的hackish模板库所做的那样。

解决方案是从int array array转移到精炼数据类型,例如:

type matrix =
  | Raw of int array array
  | Add of matrix * matrix
  | Mult of matrix * matrix

然后提供eval : matrix -> int array array函数,通过可能应用特定于域的优化来“解释”那些矩阵描述(实质上是矩阵运算的抽象语法树)。一个天真的例子:

let rec eval = function
  | Raw m -> m
  | Add(Mult(a,b), c) | Add(c, Mult(a,b)) ->
    multiply_and_add (eval a) (eval b) (eval c)
  | Add (a, b) -> add (eval a) (eval b)
  | Mult (a, b) -> mult (eval a) (eval b)

然后,您可以定义您喜欢的语法糖,以使matrix构造函数更易于阅读。

module Matrix = struct
  let (!) m = Raw m
  let (+) a b = Add (a, b)
  let (*) a b = Mult (a, b)
end

let ... = eval Matrix.(!A * !B + !C)

答案 1 :(得分:2)

假设Matrix类是你的,你可以重载运算符:

public static Matrix operator *(Matrix x, Matrix y)
{
    return Matrix.Multiply(x, y);
}

public static Matrix operator +(Matrix x, OffsetsType offsets)
{
    x.Add(offsets);
    return x;
}

所以,这个表达式(这是你问题中的一个修改,因为在你的问题中你将Matrix乘以source并且没有意义):

target = target * source + offsets;

爆发,看起来像这样:

var z = Matrix.Multiply(target, source);
z.Add(offsets);
target = z;

答案 2 :(得分:0)

如果性能是您最关心的问题,最快的方法是将Matrix定义为结构(如果尚未定义),然后使用ref / out参数而不是创建大量副本的参数。

public void Multiply(ref Matrix left, ref Matrix right, out Matrix result)

这是许多C#游戏/图形框架采用的方法。

还有一篇关于优化OpenTK矩阵乘法的非常有趣的文章,它可能会为您提供有关如何进一步加速矩阵运算的想法:Making OpenTK matrix multiplication faster

当然,如果你的矩阵类不是固定大小,那么这些信息基本上没用了。

答案 3 :(得分:-1)

您可以为班级创建自定义运算符:

http://msdn.microsoft.com/en-us/library/8edha89s.aspx

这为您提供了所需的语法,但仍然在幕后进行功能编程。性能将没有什么不同,因为.NET只会将运算符使用代码编译为函数调用。