目录
Arm64(通常称为AArch64)提供功耗优化体系结构,该体系结构是许多片上系统(SoC)的基础。SoC集成了CPU、内存、GPU和I/O设备,可在各种行业、应用和设备中执行高能效计算操作。由于其便携性和低功耗,Arm64体系结构非常适合移动设备。但是,笔记本电脑和台式机也开始使用Arm64。
Microsoft Windows 11通过支持Arm64并提供多项简化应用移植的功能,帮助加快了这种采用速度。具体而言,Windows 11提供Windows on Arm (WoA)以通过本机Arm64方法很好地运行 Python应用程序,而Arm64EC(仿真兼容)有助于逐步将x64应用移植到Arm64。此外,包括Qt在内的许多框架现在使用 Windows on Arm(WoA)来本机运行基于UI的应用程序。为了帮助开发人员,Microsoft 推出了 Windows Dev Kit 2023,它提供了一个方便的Arm64驱动的设备。
Arm64在使用C++和Python时具有原生优势,但它也为其他框架提供了许多优势。例如,.NET是适用于Windows、Linux和MacOS的跨平台开发框架。将框架与Arm64结合使用,可以在多个平台上实现高效的应用。
本文演示如何使用Arm64运行.NET应用程序,从而获得本机体系结构的优势,例如电源效率和速度提升。可以为.NET设置开发环境,并了解通过在Arm64上本机运行代码可以预期的性能提升。下载配套代码以继续操作。
环境设置
若要设置开发环境,需要满足以下条件:
- Arm64设备上的Windows 11(例如,Windows开发工具包2023))
- Visual Studio Code 或 Visual Studio 2022
- .NET 8.0 SDK
- 适用于Windows 64位的Git
首先,在Arm64设备上为两种体系结构(x64 和 Arm64)中的每一个安装适用于Windows的.NET 8.0 SDK。该SDK目前提供预览版(在此处下载)。要确认安装成功,请打开命令提示符窗口并输入:
dotnet --info
这将产生以下输出。
默认情况下,该dotnet命令在Arm64设备上运行时使用Arm64体系结构。但是,它认识到x64体系结构在“找到的其他体系结构”列表中也可用。
安装.NET SDK后,将 Visual Studio Code for Windows 安装为IDE。选择“Arm64 的用户安装程序”,然后启动安装程序。使用默认设置。安装后,选择您的颜色主题。最后,使用64位独立安装程序安装 Git for Windows。
对.NET应用程序进行基准测试
.NET团队提供了一组基准测试,可用于评估不同体系结构上各种.NET版本的性能。这些基准测试依赖于BenchmarkDotNet 库,该库提供了一个框架来简化代码执行时间的度量。
可以使用C#属性将这些度量添加到代码中。该库评估执行时间并报告平均计算时间和标准偏差。此外,该库可以生成绘图来帮助您评估代码性能。所有这些功能在.NET性能基准测试中也可用。
要使用这些基准测试,请从克隆dotnet performance存储库开始:
git clone https://github.com/dotnet/performance.git
然后,导航到performance\src\benchmarks\micro,如下所示。
在performance\src\benchmarks\micro中,输入以下命令:
dotnet run -c Release -f net8.0
应用程序将生成并启动,并显示可用性能基准的列表。
现在,键入“475”并按Enter键以启动C#字符串数据类型的性能测试。此操作的结果如下所示(向上滚动以查看此表)。
默认情况下,该表汇总了性能测试结果。您可以看到每个性能测试的执行时间和统计信息(平均值、中位数、最小值、最大值和标准差)。这为您提供了有关代码性能的全面信息。
与控制台应用一样,该dotnet run命令默认使用Arm64体系结构。若要使用x64启动性能测试,请使用以下-a开关:
dotnet run -c Release -f net8.0 -a x64
但是,目前,BenchmarkDotNet库与基于Arm64的计算机上适用于x64的.NET SDK不兼容。因此,BenchmarkDotNet会报告错误和不正确的执行时间。
若要比较x64和Arm64上的.NET性能,请使用控制台应用模板并实现自定义基准。
自定义基准测试
若要实现自定义基准,请使用 System.Diagnostics.Stopwatch 类。首先创建控制台应用程序Arm64.Performance(dotnet new console -o Arm64.Performance)。然后,在Visual Studio中打开它。接下来,通过单击“新建文件”并键入“PerformanceHelper.cs”作为文件名来创建一个新文件。
然后,打开PerformanceHelper.cs,并定义PerformanceHelper类:
using System.Diagnostics;
namespace Arm64.Performance
{
public static class PerformanceHelper
{
private static readonly Stopwatch stopwatch = new();
public static void MeasurePerformance(Action method, int executionCount, string label)
{
stopwatch.Restart();
for(int i = 0; i < executionCount; i++)
{
method();
}
stopwatch.Stop();
Console.WriteLine($"[{label}]: {stopwatch.ElapsedMilliseconds.ToString("f2")} ms");
}
}
}
该PerformanceHelper类是静态的。它有一个方法,即MeasurePerformance,该方法通过使用 Action委托调用提供的函数来工作。此委托作为MeasurePerformance方法的第一个参数传递。调用该方法的第二个参数executionCount,所指定的次数与调用次数相同。之后,该MeasurePerformance方法打印执行特定代码所需的时间。此外,MeasurePerformance接受第三个参数label,您可以使用该参数传递描述性能测试的字符串。
您可以通过创建一个新文件来定义性能测试,PerformanceTests.cs,您可以在其中声明PerformanceTests类:
namespace Arm64.Performance
{
public static class PerformanceTests
{
}
}
此类为空。您将在下一节中对其进行扩展。
列表排序
列表排序的性能测试显示对包含100,000个double类型元素的列表进行排序所需的时间。可以使用 System.Random 类中提供的伪随机数生成器来准备列表。
若要实现此测试,请使用以下代码补充该PerformanceTests类:
public static class PerformanceTests
{
private static readonly Random random = new();
private static List<double> PrepareList()
{
const int listSize = 100000;
return Enumerable.Range(0, listSize)
.Select(r => random.NextDouble())
.ToList();
}
public static void ListSorting()
{
var list = PrepareList();
list.Sort();
}
}
有三个新元素:
- private static random成员的声明和初始化。这是伪随机数生成器的一个实例。
- private static PrepareList方法创建一个伪随机列表,其中包含100,000个double类型的元素。若要生成此列表,请使用System.Linq 中的Enumerate.Range方法。伪随机数生成器创建此列表的每个元素。
- 公共静态ListSorting方法首先调用PrepareList帮助程序方法来创建随机列表。然后,该Sort方法对这个随机列表进行排序。
矩阵乘法
现在,您实现了方阵乘法。首先,使用该GenerateRandomMatrix方法扩展PerformanceTests类的定义。在以下ListSorting位置将此方法添加到PerformanceTests.cs文件中:
private static double[,] GenerateRandomMatrix()
{
const int matrixSize = 500;
var matrix = new double[matrixSize, matrixSize];
for (int i = 0; i < matrixSize; i++)
{
for (int j = 0; j < matrixSize; j++)
{
matrix[i, j] = random.NextDouble();
}
}
return matrix;
}
此方法生成一个500 x 500的平方矩阵。一个两层for循环,其中每一步调用random.NextDouble,生成一个伪随机生成的double类型的值。
接下来,在PerformanceTests类中,添加以下方法:
private static double[,] MatrixMultiplication(double[,] matrix1, double[,] matrix2)
{
if (matrix1.Length != matrix2.Length)
{
throw new ArgumentException("The matrices must be of equal size");
}
if (matrix1.GetLength(0) != matrix1.GetLength(1) || matrix2.GetLength(0) != matrix2.GetLength(1))
{
throw new ArgumentException("The matrices must be square");
}
int matrixSize = matrix2.GetLength(0);
var result = new double[matrixSize, matrixSize];
for (int i = 0; i < matrixSize; i++)
{
for (int j = 0; j < matrixSize; j++)
{
result[i, j] = 0;
for (int k = 0; k < matrixSize; k++)
{
result[i, j] += matrix1[i, k] * matrix2[k, j];
}
}
}
return result;
}
该MatrixMultiplication方法将两个平方矩阵作为输入,然后使用数学公式计算乘积。该result变量使用三层for循环来存储矩阵乘法的结果,该MatrixMultiplication方法将返回该结果。
最后,在PerformanceTests类中,实现以下公共方法,该方法生成两个平方矩阵,然后计算乘积:
public static void SquareMatrixMultiplication()
{
var matrix1 = GenerateRandomMatrix();
var matrix2 = GenerateRandomMatrix();
MatrixMultiplication(matrix1, matrix2);
}
字符串操作
对于最后一个性能基准测试,请使用字符串操作。在PerformanceTests类中,定义loremIpsum变量,该变量存储占位符文本的片段:
private static readonly string loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Curabitur ut enim dapibus, pharetra lorem ut, accumsan massa. " +
"Morbi et nisi feugiat magna dapibus finibus. Pellentesque habitant morbi " +
"tristique senectus et netus et malesuada fames ac turpis egestas. Proin non luctus lectus, " +
"vel sollicitudin ante. Nullam finibus lobortis venenatis. Nulla sit amet posuere magna, " +
"a suscipit velit. Cras et commodo elit, nec vestibulum libero. " +
"Cras at faucibus ex. Suspendisse ac nulla non massa aliquet sagittis. " +
"Fusce tortor enim, feugiat ultricies ultricies at, viverra et neque. " +
"Praesent dolor mauris, pellentesque euismod pharetra ut, interdum non velit. " +
"Fusce vel nunc nibh. Sed mi tortor, tempor luctus tincidunt et, tristique id enim. " +
"In nec pellentesque orci. Nulla efficitur, orci sit amet volutpat consectetur, " +
"enim risus condimentum ex, ac tincidunt mi ipsum eu orci. Maecenas maximus nec massa in hendrerit.";
然后,实现StringOperations public方法:
public static void StringOperations()
{
loremIpsum.Split(' ');
loremIpsum.Substring(loremIpsum.LastIndexOf("consectetur"));
loremIpsum.Replace("nec", "orci");
loremIpsum.ToLower();
loremIpsum.ToUpper();
}
此方法使用空格分隔符将占位符文本拆分为子字符串。然后,它采用从单词consectetur的最后一个索引开始的子字符串。接下来,它将替换nec为orci,将字符串转换为小写,然后转换为大写,以模拟C#应用中字符串变量的典型操作。
把东西放在一起
现在,您可以在控制台应用程序中使用这些性能测试。通过将默认内容(Console.WriteLine("Hello, World!");)替换为以下语句来修改 Program.cs 文件:
using Arm64.Performance;
Console.WriteLine($"Processor architecture: " +
$"{Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")}");
const int trialCount = 5;
for ( int i = 0; i < trialCount; i++ )
{
Console.WriteLine($"Trial no: {i + 1} / {trialCount}");
PerformanceHelper.MeasurePerformance(PerformanceTests.ListSorting,
executionCount: 500, "List sorting");
PerformanceHelper.MeasurePerformance(PerformanceTests.SquareMatrixMultiplication,
executionCount: 10, "Matrix multiplication");
PerformanceHelper.MeasurePerformance(PerformanceTests.StringOperations,
executionCount: 500000, "String operations");
}
此代码导入定义PerformanceHelper和PerformanceTests类的Arm64.Performance命名空间。然后,代码打印处理器体系结构(Arm64或AMD64),具体取决于用于运行应用的SDK的体系结构。
您有一个常量trialCount,可用于指定性能测试集的执行频率。每个测试批处理运行ListSorting 500次,然后执行SquareMatrixMultiplication 10次和StringOperations 500,000次。这实现了测试批次的可比执行时间。单矩阵乘法比单字符串运算慢。因此,必须对后者进行更多处决。
Arm64上的.NET性能提升
您现在可以启动该应用程序以评估其在不同架构上的性能。首先使用Arm64运行应用。在Arm64.Performance文件夹中,输入:
dotnet run -c Release
这将启动控制台应用,你应看到以下输出。
现在,若要比较执行时间,请使用x64体系结构启动应用:
dotnet run -c Release -a x64
此命令将导致以下输出:
与在Arm64上本机执行这些操作相比,这些操作在模拟的x64上花费的时间都更多。平均而言,本机执行为列表排序提供了大约15%的性能提升,为矩阵乘法提供了291%的性能提升,为字符串运算提供了239%的性能提升。
下图总结了x64和Arm64本机执行的代码的平均执行时间。
该图显示了执行时间的显著改善。
结论
本文演示了如何使用.NET SDK进行跨平台控制台应用开发。它介绍了如何使用不同的处理器体系结构(x64或Arm64)创建项目应用程序模板并启动应用程序。
然后,它演示了如何使用标准和自定义代码对.NET应用程序进行基准测试。它使用后者来演示在Arm64驱动的设备上本机执行代码时显著提高性能——矩阵乘法的速度快了近三倍。在x64上运行的代码必须使用仿真层,这会消耗额外的CPU和内存。如果没有这个额外的层,本机Arm64将获得性能优势和更高的效率。
现在,你已了解如何利用Arm64的强大功能,请开始将 Arm64 用于.NET应用。
https://www.codeproject.com/Articles/5367981/NET-Performance-on-Arm64