在Windows on Arm上构建原生应用

随着Windows on Arm(WoA)设备的普及,开发者们越来越关注如何为这些设备构建性能优化的原生应用。WoA设备可以模拟运行x64 Windows应用,但这种模拟是通过将x86 CPU指令翻译成Arm指令来实现的,其速度自然不如直接为Arm架构编译的应用快。为了充分利用平台的计算能力并最大化电池寿命,开发者应该创建原生应用。

创建WoA的原生应用有多种方法。例如,可以创建一个针对Arm设备的通用Windows平台(UWP)应用。从.NET Framework 5.0.8版本开始,Windows Forms、Windows Presentation Foundation(WPF)和UWP都原生支持Arm。如果正在使用这些框架之一开发现有应用,那么将其编译为WoA设备的原生应用就非常简单了。

本文将演示如何将一个在x86-64机器上开发的Windows Forms(WinForms)应用移植为WoA的WinForms原生应用。在开始之前,应该熟悉C#、.NET和WinForms。

创建新的AArch64项目

首先,将创建一个新的Windows Forms项目,然后为其编译AArch64(64位Arm)版本。确保已经安装了带有.NET桌面开发组件的Visual Studio 2019。在Visual Studio安装程序中,它看起来是这样的:

接下来,打开Visual Studio并选择“创建新项目”。在模板列表中,选择“Windows Forms App”,而不是“Windows Forms App (.NET Framework)”。不选择Windows Form App,因为它是针对.NET Framework 4.x的,而想要的是.NET 5。

然后,点击“下一步”,选择应用的“项目名称”和“位置”。

向导接着询问想要针对哪个框架:.NET Core 3.1或.NET 5。虽然可以在针对.NET Core 3.1时编译到AArch64,但是当针对.NET 5时性能更好,因为它使用了更多的A硬件内在特性。因此,选择.NET 5.0并点击“创建”。然后,就可以像平常一样开始开发Windows Forms应用了。

作为一个简单的演示,让创建一个只有一个按钮的表单。当用户点击它时,会弹出一个消息框显示进程架构。

首先在设计器中向表单添加一个按钮:

private void button1_Click(object sender, EventArgs e) { MessageBox.Show(System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString()); }

如果在开发机器上运行应用程序并按下按钮,它可能显示x64(除非不在x64设备上,当然)。

编译原生WoA应用

现在,让将应用程序编译为WoA设备的原生应用。首先,在“解决方案资源管理器”中双击“属性”:

这样做会打开项目的属性。然后转到“构建”标签。

所要做的就是将“平台目标”更改为“ARM64”并重新编译。现在不能在开发机器上运行应用程序,因为架构不兼容,所以让尝试在Arm设备上运行它。将bin/Debug/net5.0-windows目录复制到Arm设备上,并尝试在该目录下运行WinFormsArm.exe。

如果还没有安装.NET运行时,设备会提示首先安装它,并将重定向到下载页面。在该页面上,选择“下载Arm64”下的“运行控制台应用”,运行安装程序,对于“运行桌面应用”(需要两者)也是如此。

当.NET运行时安装完成后,尝试运行应用程序并再次按下按钮。它现在在消息框中显示Arm64。

为了比较,也可以尝试将“平台目标”设置为x86,在Arm设备上下载x86运行时,然后运行x86可执行文件。会看到消息框显示“x86”,所以它是在模拟下运行的。

正如看到的,编译Windows Forms应用程序为AArch64非常简单:只需要确保针对.NET 5并将“平台目标”更改为ARM64。对于.NET Framework 4.x上的现有应用程序,应该在Visual Studio中创建一个新项目(因为.NET 5和.NET Framework 4.x的项目类型不同),并从旧项目导入文件。

在为Windows on Arm创建或移植应用程序时,请注意“缩放和布局”Windows设置。像Surface Pro这样的设备在相对较小的屏幕上有高分辨率,所以推荐设置为200%。另一方面,开发机器可能将这个值设置在100%到150%之间。

“缩放和布局”设置会影响文本和表单大小。但是PictureBox等控件不会缩放。会想要确保应用程序在这个设置的值不管如何都能看起来不错。否则,表单在Arm设备上可能会看起来相当扭曲。当然,这个问题并不是Arm独有的,因为所有Windows设备都有这个设置,但记住这一点是好的。

基准测试AArch64与x86模拟

编译为AArch64而不是模拟x86二进制文件的优势在于它更快。让做一个快速的基准测试来确认这是真的。

基准测试包括生成一个400x400的位图,用随机像素填充它,并显示结果。期望在编译为AArch64时比编译为x86时更快。

将扩展表单,添加一个新的按钮来开始基准测试,一个标签来显示它花了多长时间,以及一个PictureBox来显示图像。

首先,将按钮命名为benchmarkBtn,标签命名为benchmarkLabel,PictureBox命名为benchmarkPbox。然后,双击benchmarkBtn添加一个点击事件处理程序来执行基准测试:

private void benchmarkBtn_Click(object sender, EventArgs e) { Stopwatch sw = new Stopwatch(); sw.Start(); Random rng = new Random(); Bitmap bmp = new Bitmap(400, 400); for (int i = 0; i < 10; i++) { for (int x = 0; x < 400; x++) { for (int y = 0; y < 400; y++) { bmp.SetPixel(x, y, Color.FromArgb(rng.Next())); } } } benchmarkPbox.Image = bmp; sw.Stop(); benchmarkLabel.Text = "Benchmark time: " + sw.Elapsed.ToString(); }

需要在文件顶部添加using System.Diagnostics;,因为代码使用了Stopwatch。

基准测试使用SetPixel一次填充一个像素,这是一种生成随机图像的低效方式。然而,对于这种类型的基准测试来说并不重要,因为结果只是相对于彼此才有意义。

生成随机图像的整个过程重复十次,以平均每次迭代的结果。只有最后一张图片显示,但这也不重要。当图片生成完成后,应用程序在标签上显示经过的时间。

通过为AArch64和x86编译应用程序,然后在Arm设备上运行它们来试试。还想将构建配置设置为“发布”而不是“调试”以优化代码。然后可执行文件就会出现在bin/Release而不是bin/Debug中。

在Arm设备上,原生AArch64应用程序在0.6到0.7秒内完成了基准测试。模拟的x86应用程序需要1.2到1.5秒。所以,原生应用程序的执行速度是模拟应用程序的两倍。当然,它只是针对特定情况计时,并不是所有操作都有相同的相对速度,但它确实突出了原生编译应用程序的效率,正如所期望的。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485