在c+中的高性能库之间切换+; 我正在编写一些C++代码,以面向不同的平台。这包括x86、x64和ARM。我目前在x64上使用Intel IPP和MKL(用于SSE),并希望为ARM添加NEON库。是否有一种标准的方法来围绕特定的库进行分支,并尽量减少依赖性和麻烦?我目前正在使用Visual Studio 2008或2012

在c+中的高性能库之间切换+; 我正在编写一些C++代码,以面向不同的平台。这包括x86、x64和ARM。我目前在x64上使用Intel IPP和MKL(用于SSE),并希望为ARM添加NEON库。是否有一种标准的方法来围绕特定的库进行分支,并尽量减少依赖性和麻烦?我目前正在使用Visual Studio 2008或2012,c++,visual-studio,neon,intel-ipp,C++,Visual Studio,Neon,Intel Ipp,我最初的想法是围绕特定的调用进行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]; #e

我最初的想法是围绕特定的调用进行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

}
void addVectors(int*a,int*b,int-n)
{
#ifdef INTELIPP
ippsad_32s_I(…);
#艾利夫·阿姆内斯
neonAdd_32s_I(…);
#否则
对于(int k=0;k
但这可能会变得非常混乱。我想知道标准方法是什么。例如,我希望IPP和NEON代码的单独项目更简洁,并且只针对其中一个构建主项目


除了支持之外,IDE并不是非常重要——我想我们会转向Eclipse这样的ARM工作。

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

  • MyFFT.h(公共API)
  • MyFFT_Intel.c
  • MyFFT_Neon.c
  • MyFFT_CrossPlatform.c(普通c实现)
  • MyFFT_Common.c
  • MyFFT_Private.h(用于在MyFFT_common.c中实现的公共辅助函数原型)
当然,拥有大量的单元测试对于所有这样的跨平台抽象来说都是非常关键的


另一个需要考虑的问题是CPU调度。例如,如果您在ARM上运行,您可能需要检测运行时是否存在霓虹灯。IPP对于Intel变体来说是隐藏的,但随着ARM的成熟和NEON功能的改变,SSE的变化方式也一样,您可能需要实现自己的调度机制,除非您使用的是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
dot(双*a,双*b,双*c,整数n)
gemm(双*a,双*b,双*c,整数n);
#如果BLASLIB==0
#包括
静态内联点(双*a、双*b、双*c、整数n){
点阵默认值(a、b、c、n);
}
静态内联gemm(双*a、双*b、双*c、整数n){
gemm_默认值(a、b、c、n)
}
#elif BLASLIB==1
#包括
静态内联点_mkl(双*a,双*b,双*c,整数n){
cblas_daxpy(a,b,c,n);//固定参数
}
静态内联gemm(双*a、双*b、双*c、整数n){
cblas_gemm(a,b,c,n);//固定参数
}
#elif BLASLIB==2
#包括
静态内联点_霓虹灯(双*a、双*b、双*c、整数n){
点氖(a,b,c,n);
}
静态内联gemm(双*a、双*b、双*c、整数n){
gemm_霓虹灯(a、b、c、n)
}
#恩迪夫
然后制作三个不同的构建文件,包括适当的库,并将例如
-DBLASLIB 1
添加到命令行选项中。有关处理三个库的示例,请参见Agner Fog中的文件“vectormath.h”:C数学库、Intel SVML和AMD LIBM。您可以将Eigen用于NEON(MKL比x86上的Eigen快得多),这样就不必编写任何附加模块。只有这个头文件