编辑:添加unsafe
了 C# 代码版本。谢谢大家的建议,unsafe
C# 代码运行速度更快,但只有 3% 左右。
短版:我用 C# 和 Objective-C 编写了一些基准代码,并在 iPad 3 上对其进行了测试。MonoTouch/C# 版本比 Objective-C 中的相同代码需要多 50%-150% 的时间来执行。这是我的问题:我可以编写比我用于基准测试的代码(见下文)执行得更快的 C# 代码,还是这是由某些固有的 MonoTouch/Obj-C 差异引起的?
长版:我刚刚为一个计划中的多平台游戏写了一个小原型。将游戏核心从 Windows/.NET 移植到 iPad 3/MonoTouch 后,我注意到代码在 iPad 上运行的速度慢了很多。游戏核心的某些关键部分在 iPad CPU 上的运行速度比在 Intel CPU 上慢约 10 倍(这似乎是正常的,因为 iPad 使用 ARM 处理器运行)。
然而,由于这对我们的游戏来说是一个主要问题,所以我在 iPad 3 上运行了一个小型基准测试,一次使用MonoTouch,然后使用普通的Objective-C进行同样的测试。基准做了很多简单的float
添加和float
数组查找。由于 MonoTouch 是经过 GC 处理的,我预计会看到有利于 Obj-C 的微小差异,但令我惊讶的是,MonoTouch 代码需要比 Obj-C 代码更多的时间来运行。准确地说:
- Obj-C代码需要在
47'647 ms
DEBUG 模式和27'162 ms
RELEASE 模式下运行。 - MonoTouch代码无需使用在 DEBUG 模式和RELEASE 模式下运行所需的
unsafe
指针。116'885 ms
40'002 ms
- 带有指针的MonoTouch代码需要在 DEBUG 模式和RELEASE 模式下运行。
unsafe
90'372 ms
38'764 ms
当然,RELEASE模式是我关心的。
LLVM
考虑到 MonoTouch 编译为与Obj-C相同的本机代码,这种差异对我来说似乎有点高。
这是我使用的 Obj-C 代码:
int i, j;
long time = GetTimeMs64();
float * arr = (float *) malloc(10000 * sizeof(float)); // 10'000
for (j = 0; j < 100000; j++) { // 100'000
arr[0] = 0;
arr[1] = 1;
for (i = 2; i < 10000; i++) { // 10'000
arr[i] = arr[i - 2] + arr[i - 1];
if (arr[i] > 2000000000) { // prevent arithm. overflow
arr[i - 1] = 0;
arr[i] = 1;
}
}
}
long time2 = GetTimeMs64() - time;
GetTimeMs64()
使用. <sys/time.h>
_gettimeofday
这是我的 C#/MonoTouch 代码,在unsafe
版本中:
var array = new float[10000]; // 10'000
var watch = System.Diagnostics.Stopwatch.StartNew();
fixed (float* arr = array)
{
for (int j = 0; j < 100000; j++) // 100'000
{
*(arr + 0) = 0;
*(arr + 1) = 1;
for (int i = 2; i < 10000; i++) // 10'000
{
*(arr + i) = *(arr + i - 2) + *(arr + i - 1);
if (*(arr + i) > 2000000000) // prevent arithm. overflow
{
*(arr + i - 1) = 0;
*(arr + i) = 1;
}
}
}
}
watch.Stop();
编辑 2:这是我们从 Xamarin 得到的答案:
这个特定示例存在两个影响单声道性能的问题。
首先,如果您使用的是默认的 MonoTouch 编译器,而不是 LLVM,则可能会出现较低的性能,因为它针对的是编译速度而不是执行速度。LLVM 会给你更好的结果。
其次,mono 符合有关浮点的 ECMA 规范,并以双精度进行所有计算。与使用浮点数的 C 代码相比,这通常具有可衡量的性能成本。
我们一直在寻找在不影响正确性的情况下放松双精度性能的方法,或者至少有一个选择机制。