This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TMS320C6678:使用 DSPLIB 进行矩阵乘法

Guru**** 2614265 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/processors-group/processors/f/processors-forum/581907/tms320c6678-matrix-multiply-using-dsplib

器件型号:TMS320C6678

您好!

我正在尝试使用 spmatrix 乘法例程将矩阵乘以2k x 2k 的顺序。

两个矩阵都位于 DDR 中、L1和 L2都已完全缓存。

单核性能非常慢、无法满足我的实时要求。

我想使用 openmp 将代码并行至多核。

我知道单核性能下降是由于未对齐读取、这是矩阵乘法的固有问题。

是否有方法可以实现更好的性能。 我将查看的性能接近为 DSPLib 矩阵乘法例程提供的基准标记。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!

    我邀请了 DSP 专家。 他们的反馈将直接发布在此处。

    此致、
    Yordan
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Prathek

    这是一个非常有趣的问题。  很明显、16M 字节的数据不能放入 L2中、DDR 存在非连续数据的问题、因此我建议如下:

    1.使用 EDMA 将数据加载到 L2中。 使用 B 索引从 DDR 连续读取、并在 L2中使用 STRIDE 进行写入。

    2.将矩阵分为多个部分,每个部分都应安装在 L1D 内。 L1D 为32KB 或8K sp 值、因此将矩阵分解为64 x 64 (或类似分区)的一部分。  将数据读写到 L2、并使用 EDMA 将数据从 L2移入 DDR 并移回

    我建议首先尝试对 L2存储器中的128 x 128矩阵执行此操作、如果这对您有效、请转到 EDMA 方案

    过去、我开发了用于对大型矩阵进行 Cholesky 分解 的代码(我测试了128x128)、在我的情况下、数据位于 L2中、但分区是为了适应 L1D 高速缓存并存储中间结果而完成的。  我使用大小至少为10x10的进程的函数、我展示了如何使用此帖子中包含调试代码的源代码完成该操作。 实际代码显然没有所有消耗如此多周期的 printf、并且不需要某些操作(这些操作仅用于调试目的)。 此外、我附加了一个带有已测量基准的文档(同样、对于 Cholesky)。 请注意、大小必须至少为10 (我有用于较小矩阵的类似函数)

    请注意、这是我的个人职能部门、而不是 TI、因此不存在明确或隐含的保证、即它能够正常工作、提供正确的结果或提供任何支持

    /cfs-file/__key/communityserver-discussions-components-files/791/0363.Test-Bench-Report-of-the-Single-Precision-Floating-point-Implementation-of-Cholesky-Decomposition-on-Key.doc



    void kincholeskyLarger_10 (int N、float * in、float * out、float * OneOverDiag)



       float x0、x1、 xx0、xx1 ;
       int k,l,m   ;

       float  *p1,*p2;   

       float  y0、y1;
       float *p_in  、*p_in1、 *p_out1、 *p_out;
       内扩散  ;
       双 A01 、B01、C01、D01、E01、 F01、 G01、h01;
       float  a0 ;

    //////   在步骤6中、我将尝试对指针进行条纹线
    ////   输入指针可以是 restrict
    ////   输出指针不能为、因为它在写入后被重新读取
    ////   我们可以分离


       如果 (N < 10)
       {
          printf(" N 必须至少为10\n")  ;
          返回 ;
       }

     ////////////  处理第一行/列

         P_IN = IN  ;
         P_OUT = OUT  ;
         printf ("p_in %x p_out %x \n"、p_in、p_out)  ;
         
          A01 =   _amemd8 (IN);//  
          A0 =_lof (A01)  ; //用于调试        
         IN = IN + 2 ;
         y0 =_rsqrsp (a0)  ; //第一个 NR 猜测、
         Y1 = y0 *(3- a0 * y0 * y0)* 0.5  ; // 第一次迭代,16位准确度
         x0 = Y1 *(3- a0 * Y1 *)* 0.5  ;   //  第二次迭代

      
         printf (" a0和 x0 %f %f \n"、a0、x0) ;
         b01 =_ftod (x0、x0) ;
         
        *OUT+=a0 * x0  ;
        *OUT+=0.0  ;
         
         printf("第一 个输出 %f\n", a0 * x0 );
         
         *OneOverDiag ++= x0  ;     
         _nassert( N >=10 ); /* 最小的矩阵为10x10           */
         
         扩散= 4;
         
    ///  下一个块计算第一列中的所有数字      
         
         对于(k=1;k< N;k++)
        {
          A01 =   _amemd8 (in);
          printf(" A01  %f %f\n"),_lof (A01),_hif (A01)) ;
          
          C01 =_dmpysp (A01、B01) ;
          _amemd8 (out)   = C01   ;
          IN = IN +扩散;   
          OUT = OUT +扩散;
          扩散=扩散+ 2 ;
           printf ("p_in %x p_out %x \n"、in、out)  ;
           printf ("out %f %f \n"、_lof (C01)、_hif (C01));      
       }





    ////////////////  第二行/列的处理
       
    ///将指针移回和移出

        IN = p_in + 4  ;//第二行对角线处的点
        OUT = p_OUT + 2;
       
       
       A01 =_amemd8 (out);
       OUT = OUT + 2 ;
       b01 =_dmpysp (A01、A01);
       XX0 =  _HIF (B01)+_lof (B01);   
       
       printf("xx0 %f p_in %x \n",xx0,p_in ) ;
       
       xx1 =   (浮点)*in; // 仅读取实值
       x0=(xx1-xx0)      ;
       printf (“xx1 %f x0 %f \n”、xx1、x0) ;   
     
         
         y0 =_rsqrsp (x0)  ; //第一个 NR 猜测、
         Y1 = y0 *(3.0 - x0 * y0 * y0)* 0.5  ; // 第一次迭代,16位准确度
         X1 = Y1 *(3.0 - x0 * Y1 *)* 0.5  ;   //  第二次迭代
     
         D01 =_ftod (x1、x1) ;
         *OneOverDiag ++= x1 ;
         
     
          *OUT++=x0 * x1 ;
          printf("out %f \n"x0*x1) ;
          *OUT+=0.0   ; // 保存对角线

    //    此时,输出指向第三行的第一个元素(位置6)和
    //    以行2对角线的实部为单位(位置4)
     ///////////////////////////////////
    ///  下一个块计算第二列中的所有数字
    //A01  具有第二行第一个元素的值     
          扩散= 4  ;
          IN = IN + 4  ;
          对于(l = 2;l < N;l++)
         {

              printf (“In %x diffP %d”,In,diffP) ;

              h01 =   _amemd8 (out); //读取输出输入
              OUT = OUT + 2 ;        // 指向行的第二个元素
              printf("h01 %f %f\n",_lof (h01),_hif (h01));
               
              E01 =_complex_conjugate_mpysp (h01、A01)  ;
            
              printf (" E01 %f %f \n",_lof (E01),_HIF (E01));
     
              F01 =_amemd8 (in)  ; // 读取输入
              G01 =_dsubsp (F01、E01) ;
              
              printf (" F01 %f %f \n",_lof (F01),_HIF (F01));
              printf (" G01 %f %f \n",_lof (G01),_hif (G01));
     
             C01 =_dmpysp (G01、D01) ;
             
              printf (" C01 %f %f \n",_lof (C01),_HIF (C01));           
              _amemd8 (out)   = C01   ;
             printf (“difP =%d \n”,扩散 P) ;
             OUT = OUT +扩散;   
             扩散=扩散+2  ;
             IN = IN +扩散;                        
         }
     
     
     //////    第 k 行的通用循环开始
     //// 从第2行开始,指针 P1从 out+6开始,它将开始
     ///递增  6、8、10等
     ////  因此、扩散 P1为6、P1 = OUT+ 6、并且在每个环路 P1 = P1 +扩散 P1之后
     ///   和扩散 P1 =扩散 P1 + 2
     ////  p_in 点在 in + 10、然后是18、28、40
     ////  因此 p_in 比 p1递增2

     
          in =p_in ;
          out = p_out;
     
         P1 =输出+ 6  ;
         P_IN = IN + 10 ;

         扩散= 6  ;    
        printf (" in %x out %x \n"、in、out) ;
       对于(k= 2、000、<N; k++)
        {

           P_IN = IN + k *(k+3)  ;
    ////   步骤1 -计算总和         
          printf(" P1 %x、p_in %x \n"、 p1、p_in ) ;
          P_OUT1 = P1 ;
          xx1 = 0.0  ;
         对于(L=0;<k; l++)
         {
               A01 =   _amemd8 (p_out1);
               printf("A01 %f %f\n",_lof (A01),_hif (A01));
               b01 =_dmpysp (A01、A01);            
               XX0 =  _HIF (B01)+_lof (B01);   
             P_OUT1 = p_OUT1 + 2    ;
             xx1 = xx1 + xx0 ;
             printf ("sumD %f \n"、xx1) ;
         }    
     //p_out1  指向行 k 中的最后一个元素(对角线)
     /// P1  在第一个优先级点  
     //p_in  指向输入中的对角线
     
    //     h01 =_amemd8 (p_in) ;
         XX0 =*p_in ;
         x0=(xx0-xx1)      ;
         printf (“xx1 %f xx0 %f x0 %f \n”、xx1、xx0、x0) ;    
         
         y0 =_rsqrsp (x0)  ; //第一个 NR 猜测、
         Y1 = y0 *(3.0 - x0 * y0 * y0)* 0.5  ; // 第一次迭代,16位准确度
         X1 = Y1 *(3.0 - x0 * Y1 *)* 0.5  ;   //  第二次迭代
      
    //   x1是1/sqrt   
      
         D01 =_ftod (x1、x1) ;
     
         *OneOverDiag ++= x1 ;
          printf ("x1 =%f \n"、x1) ;
          
     /// 现在我们有1/sqrt、我们可以从开始对第 k 行的所有释放进行筛选
     /// 对角线
     //p_out1 指向对角线(输出)-对于第2行、它是10;对于第3行、它是18
     /// 对于第2行、它以6、8等递增
     /// 对于第3行、它以8、10等递增
     /// P1 指向行 k 的第一个元素
     /// 请注意、瓶颈可能不是倍增器 、而是寄存器或负载、
     /// 、因此我们可以将寻址改回多路复用器
     
     
     
     
    //     m = k *(k+1)  ;
    //     n = k *(k+3) ; //对角线位置
    //     P1 =&out[m]  ;

          *p_out1++=x0 * x1;
          *p_out1++ =0.0 ;
          printf (“对角线输出%f \n",x0 * x1);
    ///   下一步是构建所有低于对角线的值
    ////  这些元素
    ////  对角线位于位置 k*(k+3)
    ////  下一个位于位置 k*(k+3)+ 2 *(k + 1)
    ////  下一个位于地址 k*(k+3)+ 2*(k+ 3)+ 2 *(k+1)
    ///  和一般情况。 在第 k+u 行中,新元素的地址为 k*(k+3)+和(2*r),其中 r 为 k+1 到 u

    //   现在到值的地址。  列 k 和行 l 中的值 (k < l)
    ///   具有以下公式 (输入矩阵、L 和 U 为输出)
    //   a (k+m、k)=和(u 从 o 到 k-1) L (k、u)* L (k+m、u)共轭+ D1 * L (k+m、k)
    ///   因此
    ///  L (k+m、k)=(A (k+m、k)-和)* x1

          如果 (k+1 == N) 返回;


    //   计算低于对角线的所有值(对于 L)      
         
          b01 =_ftod (0.0、0.0) ;
          对于(l = k+1;l < N;l++)
         {

            P1 = Out + k*(k+1)  ; //  保留寻址不变
            P2 =输出+ l *(l+1) ;
              p_in1 = in + k*2 +(l+1)* l ;
              P_OUT1 =输出+ k*2 +(l+1)* l ;
        

              对于(m=0;<k;m++)
              {
                printf("P1 %x P2 %x \n"、P1、P2 ) ;
                  h01 =   _amemd8 (P1); //读取输出输入
                  A01 =   _amemd8 (P2);
            
                  P1 = P1 + 2  ;
                  P2 = P2 + 2  ;
     
                   printf ("h01和 a 01 %f %f  %f %f %f \n"、_lof (h01)、_hif (h01)、_lof (A01)、_hif (A01))));
               
                  E01 =_complex_conjugate_mpysp (h01、A01)  ; //  或其他方法
                  b01 =_daddsp (E01、B01) ;//累加图像和实数
              }    
              
              
              
              
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            
              printf (" B01 %f %f \n",_lof (B01),_hif (B01));
     
              F01 =_amemd8 (p_in1)  ; // 读取输入
              G01 =_dsubsp (F01、B01) ;
              printf (" F01 %f %f \n",_lof (F01),_HIF (F01));
              printf (" G01 %f %f \n",_lof (G01),_hif (G01));
     
             C01 =_dmpysp (G01、D01) ;
              printf (" C01 %f %f \n",_lof (C01),_HIF (C01));           
        
             _amemd8 (p_out1)   = C01   ;
             P1 = P1 + 2  ;


    //////////   输出的地址是 什么??????

         }
     
     
       }


       返回 ;