使用Intel 向量化编译器优化性能(2)

By | 04月02日
Advertisement

使用Intel 向量化编译器优化性能(2)

本文部分翻译自Intel网站,部分选自Intel编译器文档以及中科院计算所的一些资料

首先要向大家道歉,由于疏忽,才发现intel的这个教程可能只是针对5.0版本的编译器,现在已经是7.1了,所以文中的例子可能在现在的编译器上会有不同的表现,我会把每一个例子测试一下,并结合从其它渠道找到的关于intel编译器的文档来给大家介绍向量化编译技术.

1. 如何编写向量化程序

向量化代码是一个不断尝试的过程,每一个循环在被向量化前都可能需要调整很多次,依据下面几条原则去做能更好帮你完成工作.

a) 先尝试编译一次,会有一些循环是可以直接被向量化的.

一些现有的代码可能已经符合了编译器的要求,不需要做任何修改就可以直接被向量化,不管怎么样,先试一下.

b) 找到你的代码中的那些最影响程序性能的循环

使用代码分析工具来寻找你的代码中那些经常被调用的循环,这些循环最值得被向量化,即使是一个很小的循环,如果被频繁调用的话,对性能的影响是很可观的,所以能够向量化这个循环将给你的代码带来很大的性能的提高,Intel VTune Performance Analyzer 可以帮你找到这些性能上的瓶颈,你可以从Intel的网站上下载并免费试用 http://www.intel.com/software/products/vtune. 关于VTune的具体使用方法我会在别的时间给大家介绍,现在我就把它能做什么简单说一下

1. 附加在一个可执行文件上,在这程序运行的期间搜集处理器数据.

2. 跟踪你的代码,并把关于你的代码的性能的数据记录下来,比如函数A运行了多少时钟周期,使用了多少条指令…等,这样你就很容易找到那些最影响性能的循环.

3. 把运行的数据生成图形报表.

4. 对统计结果提出建议,知道你对代码的优化.

c) 使用#pragma ivdep

这个上次说明过了.

d) 重新编写这个循环

有时候一个循环在结构上注定无法向量化,这时只能另起炉灶了...

2. 循环中的数据依赖

为了从分利用Intel编译器,你需要了解数据依赖产生的原因以及如何处理这些情况

a) 这里因为Intel网站的例子可能有问题,应该是编译器版本问题,所以我换了个例子以说明问题,当然这样的例子在实际应用中没有什么意义,这里只是为了说明问题

下面这段程序是无法被向量化的.

float data[100];

int i = 0;

for (i = 0; i < 100-1; ++i)

{

s1: data[i] = data[i-1]*0.25 + data[i]*0.5+data[i+1]*0.25;

}

在这里在S1语句中的data[i]对data[i-1]产生数据依赖,data[i]被data[i-1]在上一个循环迭代中所修改,不明白?,这么看:

data[i-1] 的下标比 data[i]小1,操作中data[i-1]总是会覆盖data[i],这就说明了上一次说的向量化不是并行化,因为在向量化中是要考虑这样的相互影响,所以不能说是完全的并行化,虽然数据是被SIMD方式并行操作的.偶是这么理解的,或许有不正确的地方,请大家及时指正.再看下面的说明可能会更清晰一些.

上面的例子中 当i=1时: i=2时

read data[0] read data[1]

read data[1] read data[2]

read data[2] read data[3]

write data[1] write data[2]

当使用标量循环时不会有什么问题,data[1]数据依次被改写,但是在SIMD并行操作中问题就出现了i=2时 read data[1]时,数据就可能已经被i=1的write data[i]修改,这就改变了这个循环的本意.这是绝对不可以.

b) 上面是数据依赖的实际例子,下面我们从理论上解释一下数据依赖的原因.

数据依赖产生的条件就是在操作中发现可能对内存中发生的覆盖的存取,如果代码中有2个引用,那么.

1. 这2个引用可能是别名,而他们指向了同一块内存或是两块相互覆盖的内存

2. 如果是数组,那就很据他们的下标来判断是否覆盖同一块内存.

对于数组,编译器的数据依赖分析器将使用一系列逐渐强化的测试来获取循环在时间和空间上的开销.首先编译器会对数组的每个维进行一些简单的测试,直到在任何一个维上都没有发现数据依赖的问题.在测试前,使用多维数组时可能发生的在它们定义边界内的数据交叉可以被转换为一个线性化的形式,其中的一些测试会使用快速最大公约数测试, 测试会使用达到数组边界的数据已确保不会有数据覆盖的问题.

如果上面的测试失败,编译器就会使用Fourier-Motzkin消去法来解决所有维中的数据依赖问题.

这里我发现Intel网站的教程中并没有提到这一过程,可能这就是在高版本编译器中更容易解决数据依赖问题而不需要人为去修改代码的原因,实际上我说的那个有问题的例子就是直接可以进行向量化编译而不需要修改.

下面看几个循环的例子.

1. 循环结构

可以使用for/while/until来构成循环,但是循环只能有一个入口和一个出口.

While( i< 100)

{

a[i] = b[i] * c[i];

if (a[i] < 0.0)

{

a[i] = 0.0;

}

++i;

}

这个可以被向量化

While( i< 100)

{

a[i] = b[i] * c[i];

if (a[i] < 0.0)

{

a[i] = 0.0;

break;

}

++i;

}

这样不行,不能有多个出口

2. 循环退出条件

循环退出条件决定了,循环执行的迭代次数,比如一个固定的次数.总之这必须是一个可以确定循环次数的表达式.

a) 常量,如100

b) 一个不被改变的变量,如 i=100

c) 一个计算循环次数的线性函数 如 i=100-1

下面是几个示例

int k = 100;int i = 0;

while(i< k - 1)

{

a[i] = b[i] * c[i];

if (a[i] < 0.0)

{

a[i] = 0.0;

}

++i;

}

条件确定

int k = 100;int i = 0;

while(i< k – i)

{

a[i] = b[i] * c[i];

if (a[i] < 0.0)

{

a[i] = 0.0;

}

++i;

}

条件不确定,循环次数依赖i

for (i = m;i < n;++i)

{

a[i] = i + 1;

}

条件确定,循环次数为 n-m

Similar Posts:

  • Visual C++利用Intel C++ 编译器提升多核性能与多媒体指令支持获取更高的程序效率与缩小程序体积

    Intel c++编译器有下列优点,建议VC++项目开发采用intel c++编译器取代VS自带c++编译器: 与 Microsoft Visual C++ 相兼容,可以嵌入 Microsoft Visual Studio 开发环境. 支持最新的多核处理器,并提供安全功能,可以通过执行堆栈桢运行时错误检查,使得缓冲区溢出安全漏洞更不易受到攻击. 支持多线程应用程序,支持 Open MP,拥有自动并行化功能. 遵循 ANSI C/C++ 及 ISO C/C++ 的标准. 包括Intel Debug

  • 程序速度性能优化中编译器优化和SSE优化的问题

    编译器:Intel C++编译器 测试环境: CentOS [问题] 针对两个同样功能的程序,一个不使用SSE,一个使用SSE (intel intrinsics),结果当不适用编译器优化时,使用SSE时速度优于不适用SSE,而当编译器使用O2优化时,使用SSE反而使速度下降. 样例程序sse_normal.c: #include using namespace std; const int N=12000; int main() { float f1[N],f2[N],f3[N]; float

  • 每个程序员应知晓的编译器优化相关内容

    此文转载.原文地址:https://msdn.microsoft.com/zh-cn/magazine/dn904673.aspx 高级编程语言提供了许多抽象的编程构造(如函数.条件语句和循环),可以让我们获得令人惊讶的工作效率.但是,在高级编程语言中编写代码的一个缺点是性能可能显著降低.理想情况下,您应编写易于理解.可维护的代码 - 同时不影响性能.出于此原因,编译器将尝试自动优化代码以提高其性能,如今在这方面的操作已经相当复杂.他们可以转换循环.条件语句和递归函数:消除整个代码块:并且利用目

  • 关于编译器优化方面的知识

    通常编译器可以分成前端(FrontEnd/FE)和后端(BackEnd/BE)两个部分,其中前端负责将用户的源代码翻译成一种编译器的内部表示(Intermedium Representation/IR),我们简称IR. 这个就是通常词法分析,语法分析所做的事情.对于不同的源代码语言,我们需要不同的前端,但是我们可以通过使用公共的IR,使得对于不同的语言,可以使用相同编译器的后端.而编译器的后端,现在通常分成两个部分,一部分负责同平台无关的优化工作,我们通常称为中间端(MiddleEnd/ME),

  • ASP.NET中常用的优化性能方法

    文章转自:http://i.aspx1.com/showtopic-1504.htm ASP.NET中常用的优化性能方法 1. 数据库访问性能优化 数据库的连接和关闭 访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源.ASP.NET中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响.系统将用户的数据库连接放在连接池中,需要时取出,关闭时收回连接,等待下一次的连接请求.连接池的大小是有限的,如果

  • ASP.NET优化性能技巧:数据库,字符串及其他

    ASP.NET优化性能技巧1. 数据库访问性能优化 数据库的连接和关闭 访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源.asp.net中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响.系统将用户的数据库连接放在连接池中,需要时取出,关闭时收回连接,等待下一次的连接请求.连接池的大小是有限的,如果在连接池达到最大限度后仍要求创建连接,必然大大影响性能.因此,在建立数据库连接后只有在真正需要操

  • 编译器优化陷阱——全局指针多次使用异常

    做程序开发一定会和编译器打交道,编译器优化可以给我们代码运行带来一定的提升,但也可能存在一些意想不到的问题.下面就是我在开发时候遇到的一个坑,希望可以给大家一些借鉴 直接上代码说话吧 1 static unsigned char* s_data = NULL; //存储一帧视频数据 2 void DoRendering () 3 { 4 // D3D11 case 5 if (s_DeviceType == kUnityGfxRendererD3D11 && EnsureD3D11Reso

  • C和Java效率对比试验和编译器优化影响

    首先得承认这不是一个好例子,逻辑过于简单,受环境的干扰也特别大.不能作为评价一门语言综合效率的用例,仅仅是基于个人兴趣的小实验的记录. C语言版本1 #include<stdio.h> int main(){ long a = 0; for(long i=0; i<100000000; i++){ a += i; } printf("%ld\n", a); return0; } Java版本1 public class T{ publicstaticvoid main

  • 由一道题目想到的C++编译器优化问题

    这两天看到了一个问题,看似简单,但是用的知识着实不少,原题如下: #include "stdafx.h" class Base { public: Base(){} virtual ~Base(){} Base(const Base &other); // 只声明, 没定义 private: Base &operator=(const Base &other); } ; int _tmain(int argc, _TCHAR* argv[]) { const B

  • Intel C++ 编译器在VC6下使用STLPort

    用Visual C++ 6.0编译好STLport v5.1.7的动态库,此动态库只有两个依赖:msvcrt.dll和kernel32.dll,这正是我想要的,在使用过程中发现:使用Visual C++ 6.0编译器可以正常编译链接自己的应用程序,可是,当换成Intel C++ 编译器时,在链接时老是报LNK2001错误,试遍了所有的方法都不行, 前几天,当我翻看着STLport的源码时,发现在stlport/stl/config目录下的_msvc.h头文件中有这样一个宏:__ICL,和_STL

Tags: