循环展开没有给出浮点点积的预期加速比速比、浮点

2023-09-04 22:44:39 作者:我是天蝎座我骄傲!

 /* Inner product. Accumulate in temporary */
  void inner4(vec_ptr u, vec_ptr v, data_t *dest)
{
     long i;
     long length = vec_length(u);
     data_t *udata = get_vec_start(u);
     data_t *vdata = get_vec_start(v);
     data_t sum = (data_t) 0;

        for (i = 0; i < length; i++) {
                 sum = sum + udata[i] * vdata[i];
       }
  *dest = sum;
 }
编写上述问题中描述的内积过程的一个版本 使用6x1a循环展开。对于x86-64,我们对展开版本的测量 对于整数数据,CPE为1.07,但对于两个浮点数据,CPE仍为3.01。

我的6*1a版本循环展开代码

 void inner4(vec_ptr u, vec_ptr v, data_t *dest){
       long i;
       long length = vec_length(u);
       data_t *udata = get_vec_start(u);
       data_t *vdata = get_vec_start(v);
       long limit = length -5;
       data_t sum = (data_t) 0;

      for(i=0; i<limit; i+=6){
             sum = sum +
                   ((udata[ i ] * vdata[ i ]
                  + udata[ i+1 ] * vdata[ i+1 ])
                  + (udata[ i+2 ] * vdata[ i+2 ]
                  + udata[ i+3 ] * vdata[ i+3 ]))
                   + ((udata[ i+4 ] * vdata[ i+4 ])
                  + udata[ i+5 ] * vdata[ i+5 ]);
      }
     for (i = 0; i < length; i++) {
             sum = sum + udata[i] * vdata[i];
   }
  *dest = sum;
      
 }

问题:解释为什么在英特尔酷睿i7 Haswell处理器上运行的任何(标量)版本的内积程序都无法实现低于1.00的CPE。

最强旗舰手机功能曝光 大变活人 智能静音 拍照测肤

您知道如何解决此问题吗?

推荐答案

您的展开无助于解决FP延迟瓶颈:

sum + x + y + z没有-ffast-mathsum += x;的操作顺序相同...因此,您还没有对通过所有+操作运行的单个依赖关系链做任何操作。循环开销(或前端吞吐量)不是的瓶颈,而是Haswell上addss的3个周期延迟,所以这个展开基本上没有区别。

有效的方法是sum += u[i]*v[i] + u[i+1]*v[i+1] + ...作为一种无需多个累加器的展开方式,因为这样每组元素的总和是独立的。

以这种方式进行的数学运算会稍微多一些,比如以mul开头,以Add结尾,但如果您用-march=haswell编译,中间的运算仍然可以收缩为FMA。GCC将sum += u0*v0;sum += u1*v1天真的展开变成sum += u0*v0 + u1*v1;的例子,见AVX performance slower for bitwise xor op and popcount的评论。在这种情况下,问题略有不同:sum += (u0-v0)**2 + (u1-v1)**2;的平方差之和,但归结为最终进行一些乘法和加法的相同延迟问题。

另一种解决问题的方法是使用多个累加器,允许所有操作都是FMA。但Haswell有5个周期的延迟FMA和3个周期的延迟addss,因此单独进行sum += ...添加,而不是作为FMA的一部分,实际上有助于解决Haswell的延迟瓶颈(与Skylake Add/Sub/MUL都是4个周期延迟不同)。以下所有内容都显示了使用多个累加器展开,而不是像您正在做的那样,使用第一个两两求和的方法将组加在一起:

Why does mulss take only 3 cycles on Haswell, different from Agner's instruction tables? (Unrolling FP loops with multiple accumulators) When, if ever, is loop unrolling still useful? Loop unrolling to achieve maximum throughput with Ivy Bridge and Haswell

FP数学指令吞吐量不是现代CPU上大点产品的瓶颈,而是延迟。或加载吞吐量(如果展开足够多)。

解释为什么在英特尔酷睿i7 Haswell处理器上运行的任何(标量)版本的内积程序都无法实现低于1.00的CPE。

每个元素需要2个负载,并且只有2个负载端口,这是一个硬的吞吐量瓶颈。(https://agner.org/optimize//https://www.realworldtech.com/haswell-cpu/5/)

我假设您将一个";元素";计算为一个i值,这是一对浮点数,分别来自udata[i]vdata[i]。在Haswell上,FP FMA吞吐量瓶颈也是2/时钟(无论它们是标量、128位还是256位向量),但是点积在每个FMA上需要2个负载。从理论上讲,即使是沙桥,甚至K8也可以实现每个时钟1个元素,使用单独的mul和加法指令,因为它们都支持每个时钟2个加载,并且具有足够宽的流水线,以便通过流水线获得加载/muss/adds,并留出一些空闲空间。