我正在努力确保gcc对我的循环进行矢量化.事实证明,通过使用-march=znver1(或-march=native),gcc跳过了一些循环,即使它们可以矢量化.为什么会这样?

在这段代码中,将每个元素乘以一个标量的第二个循环不是矢量化的:

#include <stdio.h>
#include <inttypes.h>

int main() {
    const size_t N = 1000;
    uint64_t arr[N];
    for (size_t i = 0; i < N; ++i)
        arr[i] = 1;

    for (size_t i = 0; i < N; ++i)
        arr[i] *= 5;

    for (size_t i = 0; i < N; ++i)
        printf("%lu\n", arr[i]); // use the array so that it is not optimized away
}

gcc -O3 -fopt-info-vec-all -mavx2 main.c:

main.cpp:13:26: missed: couldn't vectorize loop
main.cpp:14:15: missed: statement clobbers memory: printf ("%lu\n", _3);
main.cpp:10:26: optimized: loop vectorized using 32 byte vectors
main.cpp:7:26: optimized: loop vectorized using 32 byte vectors
main.cpp:4:5: note: vectorized 2 loops in function.
main.cpp:14:15: missed: statement clobbers memory: printf ("%lu\n", _3);
main.cpp:15:1: note: ***** Analysis failed with vector mode V4DI
main.cpp:15:1: note: ***** Skipping vector mode V32QI, which would repeat the analysis for V4DI

gcc -O3 -fopt-info-vec-all -march=znver1 main.c:

main.cpp:13:26: missed: couldn't vectorize loop
main.cpp:14:15: missed: statement clobbers memory: printf ("%lu\n", _3);
main.cpp:10:26: missed: couldn't vectorize loop
main.cpp:10:26: missed: not vectorized: unsupported data-type
main.cpp:7:26: optimized: loop vectorized using 16 byte vectors
main.cpp:4:5: note: vectorized 1 loops in function.
main.cpp:14:15: missed: statement clobbers memory: printf ("%lu\n", _3);
main.cpp:15:1: note: ***** Analysis failed with vector mode V2DI
main.cpp:15:1: note: ***** Skipping vector mode V16QI, which would repeat the analysis for V2DI

-march=znver1包括-mavx2,所以我认为gcc出于某种原因 Select 不将其矢量化:

~ $ gcc -march=znver1 -Q --help=target
The following options are target specific:
  -m128bit-long-double              [enabled]
  -m16                              [disabled]
  -m32                              [disabled]
  -m3dnow                           [disabled]
  -m3dnowa                          [disabled]
  -m64                              [enabled]
  -m80387                           [enabled]
  -m8bit-idiv                       [disabled]
  -m96bit-long-double               [disabled]
  -mabi=                            sysv
  -mabm                             [enabled]
  -maccumulate-outgoing-args        [disabled]
  -maddress-mode=                   long
  -madx                             [enabled]
  -maes                             [enabled]
  -malign-data=                     compat
  -malign-double                    [disabled]
  -malign-functions=                0
  -malign-jumps=                    0
  -malign-loops=                    0
  -malign-stringops                 [enabled]
  -mamx-bf16                        [disabled]
  -mamx-int8                        [disabled]
  -mamx-tile                        [disabled]
  -mandroid                         [disabled]
  -march=                           znver1
  -masm=                            att
  -mavx                             [enabled]
  -mavx2                            [enabled]
  -mavx256-split-unaligned-load     [disabled]
  -mavx256-split-unaligned-store    [enabled]
  -mavx5124fmaps                    [disabled]
  -mavx5124vnniw                    [disabled]
  -mavx512bf16                      [disabled]
  -mavx512bitalg                    [disabled]
  -mavx512bw                        [disabled]
  -mavx512cd                        [disabled]
  -mavx512dq                        [disabled]
  -mavx512er                        [disabled]
  -mavx512f                         [disabled]
  -mavx512ifma                      [disabled]
  -mavx512pf                        [disabled]
  -mavx512vbmi                      [disabled]
  -mavx512vbmi2                     [disabled]
  -mavx512vl                        [disabled]
  -mavx512vnni                      [disabled]
  -mavx512vp2intersect              [disabled]
  -mavx512vpopcntdq                 [disabled]
  -mavxvnni                         [disabled]
  -mbionic                          [disabled]
  -mbmi                             [enabled]
  -mbmi2                            [enabled]
  -mbranch-cost=<0,5>               3
  -mcall-ms2sysv-xlogues            [disabled]
  -mcet-switch                      [disabled]
  -mcld                             [disabled]
  -mcldemote                        [disabled]
  -mclflushopt                      [enabled]
  -mclwb                            [disabled]
  -mclzero                          [enabled]
  -mcmodel=                         [default]
  -mcpu=                            
  -mcrc32                           [disabled]
  -mcx16                            [enabled]
  -mdispatch-scheduler              [disabled]
  -mdump-tune-features              [disabled]
  -menqcmd                          [disabled]
  -mf16c                            [enabled]
  -mfancy-math-387                  [enabled]
  -mfentry                          [disabled]
  -mfentry-name=                    
  -mfentry-section=                 
  -mfma                             [enabled]
  -mfma4                            [disabled]
  -mforce-drap                      [disabled]
  -mforce-indirect-call             [disabled]
  -mfp-ret-in-387                   [enabled]
  -mfpmath=                         sse
  -mfsgsbase                        [enabled]
  -mfunction-return=                keep
  -mfused-madd                      -ffp-contract=fast
  -mfxsr                            [enabled]
  -mgeneral-regs-only               [disabled]
  -mgfni                            [disabled]
  -mglibc                           [enabled]
  -mhard-float                      [enabled]
  -mhle                             [disabled]
  -mhreset                          [disabled]
  -miamcu                           [disabled]
  -mieee-fp                         [enabled]
  -mincoming-stack-boundary=        0
  -mindirect-branch-register        [disabled]
  -mindirect-branch=                keep
  -minline-all-stringops            [disabled]
  -minline-stringops-dynamically    [disabled]
  -minstrument-return=              none
  -mintel-syntax                    -masm=intel
  -mkl                              [disabled]
  -mlarge-data-threshold=<number>   65536
  -mlong-double-128                 [disabled]
  -mlong-double-64                  [disabled]
  -mlong-double-80                  [enabled]
  -mlwp                             [disabled]
  -mlzcnt                           [enabled]
  -mmanual-endbr                    [disabled]
  -mmemcpy-strategy=                
  -mmemset-strategy=                
  -mmitigate-rop                    [disabled]
  -mmmx                             [enabled]
  -mmovbe                           [enabled]
  -mmovdir64b                       [disabled]
  -mmovdiri                         [disabled]
  -mmpx                             [disabled]
  -mms-bitfields                    [disabled]
  -mmusl                            [disabled]
  -mmwaitx                          [enabled]
  -mneeded                          [disabled]
  -mno-align-stringops              [disabled]
  -mno-default                      [disabled]
  -mno-fancy-math-387               [disabled]
  -mno-push-args                    [disabled]
  -mno-red-zone                     [disabled]
  -mno-sse4                         [disabled]
  -mnop-mcount                      [disabled]
  -momit-leaf-frame-pointer         [disabled]
  -mpc32                            [disabled]
  -mpc64                            [disabled]
  -mpc80                            [disabled]
  -mpclmul                          [enabled]
  -mpcommit                         [disabled]
  -mpconfig                         [disabled]
  -mpku                             [disabled]
  -mpopcnt                          [enabled]
  -mprefer-avx128                   -mprefer-vector-width=128
  -mprefer-vector-width=            128
  -mpreferred-stack-boundary=       0
  -mprefetchwt1                     [disabled]
  -mprfchw                          [enabled]
  -mptwrite                         [disabled]
  -mpush-args                       [enabled]
  -mrdpid                           [disabled]
  -mrdrnd                           [enabled]
  -mrdseed                          [enabled]
  -mrecip                           [disabled]
  -mrecip=                          
  -mrecord-mcount                   [disabled]
  -mrecord-return                   [disabled]
  -mred-zone                        [enabled]
  -mregparm=                        6
  -mrtd                             [disabled]
  -mrtm                             [disabled]
  -msahf                            [enabled]
  -mserialize                       [disabled]
  -msgx                             [disabled]
  -msha                             [enabled]
  -mshstk                           [disabled]
  -mskip-rax-setup                  [disabled]
  -msoft-float                      [disabled]
  -msse                             [enabled]
  -msse2                            [enabled]
  -msse2avx                         [disabled]
  -msse3                            [enabled]
  -msse4                            [enabled]
  -msse4.1                          [enabled]
  -msse4.2                          [enabled]
  -msse4a                           [enabled]
  -msse5                            -mavx
  -msseregparm                      [disabled]
  -mssse3                           [enabled]
  -mstack-arg-probe                 [disabled]
  -mstack-protector-guard-offset=   
  -mstack-protector-guard-reg=      
  -mstack-protector-guard-symbol=   
  -mstack-protector-guard=          tls
  -mstackrealign                    [disabled]
  -mstringop-strategy=              [default]
  -mstv                             [enabled]
  -mtbm                             [disabled]
  -mtls-dialect=                    gnu
  -mtls-direct-seg-refs             [enabled]
  -mtsxldtrk                        [disabled]
  -mtune-ctrl=                      
  -mtune=                           znver1
  -muclibc                          [disabled]
  -muintr                           [disabled]
  -mvaes                            [disabled]
  -mveclibabi=                      [default]
  -mvect8-ret-in-mem                [disabled]
  -mvpclmulqdq                      [disabled]
  -mvzeroupper                      [enabled]
  -mwaitpkg                         [disabled]
  -mwbnoinvd                        [disabled]
  -mwidekl                          [disabled]
  -mx32                             [disabled]
  -mxop                             [disabled]
  -mxsave                           [enabled]
  -mxsavec                          [enabled]
  -mxsaveopt                        [enabled]
  -mxsaves                          [enabled]

  Known assembler dialects (for use with the -masm= option):
    att intel

  Known ABIs (for use with the -mabi= option):
    ms sysv

  Known code models (for use with the -mcmodel= option):
    32 kernel large medium small

  Valid arguments to -mfpmath=:
    387 387+sse 387,sse both sse sse+387 sse,387

  Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options):
    keep thunk thunk-extern thunk-inline

  Known choices for return instrumentation with -minstrument-return=:
    call none nop5

  Known data alignment choices (for use with the -malign-data= option):
    abi cacheline compat

  Known vectorization library ABIs (for use with the -mveclibabi= option):
    acml svml

  Known address mode (for use with the -maddress-mode= option):
    long short

  Known preferred register vector length (to use with the -mprefer-vector-width= option):
    128 256 512 none

  Known stack protector guard (for use with the -mstack-protector-guard= option):
    global tls

  Valid arguments to -mstringop-strategy=:
    byte_loop libcall loop rep_4byte rep_8byte rep_byte unrolled_loop vector_loop

  Known TLS dialects (for use with the -mtls-dialect= option):
    gnu gnu2

  Known valid arguments for -march= option:
    i386 i486 i586 pentium lakemont pentium-mmx winchip-c6 winchip2 c3 samuel-2 c3-2 nehemiah c7 esther i686 pentiumpro pentium2 pentium3 pentium3m pentium-m pentium4 pentium4m prescott nocona core2 nehalem corei7 westmere sandybridge corei7-avx ivybridge core-avx-i haswell core-avx2 broadwell skylake skylake-avx512 cannonlake icelake-client rocketlake icelake-server cascadelake tigerlake cooperlake sapphirerapids alderlake bonnell atom silvermont slm goldmont goldmont-plus tremont knl knm intel geode k6 k6-2 k6-3 athlon athlon-tbird athlon-4 athlon-xp athlon-mp x86-64 x86-64-v2 x86-64-v3 x86-64-v4 eden-x2 nano nano-1000 nano-2000 nano-3000 nano-x2 eden-x4 nano-x4 k8 k8-sse3 opteron opteron-sse3 athlon64 athlon64-sse3 athlon-fx amdfam10 barcelona bdver1 bdver2 bdver3 bdver4 znver1 znver2 znver3 btver1 btver2 generic native

  Known valid arguments for -mtune= option:
    generic i386 i486 pentium lakemont pentiumpro pentium4 nocona core2 nehalem sandybridge haswell bonnell silvermont goldmont goldmont-plus tremont knl knm skylake skylake-avx512 cannonlake icelake-client icelake-server cascadelake tigerlake cooperlake sapphirerapids alderlake rocketlake intel geode k6 athlon k8 amdfam10 bdver1 bdver2 bdver3 bdver4 btver1 btver2 znver1 znver2 znver3

我还try 了clang,在这两种情况下,循环都是通过32字节向量进行矢量化的:

我使用的是gcc 11.2.0

编辑:

生成文件:

all:
    gcc -O3 -mavx2 main.c -o 3
    gcc -O3 -march=znver2 main.c -o 32  
    gcc -O3 -march=znver2 main.c -mprefer-vector-width=128 -o 32128
    gcc -O3 -march=znver1 main.c -o 31
    gcc -O2 -mavx2 main.c -o 2
    gcc -O2 -march=znver2 main.c -o 22
    gcc -O2 -march=znver2 main.c -mprefer-vector-width=128 -o 22128
    gcc -O2 -march=znver1 main.c -o 21
    hyperfine -r5 ./3 ./32 ./32128 ./31 ./2 ./22 ./22128 ./21

clean:
    rm ./3 ./32 ./32128 ./31 ./2 ./22 ./22128 ./21

代码:

#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
#include <time.h>

int main() {
    const size_t N = 500;
    uint64_t arr[N];
    for (size_t i = 0; i < N; ++i)
        arr[i] = 1;

    for (int j = 0; j < 20000000; ++j)
        for (size_t i = 0; i < N; ++i)
            arr[i] *= 4;

    srand(time(0));
    printf("%lu\n", arr[rand() % N]); // use the array so that it is not optimized away
}

N = 500, arr[i] *= 4:

Benchmark 1: ./3
  Time (mean ± σ):      1.780 s ±  0.011 s    [User: 1.778 s, System: 0.000 s]
  Range (min … max):    1.763 s …  1.791 s    5 runs

Benchmark 2: ./32
  Time (mean ± σ):      1.785 s ±  0.016 s    [User: 1.783 s, System: 0.000 s]
  Range (min … max):    1.773 s …  1.810 s    5 runs

Benchmark 3: ./32128
  Time (mean ± σ):      1.740 s ±  0.026 s    [User: 1.737 s, System: 0.000 s]
  Range (min … max):    1.724 s …  1.785 s    5 runs

Benchmark 4: ./31
  Time (mean ± σ):      1.757 s ±  0.022 s    [User: 1.754 s, System: 0.000 s]
  Range (min … max):    1.727 s …  1.785 s    5 runs

Benchmark 5: ./2
  Time (mean ± σ):      3.467 s ±  0.031 s    [User: 3.462 s, System: 0.000 s]
  Range (min … max):    3.443 s …  3.519 s    5 runs

Benchmark 6: ./22
  Time (mean ± σ):      3.475 s ±  0.028 s    [User: 3.469 s, System: 0.001 s]
  Range (min … max):    3.447 s …  3.512 s    5 runs

Benchmark 7: ./22128
  Time (mean ± σ):      3.464 s ±  0.034 s    [User: 3.459 s, System: 0.001 s]
  Range (min … max):    3.431 s …  3.509 s    5 runs

Benchmark 8: ./21
  Time (mean ± σ):      3.465 s ±  0.013 s    [User: 3.460 s, System: 0.001 s]
  Range (min … max):    3.443 s …  3.475 s    5 runs

N = 500, arr[i] *= 5:

Benchmark 1: ./3
  Time (mean ± σ):      1.789 s ±  0.004 s    [User: 1.786 s, System: 0.001 s]
  Range (min … max):    1.783 s …  1.793 s    5 runs

Benchmark 2: ./32
  Time (mean ± σ):      1.772 s ±  0.017 s    [User: 1.769 s, System: 0.000 s]
  Range (min … max):    1.755 s …  1.800 s    5 runs

Benchmark 3: ./32128
  Time (mean ± σ):      2.911 s ±  0.023 s    [User: 2.907 s, System: 0.001 s]
  Range (min … max):    2.880 s …  2.943 s    5 runs

Benchmark 4: ./31
  Time (mean ± σ):      2.924 s ±  0.013 s    [User: 2.921 s, System: 0.000 s]
  Range (min … max):    2.906 s …  2.934 s    5 runs

Benchmark 5: ./2
  Time (mean ± σ):      3.850 s ±  0.029 s    [User: 3.846 s, System: 0.000 s]
  Range (min … max):    3.823 s …  3.896 s    5 runs

Benchmark 6: ./22
  Time (mean ± σ):      3.816 s ±  0.036 s    [User: 3.812 s, System: 0.000 s]
  Range (min … max):    3.777 s …  3.855 s    5 runs

Benchmark 7: ./22128
  Time (mean ± σ):      3.813 s ±  0.026 s    [User: 3.809 s, System: 0.000 s]
  Range (min … max):    3.780 s …  3.834 s    5 runs

Benchmark 8: ./21
  Time (mean ± σ):      3.783 s ±  0.010 s    [User: 3.779 s, System: 0.000 s]
  Range (min … max):    3.773 s …  3.798 s    5 runs

N = 512, arr[i] *= 4

Benchmark 1: ./3
  Time (mean ± σ):      1.849 s ±  0.015 s    [User: 1.847 s, System: 0.000 s]
  Range (min … max):    1.831 s …  1.873 s    5 runs

Benchmark 2: ./32
  Time (mean ± σ):      1.846 s ±  0.013 s    [User: 1.844 s, System: 0.001 s]
  Range (min … max):    1.832 s …  1.860 s    5 runs

Benchmark 3: ./32128
  Time (mean ± σ):      1.756 s ±  0.012 s    [User: 1.754 s, System: 0.000 s]
  Range (min … max):    1.744 s …  1.771 s    5 runs

Benchmark 4: ./31
  Time (mean ± σ):      1.788 s ±  0.012 s    [User: 1.785 s, System: 0.001 s]
  Range (min … max):    1.774 s …  1.801 s    5 runs

Benchmark 5: ./2
  Time (mean ± σ):      3.476 s ±  0.015 s    [User: 3.472 s, System: 0.001 s]
  Range (min … max):    3.458 s …  3.494 s    5 runs

Benchmark 6: ./22
  Time (mean ± σ):      3.449 s ±  0.002 s    [User: 3.446 s, System: 0.000 s]
  Range (min … max):    3.446 s …  3.452 s    5 runs

Benchmark 7: ./22128
  Time (mean ± σ):      3.456 s ±  0.007 s    [User: 3.453 s, System: 0.000 s]
  Range (min … max):    3.446 s …  3.462 s    5 runs

Benchmark 8: ./21
  Time (mean ± σ):      3.547 s ±  0.044 s    [User: 3.542 s, System: 0.001 s]
  Range (min … max):    3.482 s …  3.600 s    5 runs

N = 512, arr[i] *= 5

Benchmark 1: ./3
  Time (mean ± σ):      1.847 s ±  0.013 s    [User: 1.845 s, System: 0.000 s]
  Range (min … max):    1.836 s …  1.863 s    5 runs

Benchmark 2: ./32
  Time (mean ± σ):      1.830 s ±  0.007 s    [User: 1.827 s, System: 0.001 s]
  Range (min … max):    1.820 s …  1.837 s    5 runs

Benchmark 3: ./32128
  Time (mean ± σ):      2.983 s ±  0.017 s    [User: 2.980 s, System: 0.000 s]
  Range (min … max):    2.966 s …  3.012 s    5 runs

Benchmark 4: ./31
  Time (mean ± σ):      3.026 s ±  0.039 s    [User: 3.021 s, System: 0.001 s]
  Range (min … max):    2.989 s …  3.089 s    5 runs

Benchmark 5: ./2
  Time (mean ± σ):      4.000 s ±  0.021 s    [User: 3.994 s, System: 0.001 s]
  Range (min … max):    3.982 s …  4.035 s    5 runs

Benchmark 6: ./22
  Time (mean ± σ):      3.940 s ±  0.041 s    [User: 3.934 s, System: 0.001 s]
  Range (min … max):    3.890 s …  3.981 s    5 runs

Benchmark 7: ./22128
  Time (mean ± σ):      3.928 s ±  0.032 s    [User: 3.922 s, System: 0.001 s]
  Range (min … max):    3.898 s …  3.979 s    5 runs

Benchmark 8: ./21
  Time (mean ± σ):      3.908 s ±  0.029 s    [User: 3.904 s, System: 0.000 s]
  Range (min … max):    3.879 s …  3.954 s    5 runs

我认为-O2 -march=znver1-O3 -march=znver1速度相同的运行是我在文件命名方面的一个错误,当时我还没有创建makefile,我使用的是shell的历史记录.

推荐答案

默认值-mtune=generic-mprefer-vector-width=256,而-mavx2不会改变这一点.

znver1表示-mprefer-vector-width=128,因为这是HW的所有本机宽度.使用32字节YMM向量的指令至少会解码为2个UOP,如果是车道交叉洗牌,则会解码到更多UOP.对于这样简单的垂直SIMD,32字节向量就可以了;管道有效地处理2-uop指令.(我认为它有6个uop宽,但只有5条指令宽,因此仅使用1个uop指令无法获得最大前端吞吐量).但当矢量化需要洗牌时,例如使用不同元素宽度的数组时,GCC code gen可能会变得更混乱,256位或更宽.

vmovdqa ymm0, ymm1 mov消除只适用于Zen1上的低128位的一半.此外,通常使用256位向量意味着之后应该使用vzeroupper,以避免其他CPU(但不是Zen1)上的性能问题.

我不知道Zen1如何处理未对齐的32字节加载/存储,其中每个16字节的一半是对齐的,但在单独的缓存线中.如果运行良好,GCC可能会考虑将ZNVR1 -mprefer-vector-width增加到256.但如果不知道向量的大小是向量宽度的倍数,那么更宽的向量意味着更多的清理代码.

理想情况下,GCC能够检测easy个这样的情况,并在那里使用256位向量.(纯垂直,不混合元素宽度,恒定大小为32字节的倍数.)至少在CPU上是这样的:znver1,但不是bdver2,例如,由于CPU设计缺陷,256位存储总是很慢.

你可以看到这个 Select 的结果,它将你的第一个循环,类似于memset的循环,矢量化为vmovdqu [rdx], xmm0.https://godbolt.org/z/E5Tq7Gfzc


因此,考虑到GCC决定只使用128位向量,这只能容纳两个uint64_t元素,它(正确或错误地)决定不值得使用vpsllq/vpaddd将qword *5实现为(v<<2) + v,而不是在一条LEA指令中使用整数.

在这种情况下几乎肯定是错误的,因为它仍然需要 for each 元素或元素对单独加载和存储.(还有循环开销,因为GCC的默认设置是,除PGO -fprofile-use外,不展开循环.SIMD类似于循环展开,尤其是在将256位向量作为两个独立UOP处理的CPU上.)

我不确定GCC所说的"未矢量化:不支持的数据类型"到底是什么意思.直到AVX-512,x86才有SIMD uint64_t乘法指令,所以GCC可能会根据必须用多个32x32=>;64位pmuludq指令和一系列洗牌.只有当它克服了这个困难,它才意识到,对于一个只有两个设定位的常数,比如5,它实际上是相当便宜的?

这可以解释GCC在这里的决策过程,但我不确定这是正确的解释.尽管如此,这些因素还是发生在复杂的机器中,比如编译器.熟练的人可以很容易地做出更明智的 Select ,但是编译器只会做一系列优化过程,它们不总是同时考虑全局和细节.


-mprefer-vector-width=256 doesn't help:

Not vectorizing uint64_t *= 5 seems to be a GCC9 regression

(问题中的基准测试证实,一个实际的Zen1 CPU获得了接近2倍的加速比,与预期的一样,在6个uops中执行2倍的uint64,而在5个uops中执行1倍的scalar.或者在10个uops中执行4倍的uint64_t,使用256位向量,包括两个128位存储,这将与前端一起成为吞吐量瓶颈.)

即使使用-march=znver1 -O3 -mprefer-vector-width=256,我们也无法使用GCC9、10或11或当前主干对*= 5循环进行矢量化.正如你所说,我们用-march=znver2.https://godbolt.org/z/dMTh7Wxcq

We do get vectorization with those options for 100(即使向量宽度保持在128位).不管Zen1上的128位或256位矢量化,Scalar每个向量uop(不是指令)将花费4个操作,所以这并不能告诉我们*=是成本模型决定不矢量化的原因,还是每128位内部uop只需要2对4个元素.


对于uint64_t,更改为arr[i] += arr[i]<<2;仍然不会矢量化,但arr[i] <<= 1;会矢量化.(https://godbolt.org/z/6PMn93Y5G). 即使是同一个循环中的arr[i] <<= 2;arr[i] += 123,也要对GCC认为不值得对*= 5进行向量化的相同指令进行向量化,只是不同的操作数,常量而不是原始向量.(Scalar仍然可以使用一个LEA).所以很明显,成本模型看起来不像最终的x86 asm机器指令,但我不知道为什么arr[i] += arr[i]会被认为比arr[i] <<= 1;更贵,因为arr[i] <<= 1;是完全相同的东西.

GCC8 does vectorize your loop, even with 128-bit vector width: https://godbolt.org/z/5o6qjc7f6

# GCC8.5 -march=znver1 -O3  (-mprefer-vector-width=128)
.L12:                                            # do{
        vmovups xmm1, XMMWORD PTR [rsi]            # 16-byte load
        add     rsi, 16                             # ptr += 2 elements
        vpsllq  xmm0, xmm1, 2                      # v << 2
        vpaddq  xmm0, xmm0, xmm1                   # tmp += v
        vmovups XMMWORD PTR [rsi-16], xmm0         # store
        cmp     rax, rsi
        jne     .L12                             # } while(p != endp)

With -march=znver1 -mprefer-vector-width=256, doing the store as two 16-byte halves with vmovups xmm / vextracti128 is Why doesn't gcc resolve _mm256_loadu_pd as single vmovupd? znver1 implies -mavx256-split-unaligned-store (which affects every store when GCC doesn't know for sure that it is aligned. So it costs extra instructions even when data does happen to be aligned).

然而,znver1并不意味着-mavx256-split-unaligned-load,所以GCC愿意在代码中将负载作为内存源操作数折叠成ALU操作,这是有用的.

C++相关问答推荐

字符串令牌化xpath表达式

C中的__attributor__((aligned(4),packed))与 struct 的用法

C语言中的strstr问题

模拟shell并运行.sh文件

为什么net/if.h在ifaddrs.h之前?

在我的代码中,我需要在哪里编写输出函数?

如何在ASM中访问C struct 成员

Sizeof(&Q;字符串&Q;)的正确输出是什么?

从uint8_t*转换为char*可接受

为什么我的Hello World EFI程序构建不正确?

这个计算C中阶乘的函数正确吗?

从C文件中删除注释

按长度对argv中的单词进行排序

在函数外部使用内联ASM时无法指定操作数

c程序,让用户输入两类数字,并给出输出用户输入多少个数字

free后内存泄漏?

变量的指针右对齐,函数的指针左对齐

使用fread()函数读取txt文件

全局变量 y0 与 mathlib 冲突,无法编译最小的 C 代码

为什么变量突然变成0了?(c语言)