Java中方便快捷的3D矢量数学

时间:2014-10-15 19:30:07

标签: java performance math game-physics

一般来说,我喜欢用Java编程比C ++更多,因为链接库更容易(没有"依赖地狱")并且因为有很多很好的软件包开箱即用。我也喜欢jMonkey和Processing等java工具。

然而,我经常用物理学做一些事情,我需要用3D矢量进行快速数学运算。而且我没有找到任何方便的方法如何在Java中实现性能高效和可读性(C ++构造如宏,重载运算符,结构和通过引用传递变量是非常有用的工具)。

例如中心力场中质量粒子的蛙跳积分器。在C ++中我可以做这样的事情(使用类型为float3的重载运算符):

float   ir2      =  1.0f/dot(vec_pos,vec_pos);
float   ir       =  sqrt(ir2);
float3  vec_G    = -vec_pos / (ir2*ir);
        vec_v   += vec_G*dt;
        vec_pos += vec_v*dt; 
Java中的可读代码如下所示:

float   ir2      =  1.0f/vec_pos.mag2();
float   ir       =  sqrt(ir2);
float3  vec_G    =  vec_pos.mult( -ir2*ir);
        vec_v    .addLocal( vec_G.multLocal( dt ) );
        vec_pos  .addLocal( vec_v.mult     ( dt ) );

这对性能不是很好,因为它将新对象分配为临时变量其中" Local"方法不可能使用。我可以通过定义融合乘法的新方法来优化它,如:

float   ir2      =  1.0f/vec_pos.mag2();
float   ir       =  sqrt(ir2);
float3  vec_G    =  vec_pos.mult( -ir2*ir);
        vec_v    .addLocal_vec_times_scalar( vec_G, dt );
        vec_pos  .addLocal_vec_times_scalar( vec_v, dt );

但是为float3 vector的所有可能的算术运算组合定义专门的方法并不是很方便......如:

  float3.addLocal_vec1_times_vec2_times_scalar() 

如何避免动态分配临时对象的另一个策略是将此临时变量定义为一些静态全局变量(这不是很好的编码样式)或作为封闭类的属性,如:

class asteroide{
   // state variables
   float3 vec_pos;
   float3 vec_v; 
   // temporary variables
   float3 vec_G,vec_dpos;

   void update_leapfrog(float dt){
        float   ir2      =  1.0f/vec_pos.mag2();
        float   ir       =  sqrt(ir2);
        vec_G            .set_mult( vec_pos, -ir2*ir );
        vec_v            .addLocal( vec_G.multLocal( dt ) );
        dpos             .set_mult( vec_v, dt );
        vec_pos          .addLocal( dpos );
   }
}

在这两种情况下,对此对象都有解除引用指针的性能成本。它还使asteroide对象更多内存消耗

对于调用对象方法也有性能损失(即使我尝试制作它们"最终"以及"静态"因此JIT可以有效地内联它们)。根据我的测试,使用float3.mult()比3浮点数的乘法慢2-3倍。

所以我经常最终只使用float 编写复杂的向量代数计算,以避免这种性能损失。 :(((但它完全不可读。以这种方式进行刚体动力学和空气动力学计算是痛苦的屁股。它与40年前的Fortran77程序一样糟糕!!!! (仅仅是为了好奇,参见例如Xfoil的代码http://web.mit.edu/drela/Public/web/xfoil/

您推荐使用Java进行矢量数学的策略既有效又方便(可读)?

1 个答案:

答案 0 :(得分:0)

也许不是一个真正的答案,但是评论的时间太长了。

你是如何衡量表现的?所有优化都需要一段时间;如果你的测量时间不到几秒钟,那就忘了吧。

理论上,JIT应该能够优化不必要的分配。

关于丑陋:当然,像addLocal_vec_times_scalar这样的名字一定是丑陋的。像addProduct这样的东西会。

  

调用对象方法也会有性能损失(即使我尝试将它们设置为“final”和“static”,因此JIT可以有效地内联它们)

这不是必需的,因为JIT可以看到该方法是否在某处被覆盖。

  

您推荐使用Java进行矢量数学的策略既有效又方便(可读)?

不要试图让它看起来像C.定义一些方法,如融合乘法和忽略罕见的情况。如果您使用的是float3.addLocal_vec1_times_vec2_times_scalar这样的名称,那一定是丑陋的。

有什么问题
 vec1.addProduct(vec2, vec3, someScalar)