为什么此功能在 Intel i3-N305 上的性能比 AMD Ryzen 7 3800X 慢?

问题描述 投票:0回答:1

我在 Intel i3-N305 3.8GHz 和 AMD Ryzen 7 3800X 3.9GHz PC 上运行了使用 gcc-13 (https://godbolt.org/z/qq5WrE8qx) 编译的相同二进制文件。此代码使用VCL库(https://github.com/vectorclass/version2):

int loop_vc_nested(const array<uint8_t, H*W> &img, const array<Vec32uc, 8> &idx) {
  int sum = 0;
  Vec32uc vMax, iMax, vCurr, iCurr;

  for (int i=0; i<H*W; i+=W) {
    iMax.load(&idx[0]);
    vMax.load(&img[i]);

    for (int j=1; j<8; j++) {
      iCurr.load(&idx[j]);
      vCurr.load(&img[i+j*32]);
      iMax = select(vCurr > vMax, iCurr, iMax);
      vMax = max(vMax, vCurr);
    }

    Vec32uc vMaxAll{horizontal_max(vMax)};
    sum += iMax[horizontal_find_first(vMax == vMaxAll)];
  }

  return sum;
}

完整的基准源在这里:https://github.com/pauljurczak/simd-benchmarks/blob/main/main-5-vcl-eve.cpp。时间安排如下:

Ubuntu 22.04.3 LTS on AMD Ryzen 7 3800X 8-Core Processor
gcc    v13.1   __cplusplus=202100
loop_vc_nested(): 3.597  3.777 [us]  108834

Ubuntu 23.10 on Intel(R) Core(TM) i3-N305
gcc    v13.1   __cplusplus=202100
loop_vc_nested(): 11.804  11.922 [us]  108834

出现了 3.2 倍的意外减速。 AFAIK,这些 CPU 对于单线程程序具有类似的 SIMD 功能。 7-zip 基准测试的性能非常接近。为什么差距这么大?


这是

perf
的输出。 AMD 锐龙 7 3800X:

          3,841.61 msec task-clock                       #    1.000 CPUs utilized             
                20      context-switches                 #    5.206 /sec                      
                 0      cpu-migrations                   #    0.000 /sec                      
             2,191      page-faults                      #  570.333 /sec                      
    14,909,837,582      cycles                           #    3.881 GHz                         (83.34%)
         3,509,824      stalled-cycles-frontend          #    0.02% frontend cycles idle        (83.34%)
     9,865,497,290      stalled-cycles-backend           #   66.17% backend cycles idle         (83.34%)
    42,856,816,868      instructions                     #    2.87  insn per cycle            
                                                  #    0.23  stalled cycles per insn     (83.34%)
     1,718,672,677      branches                         #  447.383 M/sec                       (83.34%)
         2,409,251      branch-misses                    #    0.14% of all branches             (83.29%)

英特尔 i3-N305:

         12,015.18 msec task-clock                       #    1.000 CPUs utilized             
                57      context-switches                 #    4.744 /sec                      
                 0      cpu-migrations                   #    0.000 /sec                      
             2,196      page-faults                      #  182.769 /sec                      
    45,432,594,158      cycles                           #    3.781 GHz                         (74.97%)
    42,847,054,707      instructions                     #    0.94  insn per cycle              (87.48%)
     1,714,003,765      branches                         #  142.653 M/sec                       (87.48%)
         4,254,872      branch-misses                    #    0.25% of all branches             (87.51%)
                        TopdownL1                 #      0.2 %  tma_bad_speculation    
                                                  #     45.5 %  tma_retiring             (87.52%)
                                                  #     53.8 %  tma_backend_bound      
                                                  #     53.8 %  tma_backend_bound_aux  
                                                  #      0.5 %  tma_frontend_bound       (87.52%)

编译器选项:

-O3 -Wno-narrowing -ffast-math -fno-trapping-math -fno-math-errno -ffinite-math-only -march=alderlake


来自 i3-N305 上

perf stat -d
的其他缓存使用信息:

    15,615,324,576      L1-dcache-loads                  #    1.294 G/sec                       (54.50%)
   <not supported>      L1-dcache-load-misses                                                 
            60,909      LLC-loads                        #    5.048 K/sec                       (54.50%)
             5,231      LLC-load-misses                  #    8.59% of all L1-icache accesses   (54.50%)
c++ benchmarking simd avx2 vector-class-library
1个回答
0
投票

总结评论:你的i3-N305是Alder Lake-N系列。与型号中带有 N 的早期 Celeron/Pentium CPU 一样,这些内核都是低功耗 Silvermont 系列。在本例中,Gracemont 是来自成熟的 Alder Lake 芯片的 E 核心。 (它比 Tremont 或尤其是像 Goldmont Plus 这样的前代产品要强大得多。)而且它有 AVX2+FMA,我想这就是它作为 i3 出售的理由。 https://chipsandcheese.com/2021/12/21/gracemont-revenge-of-the-atom-cores/ 是对 CPU 微架构的非常好的深入探讨,其中包含一些带宽和延迟的微基准(作为一部分) i9-12900k,IDK,如果 i3-N 系列中的互连或 L3 有所不同。)

您的 Zen 是 Zen 2,具有 256 位 ALU。 Gracemont 上的chipsandcheese 深入研究中的许多基准测试都将其与 Zen 2 进行了比较。

最重要的因素是:

  • 矢量 ALU 和内存端口为 128 位宽,每条 256 位指令解码为 2 uops。 (如 Zen 1 和 Bulldozer 系列)。因此,当运行主要是带有一点标量开销的向量指令的代码时,每个时钟的 uops 大约是 IPC 的两倍。当每个负载仅为 128 位时,2/时钟负载带宽仅达到一半。

  • select
    应该编译为
    vpblendvb
    。不幸的是,Gracemont 上的速度非常慢,请参阅https://uops.info/ - YMM 版本为 8 uops,测得的吞吐量为每 3.86 个周期 1 个。 (或者令人惊讶的是,内存源需要 3.2 个周期而不是寄存器。)Zen 系列将 4 操作数
    vpblendvb
    作为单个 uop 运行(甚至可以选择端口)。

    8 uops 本质上会导致相同 uops/时钟吞吐量的 IPC 较低,但它也可能会导致前端停顿并减少 uops/时钟。


您可以尝试与

vpand
/
vpandn
/
vpor
手动混合,但 clang 几乎肯定会将这些内在函数“优化”为
vpblendvb
,除非 clang trunk 的
-mtune=gracemont
-march=gracemont
知道这速度很慢那个大主教。 MSVC,或者经典的 ICC,对于内在函数来说更加字面化。 GCC 确实优化了一些,我还没有检查它在这里做了什么。


您的实际问题可以通过其他方式完成,例如使用索引解包数据,如 寻找有效的函数以使用库在 SIMD 向量中查找最大元素的索引,因此 max

u16
元素包含数据和索引。 (索引可以来自
idx = _mm256_add_epi8(idx, _mm256_set1_epi8(32));
,而不是加载。 也许超过 256 个字节的内部循环可以完全展开,因此您有 8 个寄存器保存索引数据。)

因为无论如何你可能都想使用改进的缩减,所以更早解包可以节省一些清理工作,并且你的循环只有 8 个向量。

对于索引的总和,我想获得匹配项的第一次出现很重要?因此,您需要反转索引,以便在相等数据的平局时,打包为 u16 的 data:index 的最大值会选择较早的索引。无论如何,这就是我们想要的清理工作,将使用

vphminposuw


或者考虑分支策略,比如找到最大值,然后在数组中搜索第一个匹配项。 (比较/移动掩码又名

to_bits(curr == bcast_max)
,如果非零,则返回
tzcnt(mask)
)。您永远不需要加载索引数据向量,并且早期匹配可以减少工作量。 (但它可能会错误地预测哪个可能更糟糕;仍然值得一试。但是,对依赖于正确分支预测的有用的微基准测试非常困难 - 微基准可以学习一种模式。或者,如果你让它完全随机,它的预测会比真实数据更糟糕分布。)

尽管如此,只需 8 个数据向量,第二遍循环就可以在没有负载的情况下完全展开。第一遍可以将数据留在寄存器中。 (但它也必须完全展开,因此代码大小很大。) Gracemont 没有 uop 缓存;显然它的解码器的吞吐量管理得很好。

© www.soinside.com 2019 - 2024. All rights reserved.