在c++中的高性能库之间切换
Switching Between High Performance Libraries in c++
我正在编写一些针对各种平台的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快得多),这样你就不必编写任何附加模块了。只有这个头文件
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- int(c) 和 c-'0' 之间的区别。C++
- 在cuda线程之间共享大量常量数据
- 在c代码之间共享数据的最佳方式
- Mix_Init和Mix_OpenAudio SDL之间的区别是什么
- C++ 使用 assign 函数的字符串与直接使用 '=' 更改值的字符串之间的区别
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- std::atomic和std::condition_variable wait,notify_*方法之间的区别
- 大小相等但成员数量不同的结构之间的性能差异
- 类与私有变量的其他类之间的线程安全性
- 如何在cpp文件之间切换窗口?在Qt中
- 线程之间的布尔停止信号
- 我是C++编程的新手,这些代码之间有什么区别,我应该使用哪一个
- 在 const 函数中通过引用和指针返回之间的区别
- 我想知道长双倍和双倍之间的区别
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 使用.find函数在c++中查找字符和另一个字符之间的大小
- 构造函数和转换运算符之间的重载解析
- 在c++中的高性能库之间切换