我想写这个操作
Matrix.Multiply(source,target)
target.Add(offsets)
这种数学风格
target = Matrix * source + offsets;
然而,我也关注表现。 (事实上,上面的操作将在紧密的循环中运行。)我不想为每个矩阵向量乘法创建一个新向量,而为向量加法创建另一个新向量。
如何在C#或F#(或您知道的任何功能语言)中实现上述操作,以便同时实现样式和性能?
抱歉这个天真的问题。我确信这种愿望是如此根本。
答案 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只会将运算符使用代码编译为函数调用。