返回首页

简介
这是在深夜。很晚。事实上,它是这么晚了,我应该说这几乎是在清晨,但我沉浸在我有太多时间延迟的任务:??延长我自己的数学库,直接用C#编写。
首先,我必须说,这篇文章不假装什么太seriuos,只是一些有关数学,C#和。NET随笔。一些好的想法,和一些不好的想法... ... ;)背景为什么写我自己的库吗?
,因为我觉得这是很好的依赖图形API尽可能少。为什么呢?因为API是废弃不时(托管DirectX发生),或发展,或改变,或以某种形式重新出现(似乎是即将发生的托管DirectX的某些部分,通过Windows 7的代码包),或者你只是决定使用另一个API,或你想你的代码是用XNA与DirectX兼容。因为我希望能够调试我的数学胆。由于编写自己的库(即使你复制它的大部分地区),你会学到很多东西。因为看到事物的内部,你会发现一些任务多少工作,并调用它们之前你会觉得它的两倍??
你需要更多的原因?等待一分钟。更快的代码,不使用API​​?
对于CPU相关的东西,是的,它是可能的。
新华社(或DirectX)没有魔术。他们只是计算你会做的事情。但是,当然,你必须选择你这样做的每个task.If的最好的算法,你会与在API写一个快速中的代码,但问题是,你就会知道如何在胆工作,你将有一个更详细的东西成本的想法,你会优化你的代码。此外,您将能够调整位为您的特定需求,增加多一点的速度。
但是,记住,像微软这样的公司有数百或数千名在其产品中最好的工作的工程师,所以如果有一个更好的方法做了一些API将它。你需要好!
让我们来看看的优化,你需要达到使尽可能快(或更快)的API中包含的那些比你自己的数学库的一个例子。我们将包括一些矩阵运算,并与一些不同的算法速度测试。关于仿射变换矩阵的注意事项
在3D图形应用程序,转换通常是建立从初等矩阵的组合(这初等矩阵:翻译,旋转,尺度和身份矩阵)。矩阵建成这样,总是在仿射转换(右列的矩阵,矩阵0,0,0,1,如果使用行表示矩阵)。
在这篇文章中的一些算法的优化,是唯一有效的仿射变换矩阵。你应该避免使用非仿射矩阵。这是透视投影矩阵的情况下,不属于仿射变换组。有关JIT编译。NET的数学性能的说明
这是一个非常有趣的问题:是局部变量的速度比成员变量?
起初,人们可以说他们是不会,因为它们必须重新创建的每个方法被称为。但是,正如指出,而JIT编译器可以注册的本地变量,它不能做相同的成员字段。因此,我们应该说,是的,他们的数学计算,当谈论。正如你可以看到,在测试中,它使一个显着差异。所以:
如果您在。NET开发和你有一个性能的关键数学。计算方法,你应该使用局部变量副本,以加快计算 事实上,如果你得到一个XNA代码内窥,Matrix类,这是性能的关键,里面的每一个非静态方法,使每个成员字段的一个本地副本。部分#1矩阵的行列式的计算一般方法(基于图形宝石我)
下面是一个漂亮的直接计算矩阵的行列式的算法。它主要设计用于非实时应用,因为它很慢,并采用双- precission。它计算一个4x4 3x3的方法为基础的决定因素,然后在2x2和等等。它是在{A}使用的矩阵求逆算法的基础。

public double Determinant4x4()

{

double ans;

double a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4;

a1 = this.M11; b1 = this.M12;

c1 = this.M13; d1 = this.M14;

a2 = this.M21; b2 = this.M22;

c2 = this.M23; d2 = this.M24;

a3 = this.M31; b3 = this.M32;

c3 = this.M33; d3 = this.M34;

a4 = this.M41; b4 = this.M42;

c4 = this.M43; d4 = this.M44;

ans = a1 * Determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4)

- b1 * Determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4)

+ c1 * Determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4)

- d1 * Determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);

return ans;

}
{C}
public double Determinant2x2(double a, double b, double c, double d)

{

double ans;

ans = a * d - b * c;

return ans;

}
一般方法(实时应用进行了优化)
第二个版本是一个完整的,一般的方法,但为实时应用而优化。它的速度要快得多,因为计算,因为分组numXX变量重复mults,节约乘法,并增加了。
        

public float Determinant()

{

float num18 = (M33 * M44) - (M34 * M43);

float num17 = (M32 * M44) - (M34 * M42);

float num16 = (M32 * M43) - (M33 * M42);

float num15 = (M31 * M44) - (M34 * M41);

float num14 = (M31 * M43) - (M33 * M41);

float num13 = (M31 * M42) - (M32 * M41);

return ((((M11 * (((M22 * num18) - (M23 * num17)) + (M24 * num16))) - (M12 * (((M21 * num18) -

(M23 * num15)) + (M24 * num14)))) + (M13 * (((M21 * num17) - (M22 * num15)) +

(M24 * num13)))) - (M14 * (((M21 * num16) - (M22 * num14)) + (M23 * num13))));

}


这是一个在XNA或SlimDX使用。它是标准事实上。优化I(更快的记忆体存取)
正如在介绍中,局部变量与成员字段的性能,我们将采用这一概念,上述方法进行了优化:
public float Determinant()

{

float L11 = this.M11;

float L12 = this.M12;

float L13 = this.M13;

float L14 = this.M14;

float L21 = this.M21;

float L22 = this.M22;

float L23 = this.M23;

float L24 = this.M24;

float L31 = this.M31;

float L32 = this.M32;

float L33 = this.M33;

float L34 = this.M34;

float L41 = this.M41;

float L42 = this.M42;

float L43 = this.M43;

float L44 = this.M44;

float C1 = (L33 * L44) - (L34 * L43);

float C2 = (L32 * L44) - (L34 * L42);

float C3 = (L32 * L43) - (L33 * L42);

float C4 = (L31 * L44) - (L34 * L41);

float C5 = (L31 * L43) - (L33 * L41);

float C6 = (L31 * L42) - (L32 * L41);

return ((((L11 * (((L22 * C1) - (L23 * C2)) + (L24 * C3))) - (L12 * (((L21 * C1) -

(L23 * C4)) + (L24 * C5)))) + (L13 * (((L21 * C2) - (L22 * C4)) + (L24 * C6)))) -

(L14 * (((L21 * C3) - (L22 * C5)) + (L23 * C6))));

}

见的性能测试结果的测试。优化二(基于图形宝石二,仿射转换)
下面的算法是一个内图形宝石,一个仿射矩阵求逆算法(见完整的算法下一篇)的代码直接翻译。这使得只有12个mults,7增加了。请记住,这种方法是唯一有效的仿射变换(见进一步资料介绍):
public float Determinant()

{

return (this.M11 * this.M22 * this.M33) +

(this.M12 * this.M23 * this.M31) +

(this.M13 * this.M21 * this.M32) -

(this.M13 * this.M22 * this.M31) -

(this.M12 * this.M21 * this.M33) -

(this.M11 * this.M23 * this.M32);

}

测试
所以,它的时间来衡量这个小男孩。
效果
测试是计算一个4x4仿射矩阵的行列式100万次,在一个英特尔酷睿2四核Q9450 2.66GHz的3 GB(非多线程)。通用方法(图形宝石我,非实时)1分钟:0.9秒一般方法(实时)2.6832秒优化I(内存访问)2.496秒优化第二(仿射变换)1.404秒
可靠性
可靠性试验是两个不同的matrices.There的是这里没有惊喜,所有的算法工作。以下是测试(仿射变换)和每种算法的计算结果(行列式)所使用的矩阵。
矩阵1:0.7071068-0.2357022760.66666670.00.00.94280910.33333334 -0.7071068-0.2357022760.66666670.00.0 0.0 1
- 通用方法(图形宝石我)1.0000001641611829一般方法(实时)1.00000012优化I(内存访问)1.00000012优化第二(仿射变换)1.00000012
黑客帝国2:9.8480190.100611590.00.0-3.505783560.2826257650.00.00.00.094.220.00.0 0.00.01 - 通用方法(图形宝石我) 295.47639778127973一般方法(实时)295.4764 优化I(内存访问)295.4764优化第二(仿射变换)295.4764#2:矩阵求逆计算
(通常)是最昂贵的矩阵运算计算一个矩阵的逆。将开始与此一般算法,并研究了优化后的情侣:一般方法
这个算法(解释{A2}),是这样的:
public Matrix Inverse()

{

float determinant = this.Determinant();

if (determinant == 0)

throw new System.InvalidOperationException("Matrix has zero determinant (singular matrix). Inverse doesn麓t exist");



Matrix b = new Matrix();

b.M11 = M22 * M33 * M44 + M23 * M34 * M42 + M24 * M32 * M43 - M22 * M34 * M43 - M23 * M32 * M44 - M24 * M33 * M42;

b.M12 = M12 * M34 * M43 + M13 * M32 * M44 + M14 * M33 * M42 - M12 * M33 * M44 - M13 * M34 * M42 - M14 * M32 * M43;

b.M13 = M12 * M23 * M44 + M13 * M24 * M42 + M14 * M22 * M43 - M12 * M24 * M43 - M13 * M22 * M44 - M14 * M23 * M42;

b.M14 = M12 * M24 * M33 + M13 * M22 * M34 + M14 * M23 * M32 - M12 * M23 * M34 - M13 * M24 * M32 - M14 * M22 * M33;



 

b.M21 = M21 * M34 * M43 + M23 * M31 * M44 + M24 * M33 * M41 - M21 * M33 * M44 - M23 * M34 * M41 - M24 * M31 * M43;

b.M22 = M11 * M33 * M44 + M13 * M34 * M41 + M14 * M31 * M43 - M11 * M34 * M43 - M13 * M31 * M44 - M14 * M33 * M41;

b.M23 = M11 * M24 * M43 + M13 * M21 * M44 + M14 * M23 * M41 - M11 * M23 * M44 - M13 * M24 * M41 - M14 * M21 * M43;

b.M24 = M11 * M23 * M34 + M13 * M24 * M31 + M14 * M21 * M33 - M11 * M24 * M33 - M13 * M21 * M34 - M14 * M23 * M31;



 

b.M31 = M21 * M32 * M44 + M22 * M34 * M41 + M24 * M31 * M42 - M21 * M34 * M42 - M22 * M31 * M44 - M24 * M32 * M41;

b.M32 = M11 * M34 * M42 + M12 * M31 * M44 + M14 * M32 * M41 - M11 * M32 * M44 - M12 * M34 * M41 - M14 * M31 * M42;

b.M33 = M11 * M22 * M44 + M12 * M24 * M41 + M14 * M21 * M42 - M11 * M24 * M42 - M12 * M21 * M44 - M14 * M22 * M41;

b.M34 = M11 * M24 * M32 + M12 * M21 * M34 + M14 * M22 * M31 - M11 * M22 * M34 - M12 * M24 * M31 - M14 * M21 * M32;





b.M41 = M21 * M33 * M42 + M22 * M31 * M43 + M23 * M32 * M41 - M21 * M32 * M43 - M22 * M33 * M41 - M23 * M31 * M42;

b.M42 = M11 * M32 * M43 + M12 * M33 * M41 + M13 * M31 * M42 - M11 * M33 * M42 - M12 * M31 * M43 - M13 * M32 * M41;

b.M43 = M11 * M23 * M42 + M12 * M21 * M43 + M13 * M22 * M41 - M11 * M22 * M43 - M12 * M23 * M41 - M13 * M21 * M42;

b.M44 = M11 * M22 * M33 + M12 * M23 * M31 + M13 * M21 * M32 - M11 * M23 * M32 - M12 * M21 * M33 - M13 * M22 * M31;



//<span class="code-comment"> This last operation involves a float * Matrix multiplication

</span>return (1f / determinant) * b;

}


您可以使用任何本条第1部分中提出的方法计算行列式。优化I(内存访问和节省一些操作)
这种方法适用于相同的概念,而是使用局部变量和包装在局部变量重复计算:优化第二(仿射变换凯文吴文章,图形宝石基于第二)
图形宝石系列丛书是必须在任何图形或游戏开发人员的书架。您可以在本书中,我谈论{A3}。
因此,吴先生purposed的算法看起来像以下(移植到C#中,原在C):
public static bool Inverse(Matrix min, Matrix mout)

{

float det_1;

float pos, neg, temp;

double PRECISION_LIMIT = 1.0e-15;

//<span class="code-comment"> Calculate determinand

</span>pos = neg = 0.0f;

temp = min.M11 * min.M22 * min.M33;

if (temp >= 0.0)

pos += temp;

else

neg += temp;



temp = min.M12 * min.M23 * min.M31;

if (temp >

请记住,该算法是唯一有效的仿射变换。当然,可以在这里完成一些内存访问优化,使用局部变量在第1部分解释。优化三(旋转和平移组成矩阵)
如果你知道你的矩阵是旋转和平移只组成,你可以应用(甚至更快)的方法,从而避免了计算行列式,并利用这种矩阵的一些性质。你可以阅读所有的信息{A4}。
public Matrix Inverse()

{

Matrix r = new Matrix();

r.M11 = this.M11;

r.M12 = this.M21;

r.M13 = this.M31;

r.M14 = 0f;

r.M21 = this.M12;

r.M22 = this.M22;

r.M23 = this.M32;

r.M24 = 0f;

r.M31 = this.M13;

r.M32 = this.M23;

r.M33 = this.M33;

r.M34 = 0f;

r.M41 = -(r.M11 * this.M41 + r.M21 * this.M42 + r.M31 * this.M43); 

r.M42 = -(r.M12 * this.M41 + r.M22 * this.M42 + r.M32 * this.M43); 

r.M43 = -(r.M13 * this.M41 + r.M23 * this.M42 + r.M33 * this.M43); 

r.M44 = 1f;

return r;

}

额外的优化
如果你需要它,其他的优化​​可以包括在这个算法中,检测特殊矩阵的逆可以计算得更快。这是对角矩阵(对角线应倒置使用1/value项),或正交矩阵(其倒数等于它的转置)的情况下。测试
我们做了一个非常简单的测试,英特尔酷睿2四核Q9450 2.66GHz的50.000.000(50万)仿射变换的逆计算,与3 GB(非多线程)。
一般方法11.523秒优化I(内存访问和运算。储蓄)5.4912秒优化第二(只仿射变换)2.7144秒优化第三(仅旋转和平移)1.326秒 结论
搜索了一下,确实有可能达到您的图形API的性能和上述的优势,有自己的库(和建设)是数不胜数。参考文献
关于提到的算法或书籍的进一步信息,您可以检查以下几个环节:{A6}{A7}{A8}{A9}{A10}
小心!

回答

评论会员:dbaechtel 时间:2012/01/25
退房博士Wildberger理性三角的书。他已大大simplifiy工作与几何和三角
的方法
评论会员:。乔纳森彗星迪金森 时间:2012/01/25
你应该张贴在Q号码的东西(没有时间亲自)。 {A11}

他问一个问题是一个五分钟的傻瓜。他不问一个问题仍然是一个傻瓜永远。 [Chineese谚语]

乔纳森迪金森彗星(C#软件工程师)