使用 OpenMP* 在现有串行代码中寻找重要的并行化机会

作者:Erik Niemeyer(英特尔公司)和 Ken Strandberg(Catlow Communications*)

当您识别不到并行化机会时,您应怎样做?您如何找到这些机会?在您当前的代码中,使用什么方法能够最好地实施并行性?本文介绍了如何识别现有串行代码中的重要并行化机会,以及如何将它们转化为性能优化的并行代码。

 

下载白皮书:
/sites/default/files/m/d/4/1/d/8/finding_non_trivial_opportunities_for_parallelism_on_serial_code.pdf

简介

本文介绍了在现有串行代码中寻找重要的并行化(线程化)机会的基本方法。通过实施线程化,用户可使用多条线程同时执行多项大型任务,或者主要任务的多个大型组成部分,进而显著提高应用性能。然而,线程化同样带来了严峻的挑战--如何适当地调整现有串行代码以实现多线程执行。

构建结构良好的并行代码是一个广泛的话题,相关的文字资料有很多。本文只是一个初级教程,介绍了如何使用英特尔工程师提供的方法在现有串行代码中添加并行性。

文章分为三个部分:

 

  • 基本并行化概念简介
  • 在现有串行代码中添加并行性的方法
  • 如何识别串行代码中可实施并行化的内容

 

如果您非常熟悉并行化概念和并行开发,完全可以跳过前两部分,着重阅读第三部分。

基本并行化概念

并行编程的目标

 

并行代码可同时运行,使用更多的计算资源(多枚内核、单枚内核中的多个执行引擎等)在相同时间内完成更多的任务或在更短时间内完成固定大小的任务。并行运行的代码可以是同一代码中负责处理不同数据的多个实例,也可以是负责处理完全独立的任务的不同代码集。根据所执行的并行化类型,获得的优势可能是缩短解决固定大小的问题所需要的时间或者提高在相同时间内处理更大的问题的能力。

并行性与并发性

 

考虑到当前的处理器微架构,更可取的方式是,如有可能,对代码进行线程化,确保它们能够并发和并行执行。相互关联时,并行性和并发性并非同义词。理解并行性和并发性对开发人员制定编码决策的影响非常重要。

并发性指两条或更多线程同时处于进程中。但是,它们并不一定同时执行。它们可能全部位于管道内,但不会同时使用资源,也不会遇到与并行执行相同的风险,或者获得与并行执行相同的优势。

并行性指多条并发线程同时执行。这种情况能够带来许多并发执行所不具备的优势,但同时也会引发诸多新的风险。

通过同时执行多条线程,您将有机会提高性能和计算资源利用率。然而,多条线程会增加应用的复杂性,加大调试的难度,此外,查找数据争夺、死锁和其它并行挑战会占用大量时间(虽然某些编程工具如英特尔® VTune™ Amplifier XE 可帮助识别存在的问题,但仍无法彻底弥补该缺陷)。

通过有效地应用线程化和并行化,有望实现巨大的应用性能优势,并根据计算问题的规模随意扩展。

 

功能分解与数据分解

 

通常,对代码进行并行处理的目标是在最短的时间内完成单个任务。为了实现该目标,您可以将整个任务拆分为多个组成部分,然后适当地分配资源,并行处理这些部分。但是,应如何拆分任务呢?是否存在大量不同的独立功能(任务)可同时执行?是否存在大量数据可划分为相当大的工作负载?是否两者兼具?

这些问题可以帮助程序员识别代码中固有的并行性类型,这会在很大程度上影响代码的并行化方式。

功能分解 是将任务拆分为多个不同的功能部分,这种方式最适合工作量随独立任务数量扩展的情况。功能分解又称为基于任务的并行化。例如,要建造一幢房子,一个人施工所需要的时间无疑最长。如果同时聘用大量技术熟练的专业人员,让他们分开从事自己最擅长的工作,各方面齐头并进,那么工作效率会更高,施工速度会更快。例如,混凝土工负责构建地基,木工负责构建房屋框架,电工负责接线,水管工负责安装管道,墙板安装工负责安装墙板,装修木工负责安装模具,砖瓦匠负责安装台面等等。各方面的工作同时进行,能够最大限度地缩短施工时间。汽车组装线是另一个关于功能分解的典型示例。不同的工人负责不同的工作,完成对零部件的加工后便传给后面的工人:车身和引擎构建工作同时进行,在某个点相遇,最终完成整个制造过程。

数据分解是将任务拆分为可单独处理的独立的数据块,这种方式最适合工作量随数据量扩展的情况。例如,为学生考试试卷评分时,如果将答题纸分给多个评卷人进行评分,速度将显著提高;假设有 1000 张试卷,由十个评卷人进行评分所需要的时间将远远少于一个评卷人的用时(很可能只需要十分之一的时间)。“天空实验室”(Skylab)空间站坠落后的零部件搜寻工作是另一个有关数据分解概念的代表性示例。相关部门指派了不同的搜寻人员来做这件事,将搜寻地段划分为多个区域。在典型应用中,通过对数据进行分区并针对每个分区应用相同的搜索,能够更加快速地在海量数据(如一张关于街道上熙熙攘攘的人群的照片)中找到特定的数据模式(如眼睛颜色)。

并行性和正确性

虽然完全并行化的代码能够显著提升性能,但如果它提供的结果不正确,很可能会造成最终行为与算法和代码初衷不符。开发人员必须选择能够出色地实施并行化或者可容忍错误结果(在某些情形下可以接受)的算法。部分极具挑战性的情形包括并行化随机数生成或序列算法。此外,为了确保获得正确的结果,必须特别关注组合的四舍五入和并行化浮点计算。如欲阅读有关这些话题的文章,请访问英特尔® 软件网络

并行化性能:加速幅度和阿姆达尔定律

通过对应用进行并行化处理可实现多大程度的加速?对于希望缩短周转时间(turn-around time)的使用情形,阿姆达尔定律是一款出色的工具,能够帮助确定可能实现的加速幅度。根据阿姆达尔定律的描述,面对具体的计算问题,能够将解决问题的执行时间缩短多少取决于您即将实施并行化的代码中串行部分所占的比例,以及可用的计算资源。

在阿姆达尔定律中:

 

  • P = 代码中并行部分所占的比例(如 0.3 或 0.7)
  • N = 并行部分可用的处理器数量
  • (1 – P) 代表了代码中串行部分的比例

 

显而易见,随着 N 无限扩大,P/N 将越来越接近 0,而且计算结果与串行部分的大小密切相关。

意义

为了实现最大的速度提升,您需要将代码的串行执行时间缩至最短,同时最大限度地拓展并行机会。这意味着,您不但要对算法中显而易见的部分进行并行处理,如有可能还需要重新设计串行算法,采用并行方式更出色地表述算法。

请记住一点,虽然并行代码在大多数情况下都能实现出色的性能,但并不一定始终都是最佳的解决方案。在某些情形下,与相同算法的并行版本相比,经过良好优化的串行代码能够实现更出色的性能。

我们刚刚讨论了基本的并行化概念,下面就让我们看一下实施并行化的不同方法,共同探讨如何寻找潜在的并行化机会。

并行化方法

对应用实施并行化的方法有很多种。具体选择哪种方法取决于多个变量,其中包括:

  • 您运行代码的计算平台。您所使用的是完全分布式计算平台如独立机器的大型集群,32、64 或 128 内核对称多处理器系统,还是四核工作站?
  • 您对并行化精细度的要求。这往往反映了您的计算平台的特性,与每条线程处理的工作量类似。
  • 您希望解决的问题。您要实施并行化的对象是嵌入式火箭控制系统还是在超级计算机上运行的药物建模应用?
  • 性能目标。您是希望提高吞吐率还是希望缩短周转时间?

 

下文介绍了几种最常见的并行编码方法。

MPI

消息传递接口(MPI)是用于高性能计算(HPC)和高度分布式处理系统的一种分布式、多线程模型。MPI API 使用表述清晰的消息在整个计算系统内传递信息。每个节点独立运行,可创建并响应 MPI 消息。

MPI 适用于精细度较低的并行化处理。适合采用 MPI 的计算平台示例包括由通过高性能通信网络相互连接的独立式服务器所组成的集群,例如美国国立卫生研究院* 的 LoBoS*(Lots of Boxes on Shelves)。

在采用 MPI 的分布式计算平台中,各节点间的通信会影响整体系统性能,因为网络必须在节点之间传递消息和数据。此外,内存架构和分区对性能的影响也很大,因为某个节点所需要的数据可能存储于另一个节点之上。因此,若想实现最出色的系统性能,进行整体程序设计时,应将数据模型和面向通信的优化特性全部考虑在内。

显式线程化

显式线程化将使用 POSIX 线程(Pthreads)、Windows* 线程或其它线程家族在代码中显式地创建线程。线程化过程完全在程序员的控制之下,代码能够彻底按照开发人员的需求实现线程化。显式线程化会生成复杂的代码,不过它能够在创建、使用和销毁线程的方式与时间方面为开发人员带来更多的控制权。显式线程化同时适用于精细度较低和较高的并行化实施。此外,它还能够与其它形式相结合,虽然这会为开发人员带来更多的挑战,增加应用的整体复杂性。

编译器指导的并行化

编译器指导的并行化(即自动并行化)可利用 API 如 OpenMP* 或英特尔® 线程构建模块显示代码内的线程化机会。兼容的编译器在编译时会自动创建必要的线程。对开发人员来说,线程化流程的复杂性降低,但程序员对线程实施方式的控制力度却有所减弱。请注意,编译器指导的并行化非常灵活,能够轻松地修改现有串行代码。作为一名工程师,我认为编译器指导的并行化是迅速构建并行代码原型的最佳方式。因此,本文将着重介绍如何寻找最适合使用这种并行化方式的机会。

并行库

英特尔可提供多个能够在英特尔® 处理器上实现并行优化的库。这些库可针对特定应用域执行复杂的并行化算法:

英特尔® 数学核心函数库(英特尔® MKL)是一个包含经过高度优化和广泛线程化的数学例程(专为需要极致性能的科学、工程及金融等领域的应用而设计)的函数库。核心数学函数包括 BLAS、LAPACK、ScaLAPACK1(ScaLAPACK 不适合 Mac OS* X 操作环境)、稀疏矩阵解算器、快速傅立叶转换、矢量数学及其它函数。

它可为当前及下一代英特尔® 处理器提供性能优化特性,能够更出色地与Microsoft Visual Studio*、Eclipse* 和 XCode* 相集成。英特尔® MKL 支持完全集成英特尔兼容性 OpenMP* 运行时库,以实现更出色的 Windows*/Linux* 跨平台兼容性。

英特尔® 集成性能基元(英特尔® IPP)是一款面向多核的扩展函数库,其中包含众多针对多媒体数据处理和通信应用实现高度优化的软件函数。英特尔® IPP 提供了数千种优化函数,涵盖了大部分常用的基本算法。

英特尔® 线程构建模块(英特尔® TBB)是一个屡获殊荣的 C++ 模板库,能够为任务提取线程,以创建可靠、便携、可扩充的并行应用。正如 C++ 标准模板库(STL)能够扩展核心语言一样,英特尔® TBB 可为 C++ 用户提供更高级别的提取能力,以支持并行。为了部署英特尔® TBB,开发人员使用熟悉的 C++ 模板和编码类型,将低级线程详情留给函数库。它还便于在架构与操作系统之间移动。通过英特尔® TBB,开发人员获得了加快编程速度、扩充性能和更轻松地保存代码等优势。

下一步--寻找机会

我们已经讨论了并行化的基本概念和一些并行化实施方法,接下来让我们共同思考最复杂的问题:如何寻找并行处理机会?在下一部分内容中,我们将分析如何寻找适合实施并行化的对象,以及如何克服常见的并行化挑战。

寻找并行化候选对象

剖析应用以寻找性能瓶颈

在您的代码中,可能有很多地方能够实施并行处理,但所付出的努力究竟能否带来令人满意的性能提升呢?这很难判断,除非您仔细分析代码,了解处理器将时间用在了什么地方。通过使用性能分析器如英特尔® 性能瓶颈分析器(英特尔® PBA)剖析代码,您将能够找到性能瓶颈的所在。然后,您便可以确定需要对哪些地方进行优化,以及是否存在实施并行化或优化算法的机会。

方法

很多白皮书和文章都介绍了在现有串行代码中寻找并行化对象的不同方法。实际上,作为一名工程师,我自己也经常需要寻找这些机会,结果发现最简单的方法就是最好的方法。寻找热代码。热代码指所占用的处理器时间明显多于其它区段的应用区段。这通常出现在循环体中,但并非总是如此。

这种方法涉及使用特定类型的取样工具集如英特尔® VTune™ Amplifier、英特尔® 性能调试实用程序,或英特尔® 性能瓶颈分析器等查找热代码。

若想收集该数据,您必须拥有代表性工作负载,能够以切合实际的方式在应用上运行。如欲详细了解如何创建合适的工作负载,请参阅以下文章: (/zh-cn/articles/the-three-stages-of-preparation-for-optimizing-parallel-software/)。

上文提到的工具集配有详细的教程,可帮助您在获得合适的工作负载后执行基本分析(请参见下方的“参考资料”部分,查看这些工具的链接)。

待适合的工作负载和数据收集工具集准备就绪后,寻找并行化对象的过程就非常简单了,只要查找最热的代码即可。图 1 为英特尔® PBA 提供的示例工作负载代码执行报告。


图 1. 英特尔® PBA 工具集提供的用于识别热代码区段的流分解图。

找到自己的热代码后,您需要根据它占用的时间(或周期)和与它相关的功能对它进行分类。再强调一次,最佳的并行化对象通常位于最热的代码中。一项通用实践是,从前五个最热的代码区段着手。秉承易于开发的宗旨,更轻松的做法是将这些热代码区段与对应的母函数相关联。请参见图 2。


图 2. 英特尔® PBA 工具集提供的用于识别热函数的流分解图。

确定候选对象后,下一步便是判断它们能否实现并行化。

候选对象是否支持并行处理?

在现有串行代码中表达并行性的方法多种多样。然而,从本质上讲,迭代操作比其它类型的操作更适合进行并行化。此外,正如本文所强调的,实施多线程的方法不计其数。不过,出于文章简短性方面的考虑,本部分的示例将只着重讨论 OpenMP 由编译器指导的编译指示的使用情况。如上文所述,OpenMP 小巧、便携,可轻松地以随机方式集成至现有串行代码中。

循环(for、do、while 等)

毫无疑问,循环是向现有串行代码中添加并行性的最佳选择。这主要是因为循环经过预先处置,可轻松地应用“worksharing”结构。Worksharing 指将存在的大问题拆分为多个并行执行线程,并将任务的不同部分分配给这些线程。在循环中,这是常见的数据分解示例(参见上文)。

简单的 for 循环

分析下方的循环可否经过并行处理:

 

	for(i = 0; i < N; i++)
	c[i] = a[i] + b[i];

 

要判断循环是否适合执行并行化,首先应回答几个关键问题。其中最重要的是:

当前的迭代是否独立于之前的迭代?

换句话说,当前的迭代不会依赖任何从之前迭代中获得的值。在上方的示例中,除指定迭代之外,任何迭代都没有引用 c[i] 和。所以说,这里不存在可能加大代码并行化难度的循环依赖性。

使用 OpenMP 进行以下更改,便可实现循环的并行化:

 

	#pragma omp parallel for
	for(i = 0; i < N; i++)
	c[i] = a[i] + b[i];

 

omp parallel for 指令要求编译器在一组遇到该 worksharing 结构的线程之间分配循环迭代。

假设 N=12,该并行区域所处的线程组中包括 4 条线程,上方的编译指示加法和兼容的编译器将生成以下行为:

 

  1. 使用循环迭代数 N 除以线程数 4,获得每条线程的工作量是3。
  2. 为每条线程分派不同的迭代集。如此一来,一条线程被指派为处理迭代 1-3,另一条线程则负责处理迭代 4-6。
  3. 在并行区域的末端,线程重新聚合,此时,整个并行区域中只存在一条线程。

 

我们所做的简单更改将能够显著缩短执行时间。该循环的并行执行时间是串行执行时间的四分之一左右--处理速度约为原来的四倍左右。即使您不熟悉 OpenMP,也很容易看出,基于编译指示的并行化非常容易集成至现有串行代码中,具备巨大的潜在优势。

更复杂的循环

下面,让我们来看一个更复杂的循环示例,该循环为并行化操作带来了一些挑战,阅读下文,了解如何克服这些挑战。该循环是一个数值积分示例,目标是计算值 Pi。在这里,我们将使用典型的基本积分代码。

代码将计算函数从 0 到 1 的积分。.您们当中有些人可能能够利用微积分学知识看出该积分求的是什么值。如果您不具备相关的数学知识,我可以告诉您,该从 0 到 1 的积分计算的是 Pi 的近似值。

为了估算曲线下方的面积(估算积分),我们将累加大量小面积(many = num_steps)。每个面积的宽度为“step”,即 num_steps 的倒数。然后,将这些小面积加在一起,得出到这一刻我们通过计算出的所有面积的总和。

代码如下:

	static long num_steps=100000;
	double step, pi;
	void main(){
		int I;
	double x, sum = 0.0;
	step = 1.0/(double) num_steps;
	for (i=0; I <num_steps; i++){
	x = (i +0.5) * step;
	sum = sum + 4.0/(1.0 + x * x);
	}
	pi = step * sum;
	}

首先,我们需要识别任何与并行化相关的挑战。该示例中便存在多项挑战,不过幸运的是,我们针对每项挑战都有相应的解决方案:

 

  • 我们需要保留 x 的私有(非共享)副本,因为每次迭代都会使用 x 计算部分和(上方代码的第 9 行)。
  • 每条线程均需要累加自己的和,然后将结果传递给最后的 pi 计算(上方代码的第 11 行)。我们将使用 OpenMP 提供的 reduction 子句,为每条线程提供总和私有副本。
  • 在循环的最后,代码需要针对每条线程的求和值执行特定操作。该示例中,我们计算的是部分和,故而采用了“+”运算符和 reduction 子句(in 2)来计算每条线程的部分和的总和。

 

下方的代码含有必要的 omp 编译指示:

	static long num_steps=100000;
	double step, pi;
	void main(){
	int I;
	double x, sum = 0.0;
	step = 1.0/(double) num_steps;
	#pragma omp parallel for private(x) reduction(+:sum)
	for (i=0; I <num_steps; i++){
	x = (i +0.5) * step;
	sum = sum + 4.0/(1.0 + x * x);
	}
	pi = step * sum;
	}

现在,是不是感觉 OpenMP* 行话让您一头雾水?不过,您无需担心。这里的主要概念是,虽然该循环存在一些与循环密切相关的严峻挑战,我们仍然可以通过适当地管理这些依赖性来实施并行化。在 OpenMP* 的帮助下,整个过程将非常简单,不过传统的显式线程化实施同样能够解决问题。

您是否拥有序列算法(如 Huffman 编码)?

序列算法是对现有串行代码实施并行化的最大挑战之一。从定义上讲,序列算法需按照特定的顺序进行处理才能获得正确的结果。Huffman 编码是典型的序列算法示例。Huffman 编码经常会在编解码器中使用,并在现有帧和原有帧之间建立相关性,借以降低已编码内容的比特率。不幸的是,这会为代码的并行化实施带来一些严峻的挑战。虽然有些方法能够消除针对特定算法的限制,但这已远远超出了本文的范围,请恕我不做详细阐述。出于简易性方面的考虑,当对某个序列算法进行线程化处理时,应尝试着判断能否对算法进行相应更改,让它更适合实施并行化,或者干脆转向其它候选对象。

是函数吗?

通常情况下,热代码都包括一个完整的函数。如果该函数反复被调用(在循环体内),或者与其它热函数无关,可能能够使用功能分解法对它实施并行化。这涉及生成一条新线程来执行该函数,使得父线程能够继续当前的操作(假设函数执行的任务不会影响到持续的循环执行)。此类情形的一个典型示例是文字处理程序的自动拼写检查功能。文字处理程序运行一个循环,等待用户的输入。接收到输入后,循环内的一条独立线程开始扫描您输入的文字,而父线程则继续接收您的输入。

其它条件

还有大量其它运算能够实现并行化。本文不会对它们一一进行详细介绍,只是有一点我要强调一下。串行代码中通常会存在一些序列执行任务的区段,但全部属于固有要求。

 

结论

大多数情况下,确实存在大量对串行代码进行并行化的机会,而且实现代码线程化能够带来显著的性能优势。若想识别并充分利用这些机会,您必须了解并行化概念,掌握多种可用于实施并行化的编程方法,并且知道如何识别并行化机会。通过使用市场上已存在的剖析和线程化工具如英特尔® 性能瓶颈分析器,您将能够迅速识别串行代码中的线程化机会。如果发现了对串行部分进行线程化处理所面临的潜在挑战,OpenMP* 等线程化方法可帮助您快速地实施代码并行化。虽然对串行代码进行并行化处理可能遇到各种各样的难题,但我们具备有效的技巧和线程化方法,能够成功解决这些难题。最终,我们将开发出经过良好调试、性能出色的并行应用。

参考资料

 

本文提到的工具可从以下链接中找到:

英特尔® VTune™ Amplifier
英特尔® 性能调试实用程序
英特尔® 性能瓶颈分析器
英特尔® 线程构建模块

 

积极参加社区活动

 

最佳的学习方法是与其他具备相同目标的同路人分享知识和经验。请告诉我们您创建并行应用的情况,包括哪些有效,哪些无效。与行业同仁和英特尔专家沟通,解决主要问题(或帮助其他人解决问题):社区论坛:基于英特尔® 并行架构的线程技术

作者简介

 

Erik Niemeyer 现任英特尔公司软件和解决方案事业部的软件工程师。过去十年来,Erik 一直致力于优化在英特尔微处理器上运行的应用的性能,专攻快速并行开发和微架构调试领域。在业余时间,Erik 很喜欢爬山。您可以发送电子邮件至 erik.a.niemeyer@intel.com,与他取得联系。

 

Ken Strandberg 是技术市场推广通信公司 Catlow Communications*(www.catlowcommunications.com)的负责人。Strandberg 致力于为新兴技术公司、财富 100 强企业和跨国公司撰写技术市场推广和非市场推广内容、视频和交互式脚本,以及培训教材等。他所撰写的内容涉及广泛的硬件和软件行业。您可以发送电子邮件至 ken@catlowcommunications.com,与他取得联系。

通知

 

本文所提供之信息均与英特尔产品相关。本文不代表英特尔公司或其它机构向任何人明确或隐含地授予任何知识产权。除相关产品的英特尔销售条款与条件中列明之担保条件以外,英特尔公司不对销售和/或使用英特尔产品做出任何其它明确或隐含的担保,包括对适用于特定用途、适销性,或不侵犯任何专利、版权或其它知识产权的担保。除非经英特尔书面同意,英特尔产品并非设计或有意用于任何英特尔产品故障可能会引起人身伤亡事故的应用领域。英特尔可以随时在不发布声明的情况下修改规格和产品说明。设计者不得依赖于标记为“保留”或“未定义”的任何特性或说明。英特尔保留今后对其定义的权利,对于因今后对其进行修改所产生的冲突或不兼容性概不负责。此处信息可能随时更改,恕不另行通知。请勿使用本信息来对某个设计做出最终决定。文中所述产品可能包含设计缺陷或错误,已在勘误表中注明,这可能会使产品偏离已经发布的技术规范。英特尔会提供最新的勘误表备索。订购产品前,请联系您当地的英特尔销售办事处或分销商了解最新技术规范。如欲获得本文或其它英特尔文献中提及的带订单编号的文档副本,可致电 1-800-548-4725,或访问: http://www.intel.com/design/literature.htm

英特尔、Intel 标识、Xeon 和至强是英特尔公司在美国和其他国家(地区)的商标。

*文中涉及的其它名称及商标属于各自所有者所有。

. 版权所有 © 2011 英特尔公司。保留所有权利。

优化声明:英特尔® 编译器、相关库和相关开发工具可能包含或使用针对英特尔® 和非英特尔微处理器中都存在的指令集(例如 SIMD 指令集)实现优化的选项,不过面向非英特尔微处理器的优化程度可能稍差一些。此外,部分面向英特尔编译器的编译器选项(包括并非英特尔微架构专用的选项)也为英特尔微处理器保留了下来。如欲获得有关英特尔编译器选项包括指令集和相关特定微处理器的详细说明,请参阅“编译器选项”下的“英特尔® 编译器用户和参考指南”。英特尔® 编译器产品中的大多数库例程面向英特尔微处理器的优化程度要高于其它微处理器。英特尔® 编译器产品中的编译器和库可为英特尔和英特尔兼容微处理器提供优化特性,具体取决于您所选择的选项以及代码和其它因素。您将能够在英特尔微处理器上获得额外性能。英特尔® 编译器、相关库和相关开发工具针对非英特尔微处理器的优化程度可能与英特尔微处理器相同或不同。这些优化特性包括英特尔® SIMD 流指令扩展 2(英特尔® SSE2)、英特尔® SIMD 流指令扩展 3(英特尔® SSE3)和追加 SIMD 流指令扩展 3(英特尔® SSSE3)指令集,以及其它优化特性。对于在非英特尔制造的微处理器上进行的优化,英特尔不对相应的可用性、功能或有效性提供担保。该产品中依赖于处理器的优化仅适用于英特尔微处理器。虽然英特尔坚信我们的编译器和库是帮助您在英特尔® 和非英特尔微处理器上获得最佳性能的理想选择,不过仍建议您对其它编译器和库进行评估,选择最符合您要求的解决方案。我们希望通过致力于最大限度提升所有编译器或库的性能来赢得您的青睐,如果您发现我们存在不足之处,欢迎您指正。通知版本 #20101101

有关编译器优化的更完整信息,请参阅优化通知
标签: