我发现 Xamarin 声称他们在 Android 上的 Mono 实现和他们的 C# 编译应用程序比 Java 代码更快。是否有人在不同的 Android 平台上对非常相似的 Java 和 C# 代码执行实际基准测试以验证此类声明,可以发布代码和结果?
添加于 2013 年 6 月 18 日
由于没有答案,也找不到别人做的这样的基准测试,所以决定自己做测试。不幸的是,我的问题仍然“锁定”,所以我不能将其作为答案发布,只能编辑问题。请投票重新打开这个问题。对于 C#,我使用了 Xamarin.Android Ver。 4.7.09001(测试版)。源码,我测试用的所有数据和编译的APK包都在GitHub上:
Java: https ://github.com/gregko/TtsSetup_Java
C#: https://github.com/gregko/TtsSetup_C_sharp
如果有人想在其他设备或模拟器上重复我的测试,我也有兴趣了解结果。
我的测试结果
我将我的句子提取器类移植到 C#(来自我的@Voice Aloud Reader 应用程序),并对英语、俄语、法语、波兰语和捷克语的 10 个 HTML 文件运行了一些测试。每次运行对所有 10 个文件执行 5 次,下面列出了 3 种不同设备和一个模拟器的总时间。我只测试了“发布”版本,没有启用调试。
HTC Nexus One Android 2.3.7 (API 10) - CyanogenMod ROM
Java:总计时间(5 次运行):12361 毫秒,文件读取总时间:13304 毫秒
C#:总计时间(5 次运行):17504 毫秒,文件读取总计:17956 毫秒
Samsung Galaxy S2 SGH-I777(Android 4.0.4,API 15)-CyanogenMod ROM
Java:总计时间(5 次运行):8947 毫秒,文件读取总时间:9186 毫秒
C#:总计时间(5 次运行):9884 毫秒,文件读取总时间:10247 毫秒
三星 GT-N7100(Android 4.1.1 JellyBean,API 16)- 三星 ROM
Java:总计时间(5 次运行):9742 毫秒,文件读取总时间:10111 毫秒
C#:总计时间(5 次运行):10459 毫秒,文件读取总时间:10696 毫秒
模拟器 - 英特尔(Android 4.2,API 17)
Java:总计时间(5 次运行):2699 毫秒,文件读取总时间:3127 毫秒
C#:总计时间(5 次运行):2049 毫秒,文件读取总时间:2182 毫秒
模拟器 - 英特尔(Android 2.3.7,API 10)
Java:总计时间(5 次运行):2992 毫秒,文件读取总时间:3591 毫秒
C#:总计时间(5 次运行):2049 毫秒,文件读取总时间:2257 毫秒
模拟器 - Arm(Android 4.0.4,API 15)
Java:总计时间(5 次运行):41751 毫秒,文件读取总时间:43866 毫秒
C#:总计时间(5 次运行):44136 毫秒,文件读取总时间:45109 毫秒
简要讨论
我的测试代码主要包含文本解析、替换和正则表达式搜索,可能对于其他代码(例如更多数字操作),结果会有所不同。在所有配备 ARM 处理器的设备上,Java 的性能优于 Xamarin C# 代码。最大的区别是在 Android 2.3 下,其中 C# 代码的运行速度约为。 Java 速度的 70%。
在英特尔模拟器上(采用英特尔 HAX 技术,模拟器以快速 virt 模式运行),Xamarin C# 代码运行我的示例代码的速度比 Java 快得多——大约快 1.35 倍。也许 Mono 虚拟机代码和库在 Intel 上比在 ARM 上优化得更好?
编辑 2013 年 7 月 8 日
我刚刚安装了 Genymotion Android 模拟器,它在 Oracle VirtualBox 中运行,而且这个模拟器再次使用原生 Intel 处理器,而不是模拟 ARM 处理器。与英特尔 HAX 模拟器一样,C# 在这里运行得更快。这是我的结果:
Genymotion 模拟器 - 英特尔(Android 4.1.1,API 16)
Java:总计时间(5 次运行):2069 毫秒,文件读取总时间:2248 毫秒
C#:总计时间(5 次运行):1543 毫秒,文件读取总时间:1642 毫秒
然后我注意到 Xamarin.Android beta 版本 4.7.11 有一个更新,发行说明中也提到了 Mono 运行时的一些变化。决定快速测试一些 ARM 设备,并且大吃一惊 - C# 数字得到改进:
BN Nook XD+, ARM (Android 4.0)
Java:总计时间(5 次运行):8103 毫秒,文件读取总时间:8569 毫秒
C#:总计时间(5 次运行):7951 毫秒,文件读取总时间:8161 毫秒
哇! C# 现在比 Java 好吗?决定在我的 Galaxy Note 2 上重复测试:
三星盖乐世 Note 2 - ARM (Android 4.1.1)
Java:总计时间(5 次运行):9675 毫秒,文件读取总时间:10028 毫秒
C#:总计时间(5 次运行):9911 毫秒,文件读取总时间:10104 毫秒
这里 C# 似乎只是稍微慢一点,但这些数字让我停顿了一下:为什么时间比 Nook HD+ 长,即使 Note 2 有更快的处理器?答案:省电模式。在 Nook 上,它被禁用,在 Note 2 上 - 启用。决定在禁用省电模式的情况下进行测试(与启用一样,它也会限制处理器速度):
Samsung Galaxy Note 2 - ARM (Android 4.1.1),省电功能已禁用
Java:总计时间(5 次运行):7153 毫秒,文件读取总时间:7459 毫秒
C#:总计时间(5 次运行):6906 毫秒,文件读取总时间:7070 毫秒
现在,令人惊讶的是,C# 在 ARM 处理器上也比 Java 稍快。大进步!
2013 年 7 月 12 日编辑
我们都知道,在速度方面没有什么比本机代码更好,而且我对 Java 或 C# 中的句子拆分器的性能不满意,特别是我需要改进它(并因此使其更慢)。决定用 C++ 重写它。这是我的 Galaxy Note 2 上本机与 Java 速度的小型比较(即,由于其他原因,文件比以前的测试要小),省电模式已禁用:
Java:总计时间(5 次运行):3292 毫秒,文件读取总时间:3454 毫秒
本机拇指:总计时间(5 次运行):537 毫秒,文件读取总时间:657 毫秒
本机 arm:总计时间(5 次运行):458 毫秒,文件读取总时间:587 毫秒
看起来对于我的特定测试,本机代码比 Java 快 6 到 7 倍。警告:无法在 Android 上使用 std::regex 类,因此必须编写我自己的专门例程来搜索段落分隔符或 html 标记。我在 PC 上使用正则表达式对相同代码进行的初步测试比 Java 快 4 到 5 倍。
呸!再次用char*或wchar*指针唤醒原始内存,瞬间感觉年轻了20岁! :)
2013 年 7 月 15 日编辑
(请参阅下文,编辑于 2013 年 7 月 30 日,以获得更好的 Dot42 效果)
遇到一些困难,我设法将我的 C# 测试移植到 Dot42(版本 1.0.1.71 beta),这是另一个适用于 Android 的 C# 平台。初步结果表明,在英特尔 Android 模拟器上,Dot42 代码比 Xamarin C#(4.7.11 版)慢大约 3 倍(3 倍)。一个问题是 Dot42 中的 System.Text.RegularExpressions 类没有我在 Xamarin 测试中使用的 Split() 函数,所以我改用 Java.Util.Regex 类和 Java.Util.Regex.Pattern.Split() ,所以在代码的这个特定位置,存在这个小差异。不过应该不是什么大问题。 Dot42 编译为 Dalvik (DEX) 代码,因此它与 Android 上的 Java 原生协作,不需要像 Xamarin 那样从 C# 到 Java 的昂贵互操作。
只是为了比较,我还在 ARM 设备上运行了测试——这里的 Dot42 代码“仅”比 Xamarin C# 慢 2 倍。这是我的结果:
HTC Nexus One 安卓 2.3.7 (ARM)
Java:总计时间(5 次运行):12187 毫秒,文件读取总时间:13200 毫秒
Xamarin C#:总计时间(5 次运行):13935 毫秒,文件读取总时间:14465 毫秒
Dot42 C#:总计时间(5 次运行):26000 毫秒,文件读取总时间:27168 毫秒
三星 Galaxy Note 2,Android 4.1.1 (ARM)
Java:总计时间(5 次运行):6895 毫秒,文件读取总时间:7275 毫秒
Xamarin C#:总计时间(5 次运行):6466 毫秒,文件读取总时间:6720 毫秒
Dot42 C#:总计时间(5 次运行):11185 毫秒,文件读取总时间:11843 毫秒
英特尔模拟器,Android 4.2 (x86)
Java:总计时间(5 次运行):2389 毫秒,文件读取总时间:2770 毫秒
Xamarin C#:总计时间(5 次运行):1748 毫秒,文件读取总时间:1933 毫秒
Dot42 C#:总计时间(5 次运行):5150 毫秒,文件读取总时间:5459 毫秒
对我来说,有趣的是,Xamarin C# 在较新的 ARM 设备上比 Java 稍快,而在旧的 Nexus One 上稍慢。如果有人也想运行这些测试,请告诉我,我会在 GitHub 上更新源代码。如果能看到带有英特尔处理器的真实 Android 设备的结果会特别有趣。
2013 年 7 月 26 日更新
只是一个快速更新,由基准应用程序使用最新的 Xamarin.Android 4.8 以及今天发布的 dot42 1.0.1.72 更新重新编译 - 与之前报告的结果相比没有重大变化。
2013 年 7 月 30 日更新 - dot42 的结果更好
使用 Robert(来自 dot42 制造商)将我的 Java 代码移植到 C# 重新测试了 Dot42。在我最初为 Xamarin 完成的 C# 移植中,我将一些原生 Java 类(如 ListArray)替换为 C# 原生的 List 类等。Robert 没有我的 Dot42 源代码,因此他再次从 Java 移植它并在中使用原始 Java 类这些地方对 Dot42 有利,我猜是因为它像 Java 一样在 Dalvik VM 中运行,而不是像 Xamarin 那样在 Mono 中运行。现在 Dot42 结果好多了。这是我的测试日志:
7/30/2013 - Dot42 在 Dot42 C# 中使用更多 Java 类进行测试
英特尔模拟器,Android 4.2
Dot42,Greg 使用 StringBuilder.Replace() 的代码(如在 Xamarin 中):
总时间(5 次运行):3646 毫秒,文件读取总时间:3830 毫秒
Dot42,Greg 的代码使用 String.Replace() (如 Java 和 Robert 的代码):
总时间(5 次运行):3027 毫秒,文件读取总时间:3206 毫秒
Dot42,罗伯特的代码:
总时间(5 次运行):1781 毫秒,文件读取总时间:1999 毫秒
赛马林:
总时间(5 次运行):1373 毫秒,文件读取总时间:1505 毫秒
爪哇:
总时间(5 次运行):1841 毫秒,文件读取总时间:2044 毫秒
ARM,三星 Galaxy Note 2,省电关闭,Android 4.1.1
Dot42,Greg 使用 StringBuilder.Replace() 的代码(如在 Xamarin 中):
总计时间(5 次运行):10875 毫秒,文件读取总时间:11280 毫秒
Dot42,Greg 的代码使用 String.Replace() (如 Java 和 Robert 的代码):
总计时间(5 次运行):9710 毫秒,文件读取总时间:10097 毫秒
Dot42,罗伯特的代码:
总计时间(5 次运行):6279 毫秒,文件读取总时间:6622 毫秒
赛马林:
总计时间(5 次运行):6201 毫秒,文件读取总时间:6476 毫秒
爪哇:
总计时间(5 次运行):7141 毫秒,文件读取总时间:7479 毫秒
我仍然认为 Dot42 还有很长的路要走。拥有类似 Java 的类(例如 ArrayList)和良好的性能将使将代码从 Java 移植到 C# 稍微容易一些。但是,这是我不太可能做的事情。我宁愿使用现有的 C# 代码(库等),这些代码将使用本机 C# 类(例如 List),并且使用当前的 dot42 代码执行起来会很慢,但使用 Xamarin 会很好。
格雷格
原文由 gregko 发布,翻译遵循 CC BY-SA 4.0 许可协议
是的,Xamarin 的 Mono 虚拟机比谷歌在 Android 中使用的 Dalvik 更令人印象深刻。我已经使用 HTC Flyer 和 Acer Iconia Tab 平板电脑对其进行了测试,以通过 Mono 对 Android 的 C# 端口与 Java Dalvik 进行基准测试,Android 的 C# 实现很好,真正击败了基于 Java 的 Dalvik。