GCC中关于代码为什么没有进行向量化的分析方法

1.问题描述

使用GCC 10.2编译下面的代码,编译选项为-O3 -msse4.2 -fprefetch-loop-arrays,其中-O3默认包含向量化,bread函数中的3个循环看起来也可以进行向量化,但实际上只有最后一个循环进行了向量化。

#include <stdio.h>
#include <string.h>
#include <sys/time.h>

#define NUM 512*1024

static char array[NUM];

long bread(void* buf, long nbytes)
{
    long sum = 0;
    register long *p, *next;
    register char *end;

    p = (long*)buf;
    end = (char*)buf + nbytes;
    for (next = p + 128; (void*)next <= (void*)end; p = next, next += 128) {
        sum +=
            p[0]+p[1]+p[2]+p[3]+p[4]+p[5]+p[6]+p[7]+
            p[8]+p[9]+p[10]+p[11]+p[12]+p[13]+p[14]+
            p[15]+p[16]+p[17]+p[18]+p[19]+p[20]+p[21]+
            p[22]+p[23]+p[24]+p[25]+p[26]+p[27]+p[28]+
            p[29]+p[30]+p[31]+p[32]+p[33]+p[34]+p[35]+
            p[36]+p[37]+p[38]+p[39]+p[40]+p[41]+p[42]+
            p[43]+p[44]+p[45]+p[46]+p[47]+p[48]+p[49]+
            p[50]+p[51]+p[52]+p[53]+p[54]+p[55]+p[56]+
            p[57]+p[58]+p[59]+p[60]+p[61]+p[62]+p[63]+
            p[64]+p[65]+p[66]+p[67]+p[68]+p[69]+p[70]+
            p[71]+p[72]+p[73]+p[74]+p[75]+p[76]+p[77]+
            p[78]+p[79]+p[80]+p[81]+p[82]+p[83]+p[84]+
            p[85]+p[86]+p[87]+p[88]+p[89]+p[90]+p[91]+
            p[92]+p[93]+p[94]+p[95]+p[96]+p[97]+p[98]+
            p[99]+p[100]+p[101]+p[102]+p[103]+p[104]+
            p[105]+p[106]+p[107]+p[108]+p[109]+p[110]+
            p[111]+p[112]+p[113]+p[114]+p[115]+p[116]+
            p[117]+p[118]+p[119]+p[120]+p[121]+p[122]+
            p[123]+p[124]+p[125]+p[126]+p[127];
    }
    for (next = p + 16; (void*)next <= (void*)end; p = next, next += 16) {
        sum +=
            p[0]+p[1]+p[2]+p[3]+p[4]+p[5]+p[6]+p[7]+
            p[8]+p[9]+p[10]+p[11]+p[12]+p[13]+p[14]+
            p[15];
    }
    for (next = p + 1; (void*)next <= (void*)end; p = next, next++) {
        sum += *p;
    }
    return sum;
}


int main()
{
    int i ;
    FILE *out;
    out = fopen("output.txt","w");

    long time_use=0;
    struct timeval start;
    struct timeval end;
    
    gettimeofday(&start,NULL);

    memset((void*)array, 1, NUM);

    for(i=0;i<1000;i++)
    {
        //主要是计算bread函数的执行时间
        long sum = bread(array, NUM);

        if (out!=NULL)
            fprintf(out,"%ld\t",sum);
        sum = 0;
    }
    gettimeofday(&end,NULL);
    time_use=(end.tv_sec-start.tv_sec)*1000000+(end.tv_usec-start.tv_usec);//us
    printf("time = %ld\n", time_use);

    return 0;
}

增加-fopt-info-vec-all选项,只打印关于向量化的优化报告,命令为gcc -O3 -msse4.2 -fprefetch-loop-arrays -fopt-info-vec-all=vec.log
vec.log文件包含下面的信息(一部分),只有最后一个循环(45行)进行了向量化。

bw_mmap_rd.c:45:5: optimized: loop vectorized using 16 byte vectors
bw_mmap_rd.c:39:5: missed: couldn't vectorize loop
bw_mmap_rd.c:39:5: missed: not vectorized: unsupported data-type
bw_mmap_rd.c:17:5: missed: couldn't vectorize loop
bw_mmap_rd.c:17:5: missed: not vectorized: unsupported data-type
bw_mmap_rd.c:9:6: note: vectorized 1 loops in function.

2.分析

增加-fdump-tree-vect-all选项,输出关于向量化的中间表示文件,命令为gcc -O3 -msse4.2 -fprefetch-loop-arrays -fdump-tree-vect-all
生成的bw_mmap_rd.c.161t.vect文件包含下面的信息(一部分)

bw_mmap_rd.c:39:5: note:  Cost model analysis: 
  Vector inside of loop cost: 516
  Vector prologue cost: 20
  Vector epilogue cost: 284
  Scalar iteration cost: 256
  Scalar outside cost: 32
  Vector outside cost: 304
  prologue iterations: 0
  epilogue iterations: 1
bw_mmap_rd.c:39:5: missed:  cost model: the vector iteration cost = 516 divided by the scalar iteration cost = 256 is greater or equal to the vectorization factor = 2.
bw_mmap_rd.c:39:5: missed:  not vectorized: vectorization not profitable.
bw_mmap_rd.c:39:5: missed:  not vectorized: vector version will never be profitable.
bw_mmap_rd.c:39:5: missed:  Loop costings may not be worthwhile.

对于第二个循环,GCC的代价模型计算出的向量化之后的代价(cost)更高,没有收益,所以没有进行向量化。因为SSE指令的XMM寄存器是128bit,16个字节,而循环中的元素是long类型,8个字节,所以一个向量只能包含2个元素,这里的vectorization factor就是2。向量化之前的代价是:Scalar iteration cost,即256。向量化之后的代价是Vector inside of loop cost / vectorization factor,即516 / 2 = 258。

bw_mmap_rd.c:17:5: note:  Cost model analysis: 
  Vector inside of loop cost: 5636
  Vector prologue cost: 20
  Vector epilogue cost: 2076
  Scalar iteration cost: 2048
  Scalar outside cost: 32
  Vector outside cost: 2096
  prologue iterations: 0
  epilogue iterations: 1
bw_mmap_rd.c:17:5: missed:  cost model: the vector iteration cost = 5636 divided by the scalar iteration cost = 2048 is greater or equal to the vectorization factor = 2.
bw_mmap_rd.c:17:5: missed:  not vectorized: vectorization not profitable.
bw_mmap_rd.c:17:5: missed:  not vectorized: vector version will never be profitable.
bw_mmap_rd.c:17:5: missed:  Loop costings may not be worthwhile.

对于第三个循环,和第二个循环一样,向量化之前的代价是2048,向量化之后的代价是5636 / 2 = 2818,没有收益,所以没有向量化。

bw_mmap_rd.c:45:5: note:  Cost model analysis: 
  Vector inside of loop cost: 20
  Vector prologue cost: 20
  Vector epilogue cost: 44
  Scalar iteration cost: 16
  Scalar outside cost: 32
  Vector outside cost: 64
  prologue iterations: 0
  epilogue iterations: 1
  Calculated minimum iters for profitability: 4
bw_mmap_rd.c:45:5: note:    Runtime profitability threshold = 4 
bw_mmap_rd.c:45:5: note:    Static estimate profitability threshold = 14 

对于第一个循环,向量化之前的代价是16,向量化之后的代价是20 / 2 = 10,向量化是有益的,GCC计算出的profitability threshold是4,所以会进行向量化。
上面的代码生成的可执行文件的运行时间为(us):

time = 16267

可以增加-fvect-cost-model=unlimited选项 ,强制让GCC进行向量化,命令为gcc -O3 -msse4.2 -fprefetch-loop-arrays -fopt-info-vec-all=vec.log -fvect-cost-model=unlimited
vec.log优化报告显示所有的循环都进行了向量化:

bw_mmap_rd.c:45:5: optimized: loop vectorized using 16 byte vectors
bw_mmap_rd.c:39:5: optimized: loop vectorized using 16 byte vectors
bw_mmap_rd.c:17:5: optimized: loop vectorized using 16 byte vectors
bw_mmap_rd.c:9:6: note: vectorized 3 loops in function.

根据bw_mmap_rd.c.161t.vect文件,-fvect-cost-model=unlimited选项会禁用代价模型:

bw_mmap_rd.c:45:5: note:  cost model disabled.
bw_mmap_rd.c:39:5: note:  cost model disabled.
bw_mmap_rd.c:17:5: note:  cost model disabled.

三个循环都进行向量化的情况下,生成的可执行文件的运行时间变长,性能变差:

time = 74177

所以GCC只给第一个循环进行了向量化。

3.补充

3.1 优化选项

前面的优化选项中,-O3包含了很多其他的优化选项,也包括-ftree-loop-vectorize(循环向量化)和-ftree-slp-vectorize(slp向量化)选项。-msse4.2指定使用SSE4.2指令集,如果没有指定GCC会默认使用SSE2指令集。-fprefetch-loop-arrays打开数组预取优化,GCC会生成prefetcht0类型的汇编指令,将数据提前移动到dcache中。

3.2 log信息

使用gcc -O3 -msse4.2 -fprefetch-loop-arrays -fopt-info-vec-all=vec.log编译,优化报告包含下面的内容:

bw_mmap_rd.c:45:5: optimized: loop vectorized using 16 byte vectors
bw_mmap_rd.c:39:5: missed: couldn't vectorize loop
bw_mmap_rd.c:39:5: missed: not vectorized: unsupported data-type
bw_mmap_rd.c:17:5: missed: couldn't vectorize loop
bw_mmap_rd.c:17:5: missed: not vectorized: unsupported data-type
bw_mmap_rd.c:9:6: note: vectorized 1 loops in function.

其中bw_mmap_rd.c:39:5: missed: not vectorized: unsupported data-type是GCC中gcc\tree-vect-loop.c的一段代码的输出:

  /* TODO: Analyze cost. Decide if worth while to vectorize.  */
  if (dump_enabled_p ())
    {
      dump_printf_loc (MSG_NOTE, vect_location, "vectorization factor = ");
      dump_dec (MSG_NOTE, vectorization_factor);
      dump_printf (MSG_NOTE, "\n");
    }

  if (known_le (vectorization_factor, 1U))
    return opt_result::failure_at (vect_location,
                   "not vectorized: unsupported data-type\n");
  LOOP_VINFO_VECT_FACTOR (loop_vinfo) = vectorization_factor;
  return opt_result::success ();

根据bw_mmap_rd.c.161t.vect文件中的信息可知,这是vectorization_factor = 1对应的输出,此时一个向量只包含一个long类型的元素,GCC不支持这种向量结构(一个向量只有1个元素就是张量了),所以会打印这种log信息。而第三个循环满足向量化的条件,所以不会判断vectorization_factor = 1的情形,也就没有这个输出了。
所以我们只需要看bw_mmap_rd.c:39:5: missed: couldn't vectorize loop就行,bw_mmap_rd.c:39:5: missed: not vectorized: unsupported data-type不是这个循环没有进行向量化的原因。

bw_mmap_rd.c:39:5: note:   ==> examining statement: sum_310 = _288 + sum_318;
bw_mmap_rd.c:39:5: note:   get vectype for scalar type: long int
bw_mmap_rd.c:39:5: note:   vectype: vector(1) long int
bw_mmap_rd.c:39:5: note:   nunits = 1
bw_mmap_rd.c:39:5: note:   ==> examining statement: next_312 = next_332 + 128;
bw_mmap_rd.c:39:5: note:   skip.
bw_mmap_rd.c:39:5: note:   ==> examining statement: if (end_301 >= next_312)
bw_mmap_rd.c:39:5: note:   skip.
bw_mmap_rd.c:39:5: note:   vectorization factor = 1
bw_mmap_rd.c:39:5: missed:   not vectorized: unsupported data-type
bw_mmap_rd.c:39:5: missed:  can't determine vectorization factor.
bw_mmap_rd.c:39:5: note:  ***** Analysis failed with vector mode V8QI
bw_mmap_rd.c:39:5: missed: couldn't vectorize loop
bw_mmap_rd.c:39:5: missed: not vectorized: unsupported data-type
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容