3

I am writing some C++ code to target various platforms. This includes x86, x64, and ARM. I currently use Intel IPP and MKL (for SSE) on x64 and expect to add a NEON library for ARM. Is there a standard way to branch around specific libraries and with minimal dependencies and fuss? I am currently using Visual Studio 2008 or 2012.

My initial thought is to just #ifdef around specific calls and test for X86, X64, ARM, etc. Like:

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

}

but this could get really messy. I was wondering what the standard approach is. For example, I expect it to be cleaner to a separate project for the IPP and NEON code and only build the main project against one of them?

The IDE is not terribly important except for support---and I suspect we will change over to something like Eclipse for the ARM work.

Steve
  • 3,957
  • 2
  • 26
  • 50

2 Answers2

3

I'm pretty sure that the only other option besides lots of preprocessor junk is to have different files for different platforms, and the build process will select the file for the architecture you're targeting for a particular library. The downside to this is that if there are more complicated functions, maintaining the different implementations of the same function so that they all behave identically gets a little more tricky. In some cases you may want to use a common file or macros to implement aspects of functions that are common across architectures. For example:

  • MyFFT.h (Public API)
  • MyFFT_Intel.c
  • MyFFT_Neon.c
  • MyFFT_CrossPlatform.c (Plain C implementation)
  • MyFFT_Common.c
  • MyFFT_Private.h (For common helper functions prototypes implemented in MyFFT_Common.c)

Of course, having lots of unit tests is really key for all cross-platform abstractions like this.

One other thing to consider is CPU dispatching. If you are running on ARM, for example, you may want to detect if NEON is present at runtime. IPP does under the hood for Intel variants, but as ARM matures and NEON capabilities change the same way SSE has, you may need to implement your own dispatching mechanism unless you are using a 3P product that handles this for you.

Peter M
  • 1,918
  • 16
  • 24
1

Rather than put the defines in each function make a define for each library which includes all its functions. Here is an example. Let's assume you want a cross platform BLAS library. For simplicity let's choose only two functions

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

Then make three different build files, include the appropriate libraries, and add e.g. -DBLASLIB 1 to the command line options. See the file "vectormath.h" in Agner Fog's Vector Class Library for an example which handles three libraries: the C math library, Intel SVML, and AMD LIBM. You could used Eigen for NEON (MKL is much faster than Eigen on x86) then you would not have to write any additions modules. Just this header file.

Z boson
  • 32,619
  • 11
  • 123
  • 226