4

我正在寻找 C# 中 3x3 旋转和 4x4 变换矩阵的最有效方法matrix * matrixmatrix * vector操作。

我目前将我的矩阵存储在多维数组 ( new double[3,3], new double[4,4]) 中。我并不完全反对改变它,但如果可能的话,我想保留语法。我当前使用 3 个标准嵌套 for 循环的乘法工作正常,但可能成为瓶颈。

到目前为止我的想法:

  • Strassen 等优化算法不适用于这些尺寸
  • 在单个 4x4 乘法级别上,并行化也没有多大意义;在更高的水平上做得更好。
  • 由于边界检查效率较低,多维数组在 c# 中(曾经?)较慢,但这可以通过不安全的指针算法来克服。(我不确定这些信息的最新情况)
  • 旋转矩阵是对称的,可能有办法利用它吗?
  • 最大的收获可能是通过使用缓存局部性来实现,确保一起访问内存中靠近的值;但我不确定如何做到这一点。

因此,在我使用 unsafe、fixed 和 3 个 for 循环组合我自己的解决方案之前,是否已经有针对这个标准问题的经过测试和优化的解决方案?

还是我忽略了其他优化?

4

2 回答 2

3

这就是我使用的,它的工作速度出奇的快。

public struct Matrix3 
{
    public readonly double a11, a12, a13;
    public readonly double a21, a22, a23;
    public readonly double a31, a32, a33;
    ...
    public vec3 Multiply(vec3 rhs)
    {
        // y= A*x
        // fill vector by element
        return new vec3(
            (a11*rhs.X+a12*rhs.Y+a13*rhs.Z),
            (a21*rhs.X+a22*rhs.Y+a23*rhs.Z),
            (a31*rhs.X+a32*rhs.Y+a33*rhs.Z));
    }

    public mat3 Multiply(mat3 rhs)
    {
        // Y = A*X
        // fill matrix by row
        return new mat3(
            (a11*rhs.a11+a12*rhs.a21+a13*rhs.a31),
            (a11*rhs.a12+a12*rhs.a22+a13*rhs.a32),
            (a11*rhs.a13+a12*rhs.a23+a13*rhs.a33),

            (a21*rhs.a11+a22*rhs.a21+a23*rhs.a31),
            (a21*rhs.a12+a22*rhs.a22+a23*rhs.a32),
            (a21*rhs.a13+a22*rhs.a23+a23*rhs.a33),

            (a31*rhs.a11+a32*rhs.a21+a33*rhs.a31),
            (a31*rhs.a12+a32*rhs.a22+a33*rhs.a32),
            (a31*rhs.a13+a32*rhs.a23+a33*rhs.a33));
    }
}

wherevec3mat3是我自己的别名Vector3Matrix3存储元素的结构是字段。同样适用于 4 元素结构。我也把它编码成这样的倒数:

    public double Determinant()
    {
        return a11*(a22*a33-a23*a32)
            +a12*(a23*a31-a21*a33)
            +a13*(a21*a32-a22*a31);
    }
    /// <summary>
    /// Solves the system of equations this*x=rhs for x
    /// </summary>
    public vec3 Solve(vec3 rhs)
    {
        double D=Determinant();
        double ID=1/D;
        return new vec3(
            (((a22*a33-a23*a32)*rhs.X+(a13*a32-a12*a33)*rhs.Y+(a12*a23-a13*a22)*rhs.Z)*ID),
            -(((a21*a33-a23*a31)*rhs.X+(a13*a31-a11*a33)*rhs.Y+(a11*a23-a13*a21)*rhs.Z)*ID),
            (((a21*a32-a22*a31)*rhs.X+(a12*a31-a11*a32)*rhs.Y+(a11*a22-a12*a21)*rhs.Z)*ID));
    }
    /// <summary>
    /// Solves the system of equations this*X = rhs for X
    /// </summary>
    public mat3 Solve(mat3 rhs)
    {
        double D=Determinant();
        double ID=1/D;
        return new mat3(
            (((a22*a33-a23*a32)*rhs.a11+(a13*a32-a12*a33)*rhs.a21+(a12*a23-a13*a22)*rhs.a31)*ID),
            (((a22*a33-a23*a32)*rhs.a12+(a13*a32-a12*a33)*rhs.a22+(a12*a23-a13*a22)*rhs.a32)*ID),
            (((a22*a33-a23*a32)*rhs.a13+(a13*a32-a12*a33)*rhs.a23+(a12*a23-a13*a22)*rhs.a33)*ID),

            -(((a21*a33-a23*a31)*rhs.a11+(a13*a31-a11*a33)*rhs.a21+(a11*a23-a13*a21)*rhs.a31)*ID),
            -(((a21*a33-a23*a31)*rhs.a12+(a13*a31-a11*a33)*rhs.a22+(a11*a23-a13*a21)*rhs.a32)*ID),
            -(((a21*a33-a23*a31)*rhs.a13+(a13*a31-a11*a33)*rhs.a23+(a11*a23-a13*a21)*rhs.a33)*ID),

            (((a21*a32-a22*a31)*rhs.a11+(a12*a31-a11*a32)*rhs.a21+(a11*a22-a12*a21)*rhs.a31)*ID),
            (((a21*a32-a22*a31)*rhs.a12+(a12*a31-a11*a32)*rhs.a22+(a11*a22-a12*a21)*rhs.a32)*ID),
            (((a21*a32-a22*a31)*rhs.a13+(a12*a31-a11*a32)*rhs.a23+(a11*a22-a12*a21)*rhs.a33)*ID));
    }
于 2013-04-08T12:58:22.040 回答
1

如果您希望它在 Microsoft C# 中用于性能,我会;

  • 展开循环。不要使用循环,而是全部写出来这对于这些较小的固定大小乘法是可行的。
  • 应用第一个建议后,尝试 Unsafe Fixed 版本(这对于许多快速数组访问仍然很重要)

对于 Mono,Mono.SIMD 库可能值得一看。

如果您可以同时卸载其中的许多,那么使用 GPU 的并行性将是一个不错的选择。对于 C#,我会查看http://www.hybriddsp.com/Products/CUDAfyNET.aspx但可能还有其他人。我还没有从 C# 完成任何 GPU 的东西,但这将是我的起点。

于 2013-04-08T11:00:56.653 回答