ObjC vs. MonoTouch浮点和数组性能

che*_*ica 4 c# objective-c xamarin.ios ipad ios

编辑:添加unsafe了C#代码的版本.谢谢你们这个建议,unsafeC#代码运行得更快,但只有3%左右.


简短版本:我在C#和Objective-C中编写了一些基准代码,并在iPad 3上进行了测试.MonoTouch/C#版本比Objective-C中的相同代码需要多50%-150%的执行时间.这是我的问题:我可以编写C#代码,其执行速度比我用于基准测试的代码(见下文)更快,还是由一些固有的MonoTouch/Obj-C差异引起的?


长版:我刚刚为计划好的多平台游戏编写了一个小型原型.在将游戏核心从Windows/.NET移植到iPad 3/MonoTouch之后,我注意到代码在iPad上运行的速度有多慢.游戏核心的某些关键部分在iPad CPU上比在英特尔CPU上慢了约10倍(这似乎是正常的,因为iPad使用ARM处理器运行).

然而,因为这是我们的游戏的一个主要问题,我跑了一个小的基准测试,在iPad 3,一旦MonoTouch的,然后用普通的同样的事情的Objective-C .基准测试执行了许多简单的float添加和float数组查找.由于MonoTouch是GC,我希望看到Obj-C有一点不同,但令我惊讶的是,MonoTouch代码比Obj-C代码需要更多的时间来运行.确切地说:

  • 所述的OBJ-C需要的代码47'647 ms,以在调试模式下运行,并27'162 ms在释放模式.
  • 不使用指针的MonoTouch代码unsafe需要116'885 ms在DEBUG模式和40'002 msRELEASE模式下运行.
  • 具有指针MonoTouch代码需要在DEBUG模式和RELEASE模式下运行.unsafe90'372 ms38'764 ms

当然,RELEASE模式是我关心的.

鉴于MonoTouch编译为与LLVMObj-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;
Run Code Online (Sandbox Code Playgroud)

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();
Run Code Online (Sandbox Code Playgroud)

编辑2:这是我们从Xamarin得到的答案:

该特定示例存在两个问题,其损害单声道性能.

首先,如果您使用的是默认的MonoTouch编译器,而不是LLVM,则可以预期性能会降低,因为它会针对编译速度而不是执行速度进行调整.LLVM将为您提供更好的结果.

其次,mono符合关于浮点的ECMA规范,并且所有计算都具有双精度.与使用浮点数的C代码相比,这通常具有可测量的性能成本.

我们一直在研究如何在不影响正确性的情况下放松双精度性能,或者至少采用选择机制.

pou*_*pou 5

就像Marc建议你应该使用unsafe代码一样,MonoTouch支持这个.NET功能.

这将删除.NET数组边界检查,这是一个不错的,更安全的.NET功能,但性能受到惩罚(因为必须检查每个数组访问).

这将使您的代码看起来更多(源代码和本机代码)看起来像Objective-C代码,性能应该更接近.我仍然建议你unsafe只在性能真正重要时使用代码(并且在测量增益值得之后).