Java C#和#x27;它在Unity中的总体性能似乎比基于JVM的语言慢几个数量级

Java C#和#x27;它在Unity中的总体性能似乎比基于JVM的语言慢几个数量级,java,c#,performance,unity3d,kotlin,Java,C#,Performance,Unity3d,Kotlin,在Unity中,我注意到,与Kotlin中的类似实现相比,我在某些代码逻辑中的性能一直很差。在分析之后,我怀疑语言/运行时本身可能会以某种方式变慢。因此,我在Kotlin和C#中制定了一个非常简短的基准来衡量基本运营的绩效: kotlin部分如下。请注意,Matrix4和Vector3是libGDX(Java/Kotlin的游戏库)类,它们只是数据容器。mul函数将矩阵与向量相乘,并将结果就地存储回向量中 fun基准(a:Matrix4,b:List){ var i=0; 而(i

在Unity中,我注意到,与Kotlin中的类似实现相比,我在某些代码逻辑中的性能一直很差。在分析之后,我怀疑语言/运行时本身可能会以某种方式变慢。因此,我在Kotlin和C#中制定了一个非常简短的基准来衡量基本运营的绩效:

kotlin部分如下。请注意,
Matrix4
Vector3
是libGDX(Java/Kotlin的游戏库)类,它们只是数据容器。mul函数将矩阵与向量相乘,并将结果就地存储回向量中

fun基准(a:Matrix4,b:List){
var i=0;
而(i<100000){
b[i].mul(a);
++一,;
}
}
变量a=Matrix4(浮动阵列of(1f、2f、3f、4f、3f、2f、1f、2f、3f、4f、3f、2f、1f、2f、3f、4f))
var b=List();
对于(i在0..100000中){
b、 添加(矢量3(3f、2f、1f));
}
//热身准时制
对于(0..9中的i){
基准(a、b)
}
var t:Double=0.0;
对于(0..9中的i){
t+=被测时间{
基准(a、b)
}.toDouble()
}
println(t/10.0/1000000.0)//毫秒
统一C#部分如下。请注意,
M4
V3
是为匹配libGDX而创建的帮助器类

private void基准(M4 a,列表b)
{
var i=0;
而(i<100000)
{
b[i].mul(a);
++一,;
}
}
变量a=新M4(1f、2f、3f、4f、3f、2f、1f、2f、3f、4f、3f、2f、1f、2f、3f、4f);
var b=新列表();
对于(int i=0;i<100000;++i)
{
b、 添加(新V3(3,2,1));
}
//热身准时制
对于(int i=0;i<10;++i)
{
基准(a、b);
}
var t=0.0;
对于(int i=0;i<10;++i)
{
var s=(双)nanoTime();
基准(a、b);
var e=(双)nanoTime();
t+=e-s;
}
Debug.Log(t/10.0/1000000.0);//毫秒
mul
的实现是为了匹配libGDX的精确实现()

该设备是2015年年中的MacBook Pro。Unity版本是2020.3.0f1,使用Mono后端构建为OSX单机版,而不是开发构建

结果如下:

  • 科特林:0.3658762ms
  • Unity C#:1.74067ms(几乎慢4倍)。如果我将
    M4
    V3
    更改为struct而不是class,它会变得更慢:2.51ms(几乎慢6倍)

造成如此显著差异的原因是什么?

好吧,像这样的事情存在是有原因的,与之相关。当使用通用代码时(为了“看穿”原语),运行时并不特别适合优化通用数字处理。Java虚拟机在优化方面也比.NET虚拟机领先一大步,尽管该部门正在做大量的工作。这两种实现并不相同。使用双倍计数时间是不安全的,并且会受到缩放和舍入问题的影响。改用秒表。SIMD CPU指令和使用它们的类型(如Vector3)的重要性怎么强调都不够。自2000年代以来的所有CPU都可以同时在多个浮点/整数/双精度上工作。他们还通过4x4矩阵乘法优化了3D变换。Vector3和Matrix4不仅仅是为了方便。它们的许多操作实际上是使用SIMD执行的instructions@PanagiotisKanavos我曾经在下面找到使用秒表的
nanoTime
。另外,从libGDX的源代码()来看,它们的类似乎没有显式地为我在基准测试中调用的特定函数使用SIMD!