在c++中的高性能库之间切换

Switching Between High Performance Libraries in c++

本文关键字:之间 高性能 c++      更新时间:2023-10-16

我正在编写一些针对各种平台的c++代码。这包括x86、x64和ARM。我目前在x64上使用Intel IPP和MKL(用于SSE),并希望为ARM添加NEON库。是否有一种标准的方法来围绕特定的库进行分支,并且最小化依赖和麻烦?我目前使用的是Visual Studio 2008或2012。

我最初的想法是#ifdef围绕特定的调用和测试X86, X64, ARM等。如:

void addVectors(int * a, int * b, int n)
{
   #ifdef INTELIPP
      ippsAdd_32s_I(...);
   #elif ARMNEON
      neonAdd_32s_I(...);
   #else
      for(int k = 0; k < n; k++)
         a[k] += b[k];
   #endif
}

但是这会变得很乱。我想知道标准的方法是什么。例如,我希望IPP和NEON代码单独的项目更清晰,并且只针对其中一个构建主项目?

除了支持之外,IDE并不是特别重要——我怀疑我们会将ARM的工作转向Eclipse之类的东西。

我非常确定,除了大量的预处理器垃圾之外,唯一的其他选择是为不同的平台提供不同的文件,构建过程将为您针对特定库的体系结构选择文件。这样做的缺点是,如果有更复杂的函数,维护同一个函数的不同实现,使它们的行为完全相同,就会变得有点棘手。在某些情况下,您可能希望使用公共文件或宏来实现跨体系结构通用的功能方面。例如:

    MyFFT.h (Public API)
  • MyFFT_Intel.c
  • MyFFT_Neon.c
  • MyFFT_CrossPlatform.c (Plain C实现)
  • MyFFT_Common.c
  • MyFFT_Private.h(用于MyFFT_Common.c中实现的常用辅助函数原型)

当然,拥有大量的单元测试对于所有这样的跨平台抽象来说是非常关键的。

另一件要考虑的事情是CPU调度。例如,如果您在ARM上运行,您可能希望在运行时检测NEON是否存在。IPP在Intel变体的底层可以实现,但是随着ARM的成熟和NEON功能的变化,您可能需要实现自己的调度机制,除非您正在使用一个3P产品来为您处理这个问题。

与其将定义放在每个函数中,不如为每个库定义一个包含其所有函数的定义。这里有一个例子。假设您想要一个跨平台的BLAS库。为简单起见,我们只选择两个函数

dot(double *a , double *b, double *c, int n)
gemm(double *a , double *b, double *c, int n);

#if BLASLIB == 0
    #include <blas_default.h>
    static inline dot(double *a , double *b, double *c, int n) {
        dot_default(a,b,c,n);
    }
    static inline gemm(double *a , double *b, double *c, int n) {
        gemm_default(a,b,c,n)      
    }
#elif BLASLIB == 1
    #include <mkl.h>
    static inline dot_mkl(double *a , double *b, double *c, int n) {
        cblas_daxpy(a,b,c,n);  //fix parameters
    }
    static inline gemm(double *a , double *b, double *c, int n) {
        cblas_gemm(a,b,c,n); //fix parameters      
    }
#elif BLASLIB == 2
    #include <blas_neon.h>
    static inline dot_neon(double *a , double *b, double *c, int n) {
        dot_neon(a,b,c,n);
    }
    static inline gemm(double *a , double *b, double *c, int n) {
        gemm_neon(a,b,c,n)      
    }
#endif

然后制作三个不同的构建文件,包括适当的库,并将例如-DBLASLIB 1添加到命令行选项中。参见Agner Fog的矢量类库中的"vectormath.h"文件,该文件处理三个库:C数学库,Intel SVML和AMD LIBM。你可以在NEON上使用Eigen (MKL比x86上的Eigen快得多),这样你就不必编写任何附加模块了。只有这个头文件