【问题标题】:Convenient and fast 3D vector math in JavaJava中方便快捷的3D矢量数学
【发布时间】:2014-12-11 00:05:15
【问题描述】:

总的来说,我更喜欢用 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 ) );

这对性能不是很好,因为它将新对象分配为临时变量,其中“本地”方法无法使用。我可以通过为 fused-multiply-add 定义新方法来优化它,例如:

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向量为算术运算的所有可能组合定义专门的方法不是很方便……比如:

  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 );
   }
}

在这两种情况下,都有取消引用指向此对象的指针的性能成本。它还使小行星天体消耗更多内存

还有调用对象方法的性能损失(即使我尝试将它们设为“最终”和“静态”,以便 JIT 可以有效地内联它们)。根据我的测试,使用 float3.mult() 比仅乘以 3 个浮点数慢 2-3 倍。

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

你推荐什么策略在 Java 中进行向量数学,既高效又方便(~可读)?

【问题讨论】:

  • @Alan,不,这不是关于性能的问题,这是关于语法的问题。 OP 想用 Java 编写数学表达式,看起来就像他在纸上写的一样。
  • @James,更准确地说:这是一个关于性能的问题,也是一个语法问题。然而,我承认,我可能过于强调前者而不是后者。
  • 这是关于妥协:如果我不想写出可读性好的向量数学性能很糟糕。
  • @ProkopHapala,这就是问题所在:在我看来,你可以用 Java 编写 a) 快速、b) 可读和 c) 的矩阵代码......问题是你只能得到选择两个。我通常要么用浮点数编写它,要么根据需要多少向量数学来获取我可以使用的性能最好的矩阵库。无论哪种方式,我都接受丑陋的矩阵数学代码(与 matlab/octave 相比)并彻底评论它。我玩过的另一种方法是在 C/C++ 中编写足够大的矩阵位并使用 JNI 调用。

标签: java performance math game-physics


【解决方案1】:

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

您是如何衡量绩效的?所有的优化都需要一段时间;如果您的测量时间不到几秒钟,那么就忘记它吧。

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

关于丑:当然,像addLocal_vec_times_scalar 这样的名字一定很丑。像addProduct 这样的东西就可以了。

调用对象方法也有性能损失(即使我尝试将它们设为“最终”和“静态”,以便 JIT 可以有效地内联它们)

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

你推荐什么策略在 Java 中进行向量数学,既高效又方便(~可读)?

不要试图让它看起来像 C。定义一些方法,如融合乘法并忽略罕见的情况。如果你使用像float3.addLocal_vec1_times_vec2_times_scalar 这样的名字,那它一定很丑。怎么了

 vec1.addProduct(vec2, vec3, someScalar)

【讨论】:

  • 这不是关于丑陋的名称,而是关于代码的通用性(可重用性)。这些方法只是为了专门化。我不喜欢一次又一次地为每个参数组合编写几乎相同的代码。
  • @ProkopHapala 我理解,但是没有那么多常见的参数组合,你可能会一直使用其中的 5-10 个,而忽略其他的低效率(如果有的话;我可以)想出超过 10 种有用的组合)。如果你的向量很大,那么你可以使用更智能的东西,但是使用 float3 就没有空间了。
猜你喜欢
  • 2022-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-03
  • 1970-01-01
  • 2015-12-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多