zoukankan      html  css  js  c++  java
  • SIMD自动向量化

    #pragma simd

    该编译指示(SIMD)是12.0编译器最新提供的功能。他可以强制性的让编译器做自动并行化。 对于其他编译指示比如#pragma ivdep来说, 如果编译器编译时发现用户提供的编译指示条件不满足, 那么编译器是不会根据编译指示来进行自动向量化的。也就是说, 编译器实际上还是会进行编译时的依赖关系检查。 而对于#pargam simd来说, 无论编译时条件如何, 编译器总是会进行自动向量化。这种情况下, 用户需要自己去保证被向量化的循环上语义的正确性, 需要自己保证被向量化变量之间的依赖关系的正确性。
    我们用一个例子来说明编译器的行为区别。

    File vec5.c
    void vec5(int* a, int n)
    {
    int i;
    for(i=0;i<n;i++)
    a[i] = a[i-1];
    }

    对上面这个例子, 由于存在flow-dependence关系, 程序中的循环明显是不能被向量化的。

    vec5.c
    C:/test/vec5.c(6) (col. 2): remark: loop was not vectorized: existence of vector dependence.
    C:/test/vec5.c(7) (col. 3): remark: vector dependence: assumed FLOW dependence between a line 7 and a line 7.
    此时我们加上#pragma ivdep想让编译器忽略循环之间的依赖关系(当然这种操作是不符合原始语义的)。

    void vec5(int* a, int n)
    {
    int i;
    #pragma ivdep
    for(i=0;i<n;i++)
    a[i] = a[i-1];
    }

    这时编译该程序, 程序的输出与不加#pragma ivdep时相同。 编译器在进行编译的时候检查发现了循环之间不可避免的依赖关系, 即使有#pragma ivdep存在, 编译器也还是不会对该循环进行向量化。
    如果我们加上#pragma simd, 重新编译程序:

    void vec5(int* a, int n)
    {
    int i;
    #pragma simd
    for(i=0;i<n;i++)
    a[i] = a[i-1];
    }

    这时编译输出:

    vec5.c
    C:/test/vec5.c(6) (col. 2): remark: SIMD LOOP WAS VECTORIZED.

    程序中的循环被向量化了, 但是此时我们显然不会得到一个正确的结果。
    我们再看一个用SIMD的正确的例子。 比如下面这个程序:

    void vec5(int* a, int n, int m)
    {
    int i;
    for(i=0;i<n;i++)
    a[i] = a[i-m];
    }

    编译该程序: icl /O2 /c /Qvec_report:3, 编译器会报告该程序因为存在向量依赖关系而不能被向量化。在该程序中, 如果我们可以保证m大于等于4, 那么我们可以加入#pragma simd指示如下所示:

    void vec5(int* a, int n, int m)
    {
    int i;
    #prgama simd
    for(i=0;i<n;i++)
    a[i] = a[i-m];
    }

    此时编译该程序可以得到正确的自动向量化的代码。

    SIMD的支持可以使得程序员可以更自由的控制程序中的向量化。 相比其他编译指示来说, SIMD提供更丰富的语法来满足程序员在各种条件下的向量化处理。 如果你发现现有的编译指示如法满足语义的需要或者向量化性能无法满足期望, 你可以尝试使用SIMD指示。 但是由于SIMD是有程序员来保证程序向量化语义的正确性, 在自由使用的同时,程序员需要很小心的检查程序的语义以保证程序的正确性。
    关于SIMD编译指示的详细使用, 请参考其他文档。

    SIMD编译指示中的向量化长度的指定

    SIMD 向量化是英特尔编译器12.0版支持的一个新功能。 该向量化使用 #pragma simd (c/c++) 或者 !DIR$ SIMD(fortran) 编译指示来有效地实现循环向量化。该向量化语法如下所示:

    #pragma simd [clause[ [,] clause]...]

    在#pragma simd之后可以带一些字句来让编译器得到更多的信息以便做更好的优化。 这些字句可以是:

    vectorlength (vl_list)
    private (expr_list, expr)
    linear (var_step_list)
    reduction (oper_var_list)
    [no]assert

    在本文中, 我们针对vectorlength子句来进行一些讨论。

    vectorlength(num1, num2, …, numN)

    vectorlength (简称VL) 用来指导向量优化单元从指定的若干向量长度num1, num2, … , numN中选择某个长度来进行向量化。对于选定的VL,向量循环的每一次执行的计算工作相当于原来标量循环VL次执行的计算工作。多个向量长度子句会合并成一个集合, 编译器从这些指定的长度中选择某个针对目标机器的最优长度进行向量化。
    看下面这个例子。

    void foo(int* a, int* b)
    {
    #pragma simd vectorlength(4)
    #pragma nounroll
    for(int i=0;i<1024;i++)
    {
    a[i]=b[i];
    }
    }

    在支持128bit向量长度的机器上, 编译器生成相应的SSEx指令来向量化上面程序中的循环。 用户指定的VL是4, 理论上说, 一次向量循环相当于4次标量循环。 在上面这个例子中, 每次向量操作的长度正好是32*4=128bit。理论上的向量循环次数为1024/4=256次。如果我们指定VL为8, 理论上说编译器在一次向量循环中会做两次128位的向量操作, 相当于做了256位(32*4+32*4)的向量操作, 对应的循环次数为1024/8=128次。 当然,如果我们不加上编译指示#pragma nounroll来阻止编译器的loop unroll优化,编译器会进行循环展开优化, 这样实际的循环次数可能更少。

    在上面的例子中, 如果我们指定多个VL, 比如#pragma simd vectorlength(4,8)。编译器会根据目标机器的向量长度和其内部的cost-modeling计算选择最优的VL。

    Vectorlength 不是必须指定的。 如果没有指定vectorlength, 一般情况下编译器会根据目标机器的向量长度和标量循环的变量类型来选择合适的VL。 那我们在什么情况下需要指定vectorlength呢? 一般情况下, 这需要根据程序的语义和对性能的要求来决定。 比如下面这个例子。

    void foo(double a*, double * b, int m, int n)
    {
    for(int i=n; i<m;i++)
    {
    a[i] = b[i];
    }
    }

    我们要对该例子中的循环进行向量化。 但是如果a 和 b存在别名关系, 比如a = b+n, 那么我们就无法通过restrict关键字来向量化上面这个循环。 如果我们的程序语义可以保证n一定满足: n=2 或者n=3, 那么我们就可以通过simd来向量化这个循环。 比如加上#pragma vectorlength(2). 这样在支持SSEx的目标机器上, 可以做到128bit的循环向量化。 在支持AVX的目标机器上, 默认情况下的向量化长度是256bit的, 所以如果没有VL为2的指定, 那么编译器可能会生成256bit的向量化循环, 对应的VL为4, 在这种情况下这样的向量化是不符合程序的语义的, 也就会产生运行时错误。 所以我们需要显式指定vectorlength(2)来满足语义的需要。

    Vectorlength的值一定是2的幂次方。 不同类型的数据针对不同的目标机器有不同的VL的范围。 比如对于支持128bit SSEx的机器来说, char类型操作的VL为16, short类型可以为8或16, int类型则可以为4,8,16。 如果指定了不合适的VL, 编译器会报告一个warning, 同时放弃对应的向量化操作。

  • 相关阅读:
    Interop.SQLDMO组件无法连接SQL2008
    关于数据连接配置connectionStrings的写法
    SQL锁表语句 (转摘)
    从思想到命运
    CIO:2013年OA选型六步走(摘)
    JS SCRIPT Confirm
    Silverlight 2 RTW中ToolTipService.ToolTip不继承父节点的DataContext的问题
    在Silverlight里实现类似WPF的UniformGrid
    尝试通过HttpWebRequest向TAOBAO批量发布商品及上传图片
    简单探照灯遮照效果的几个Silverlight程序(Silverlight 2.0)
  • 原文地址:https://www.cnblogs.com/blockcipher/p/2913002.html
Copyright © 2011-2022 走看看