Intel CPU的多线程与向量化技术解析

随着应用程序对计算能力的需求日益增长,Intel投入了数十亿美元用于研发,以从每个CPU周期中提取尽可能多的计算能力。他们通过并行化实现了这一点。历史上,CPU每个线程每个CPU周期执行一个操作。不久之后,工程师们开始研究如何使用多个线程并行执行任务。多线程最初是通过多个CPU实现的,然后是通过超线程CPU,最终是通过CPU上的多个超线程核心实现的。Intel CPU上的线程数通常是CPU核心数的两倍,这要归功于超线程技术。例如,Intel® Core处理器i7 6700HQ是一个四核CPU,因此它有八个线程。

除了多线程之外,Intel还增加了向量化。向量化是另一种形式的并行性,它允许单个指令对多个数据项执行操作——单指令多数据(SIMD)。Intel在1995年首次通过MMX指令实现了向量化,然后在1999年通过流SIMD扩展(SSE)不断扩展,2011年的AVX(高级向量扩展),2013年的AVX2和2015年的AVX-512。每一次进步都增加了新的指令,但基本原理基本相同。Intel声称,向量并行性和线程并行性的综合影响可以在某些算法上提高性能高达187倍。这比单独使用线程或向量化的顺序要大得多。

为了充分利用并行化特性,开发人员必须改变他们的编码方式。但是,通过Intel的并行化工具Intel® Advisor可以进行大量的优化。按设计,Intel Advisor在应用程序运行时分析应用程序,寻找可以从线程和向量化中受益的应用程序区域,但这里的重点是向量化。

向量化通常在操作数据数组的循环上执行。向量化通过在CPU周期内对该数据执行多个操作来工作。例如,如果一个循环正在遍历一个整数数组,并向数组中的每个元素添加一些值,那么循环首先会增加数组索引,然后对索引处的元素执行加法:

for (int i = 0; i < 1000; i++) { arrayA[i] = arrayA[i] + 2; }

向量化后,索引会增加得更快,并且会对数组进行并行操作。循环将被“展开”如下:

for (int i = 0; i < 1000; i = i + 4) { arrayA[i] = arrayA[i] + 2; arrayA[i + 1] = arrayA[i + 1] + 2; arrayA[i + 2] = arrayA[i + 2] + 2; arrayA[i + 3] = arrayA[i + 3] + 2; }

注意它现在以四的增量增加数组索引。向量化形式将类似于这样:

for (int i = 0; i < 1000; i = i + 4) { addNumberToFourElementsInASingleCycle(arrayA[i], 2); }

这更多的是伪代码,但它在功能上执行与第一个标量版本和展开版本相同的任务,但大约只需要四分之一的周期。要使向量化工作,就不能有任何数据依赖性,这可能会使并行函数产生意外的结果。理解这一点很重要,因为并非每个程序中的每个循环都会从向量化中受益,甚至能够使用向量化。因此,虽然该工具有一些丰富的特性,但至少对于向量化来说,该工具的价值在某种程度上仅限于特定类型的应用程序。

有三种使用该工具的方法:通过Intel提供的GUI,或者通过安装的Intel Visual Studio集成,或者通过命令行(在Linux中非常流行)。它还允许用户方便地在Intel C++编译器和Microsoft Visual C++编译器之间切换。Intel Advisor工具可以分析两个编译器的输出,但与MSVS/GCC相比,Intel编译器可以显示更多的建议。

通过比较向量优化循环和未优化循环,可以轻松测试向量化的结果。Intel Advisor的内置分析工具提供了这一点。实施和测试向量化的建议相当直接。输出建议在编译器设置和代码中进行并行化优化。

第一次使用分析工具在应用程序运行时呈现结果,没有打开任何并行化的推荐。应用程序需要有足够的运行时间,以便分析器可以捕获应用程序运行时的遥测数据。分析器运行后,它会产生一个图表,显示应用程序内部循环的遥测结果。

第二次通过实现了建议。对于这个特定的例子,它打开了编译器的向量化选项,并添加了两个#pragma标签。优化代码的效率得到了提高。

有趣的是,在运行应用程序几次之后,似乎并没有在实际计算时间上获得任何收益。这可能是因为应用程序相当小,执行速度很快。

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