背景

众所周知,PyTorch项目作为一个C++工程,是基于CMake进行构建的。然而当你想基于CMake来构建PyTorch静态库时,你会发现:

  • 静态编译相关的文档不全;
  • CMake文件bug太多,其整体结构比较糟糕。

由于重构整体的CMake结构需要很多的人力,一时半会还看不到优雅的解决方案,因此在这里,Gemfield先写篇文章来说明PyTorch的静态编译如何进行,以及各种注意事项。本文基于目前最新的PyTorch 1.7(在最新的master分支上也没有问题)。

尤其是涉及到PyTorch静态编译重构的时候,还牵涉到代码的重新设计。举个例子,一些模块的注册机制是依赖全局对象初始化的,在静态库中,这样的初始化逻辑并不会被链接(因为你的程序并没有调用它),这就导致程序需要调用的内容并没有被初始化。而为了解决这个问题引入的-Wl,--no-whole-archive,又导致编译目标的体积变得极为庞大。当未来,若PyTorch官方仓库为静态编译进行重构优化后,本文也就过时了(进而会被迁移到Gemfield的颓垣废址专栏下)。

思绪再回来,本文以在Ubuntu 18.04 上进行目标为x86_64 Linux的构建为例,介绍使用CMake对PyTorch进行静态编译的2种最小尺度(这种最小尺度的定义是:不更改pytorch自身代码、不使用CMake文件中intern的开关、使得libtorch能够进行模型的前向推理、tensor的序列化反序列化、库尽可能小):

  • 最小尺度CPU版本;
  • 最小尺度CUDA版本。

另外,在阅读本文前,我们需要熟悉下几个线性代数库的名字。

BLAS (Basic Linear Algebra Subprograms),是一个常见线性代数操作的API规范,偏向底层,主要内容有:向量相加、标量相乘、点积、线性组合、矩阵相乘等。

LAPACK (Linear Algebra Package) 也是一个线性代数库的规范. 基于BLAS规范,不同于BLAS的底层,LAPACK定位于高层。LAPACK定义矩阵分解的一些操作,如LU、LLt、QR、SVD、Schur,用来解决诸如找到矩阵的特征值、找到矩阵奇异值、求解线性方程组这样的问题。

BLAS/LAPACK既然是API规范,就应该有相应的实现,常用的有这些:

  • MKL,Intel Math Kernel Library。用于Intel处理器的(注意和MKL-DNN库的区别,MKL-DNN是英特尔的一个独立的神经网络库:MKL for Deep Neural Networks);
  • ATLAS,Automatically Tuned Linear Algebra Software,多平台的;
  • OpenBLAS,多平台的;
  • Accelerate,苹果macOS、iOS平台上的;
  • Eigen,这个库只有头文件,实现了BLAS和一部分LAPACK,集成到了PyTorch项目的thirdparty下;
  • cuBLAS,NVIDIA的BLAS实现,基于NVIDIA的GPU硬件(此外还有cuFFT、cuRAND, cuSPARSE等);
  • MAGMA,基于CUDA、HIP、 Intel Xeon Phi、OpenCL的BLAS/LAPACK实现;

还记得刚才提到过本文的目标是在Ubuntu 18.04上进行目标为x86_64 Linux的构建,这种情况下,我们必须要使用一个LAPACK实现:openblas、MKL或者Eigen。否则,运行时会报错:“gels : Lapack library not found in compile time”。本文使用MKL,并实验性质到介绍下如何使用Eigen。

CMake编译开关

使用CMake进行构建时,我们主要是通过一些编译开关来决定要编译的模块。这些编译开关是在CMake文件中定义的变量,其值的来源主要有这几种:

  • 默认值;
  • 用户指定;
  • 通过检测系统环境获得;
  • 通过检测软件包的安装情况获得;
  • 通过开关与开关之间的逻辑关系推导而来;

这些开关的值会影响:

  • CMakeLists.txt中要添加的编译单元;
  • 编译器、链接器的命令行参数;
  • 代码中的ifdef宏;

下面是一些主要的编译开关的初始值,这些值初始值要么为OFF,要么为ON,要么为空。如下所示:

1,默认关闭的

  1. ATEN_NO_TEST,是否编译ATen test binaries;
  2. BUILD_BINARY,Build C++ binaries;
  3. BUILD_DOCS,Build Caffe2 documentation;
  4. BUILD_CAFFE2_MOBILE,Build libcaffe2 for mobile,也就是在libcaffe2和libtorch mobile中选择,目前已经废弃,默认使用libtorch mobile;
  5. CAFFE2_USE_MSVC_STATIC_RUNTIME,Using MSVC static runtime libraries;
  6. BUILD_TEST,Build C++ test binaries (need gtest and gbenchmark);
  7. BUILD_STATIC_RUNTIME_BENCHMARK,Build C++ binaries for static runtime benchmarks (need gbenchmark);
  8. BUILD_TENSOREXPR_BENCHMARK,Build C++ binaries for tensorexpr benchmarks (need gbenchmark);
  9. BUILD_MOBILE_BENCHMARK,Build C++ test binaries for mobile (ARM) targets(need gtest and gbenchmark);
  10. BUILD_MOBILE_TEST,Build C++ test binaries for mobile (ARM) targets(need gtest and gbenchmark);
  11. BUILD_JNI,Build JNI bindings;
  12. BUILD_MOBILE_AUTOGRAD,Build autograd function in mobile build (正在开发中);
  13. INSTALL_TEST,Install test binaries if BUILD_TEST is on;
  14. USE_CPP_CODE_COVERAGE,Compile C/C++ with code coverage flags;
  15. USE_ASAN,Use Address Sanitizer;
  16. USE_TSAN,Use Thread Sanitizer;
  17. CAFFE2_STATIC_LINK_CUDA,Statically link CUDA libraries;
  18. USE_STATIC_CUDNN,Use cuDNN static libraries;
  19. USE_KINETO,Use Kineto profiling library;
  20. USE_FAKELOWP,Use FakeLowp operators;
  21. USE_FFMPEG;
  22. USE_GFLAGS;
  23. USE_GLOG;
  24. USE_LEVELDB;
  25. USE_LITE_PROTO,Use lite protobuf instead of full;
  26. USE_LMDB;
  27. USE_PYTORCH_METAL,Use Metal for PyTorch iOS build;
  28. USE_NATIVE_ARCH,Use -march=native;
  29. USE_STATIC_NCCL
  30. USE_SYSTEM_NCCL,Use system-wide NCCL;
  31. USE_NNAPI;
  32. USE_NVRTC,Use NVRTC. Only available if USE_CUDA is on;
  33. USE_OBSERVERS,Use observers module;
  34. USE_OPENCL;
  35. USE_OPENCV;
  36. USE_PROF,Use profiling;
  37. USE_REDIS;
  38. USE_ROCKSDB;
  39. USE_SNPE,使用高通的神经网络引擎;
  40. USE_SYSTEM_EIGEN_INSTALL,Use system Eigen instead of the one under third_party;
  41. USE_TENSORRT,Using Nvidia TensorRT library;
  42. USE_VULKAN,Use Vulkan GPU backend;
  43. USE_VULKAN_API,Use Vulkan GPU backend v2;
  44. USE_VULKAN_SHADERC_RUNTIME,Use Vulkan Shader compilation runtime(Needs shaderc lib);
  45. USE_VULKAN_RELAXED_PRECISION,Use Vulkan relaxed precision(mediump);
  46. USE_ZMQ;
  47. USE_ZSTD;
  48. USE_MKLDNN_CBLAS,Use CBLAS in MKLDNN;
  49. USE_TBB;
  50. HAVE_SOVERSION,Whether to add SOVERSION to the shared objects;
  51. USE_SYSTEM_LIBS,Use all available system-provided libraries;
  52. USE_SYSTEM_CPUINFO,Use system-provided cpuinfo;
  53. USE_SYSTEM_SLEEF,Use system-provided sleef;
  54. USE_SYSTEM_GLOO,Use system-provided gloo;
  55. USE_SYSTEM_FP16,Use system-provided fp16;
  56. USE_SYSTEM_PTHREADPOOL,Use system-provided pthreadpool;
  57. USE_SYSTEM_PSIMD,Use system-provided psimd;
  58. USE_SYSTEM_FXDIV,Use system-provided fxdiv;
  59. USE_SYSTEM_BENCHMARK,Use system-provided google benchmark;
  60. USE_SYSTEM_ONNX,Use system-provided onnx;
  61. USE_SYSTEM_XNNPACK,Use system-provided xnnpack。

2,默认打开的

  1. BUILD_CUSTOM_PROTOBUF,Build and use Caffe2's own protobuf under third_party;
  2. BUILD_PYTHON,Build Python binaries;
  3. BUILD_CAFFE2,Master flag to build Caffe2;
  4. BUILD_CAFFE2_OPS,Build Caffe2 operators;
  5. BUILD_SHARED_LIBS,Build libcaffe2.so;
  6. CAFFE2_LINK_LOCAL_PROTOBUF,If set, build protobuf inside libcaffe2.so;
  7. COLORIZE_OUTPUT,Colorize output during compilation;
  8. USE_CUDA
  9. USE_CUDNN
  10. USE_ROCM;
  11. USE_FBGEMM,Use FBGEMM (quantized 8-bit server operators);
  12. USE_METAL,Use Metal for Caffe2 iOS build;
  13. USE_NCCL,须在UNIX上,且USE_CUDA 或USE_ROCM是打开的;
  14. USE_NNPACK
  15. USE_NUMPY;
  16. USE_OPENMP,Use OpenMP for parallel code;
  17. USE_QNNPACK;Use QNNPACK (quantized 8-bit operators);
  18. USE_PYTORCH_QNNPACK,Use ATen/QNNPACK (quantized 8-bit operators);
  19. USE_VULKAN_WRAPPER,Use Vulkan wrapper;
  20. USE_XNNPACK
  21. USE_DISTRIBUTED;
  22. USE_MPI,Use MPI for Caffe2. Only available if USE_DISTRIBUTED is on;
  23. USE_GLOO,Only available if USE_DISTRIBUTED is on;
  24. USE_TENSORPIPE,Only available if USE_DISTRIBUTED is on;
  25. ONNX_ML,Enable traditional ONNX ML API;
  26. USE_NUMA;
  27. USE_VALGRIND;
  28. USE_MKLDNN;
  29. BUILDING_WITH_TORCH_LIBS,Tell cmake if Caffe2 is being built alongside torch libs。

3,默认为空的

  1. SELECTED_OP_LIST,Path to the yaml file that contains the list of operators to include for custom build. Include all operators by default;
  2. OP_DEPENDENCY,Path to the yaml file that contains the op dependency graph for custom build。

CMake编译开关的平台修正

编译开关的初始值并不是一成不变的,即使没有用户的手工指定,那么CMake也会通过检测硬件环境、系统环境、包依赖来进行修改。比如下面这样:

1,操作系统修正

  1. USE_DISTRIBUTED,如果不是Linux/Win32,则关闭;
  2. USE_LIBUV,macOS上,且手工打开USE_DISTRIBUTED,则打开;
  3. USE_NUMA,如果不是Linux,则关闭;
  4. USE_VALGRIND,如果不是Linux,则关闭;
  5. USE_TENSORPIPE,如果是Windows,则关闭;
  6. USE_KINETO,如果是windows,则关闭;
  7. 如果是构建Android、iOS等移动平台上的libtorch,则:
  set(BUILD_PYTHON OFF)set(BUILD_CAFFE2_OPS OFF)set(USE_DISTRIBUTED OFF)set(FEATURE_TORCH_MOBILE ON)set(NO_API ON)set(USE_FBGEMM OFF)set(USE_QNNPACK OFF)set(INTERN_DISABLE_ONNX ON)set(INTERN_USE_EIGEN_BLAS ON)set(INTERN_DISABLE_MOBILE_INTERP ON)

2,CPU架构修正

  1. USE_MKLDNN,如果不是64位x86_64,则关闭;
  2. USE_FBGEMM,如果不是64位x86_64,则关闭;如果不支持AVX512指令集,则关闭;
  3. USE_KINETO,如果是手机平台,则关闭;
  4. USE_GLOO,如果不是64位x86_64,则关闭;

3,软件包依赖修正

  1. USE_DISTRIBUTED,在Windows上,如果找不到libuv,则关闭;
  2. USE_GLOO,在Windows上,如果找不到libuv,则关闭;
  3. USE_KINETO,如果没有USE_CUDA,则关闭;
  4. MKL相关,不再赘述;
  5. NNPACK家族相关的((QNNPACK, PYTORCH_QNNPACK, XNNPACK) ),不再赘述;
  6. USE_BLAS,会被相关依赖修正;
  7. USE_PTHREADPOOL,会被相关依赖修正;
  8. USE_LAPACK,如果LAPACK包不能被找到,则关闭;且运行时会导致出错:“gels : Lapack library not found in compile time”;

4,用户手工指令的修正

  • 如果手工打开了USE_SYSTEM_LIBS,则:
  set(USE_SYSTEM_CPUINFO ON)set(USE_SYSTEM_SLEEF ON)set(USE_SYSTEM_GLOO ON)set(BUILD_CUSTOM_PROTOBUF OFF)set(USE_SYSTEM_EIGEN_INSTALL ON)set(USE_SYSTEM_FP16 ON)set(USE_SYSTEM_PTHREADPOOL ON)set(USE_SYSTEM_PSIMD ON)set(USE_SYSTEM_FXDIV ON)set(USE_SYSTEM_BENCHMARK ON)set(USE_SYSTEM_ONNX ON)set(USE_SYSTEM_XNNPACK ON)

  • 如果设置环境变量BUILD_PYTORCH_MOBILE_WITH_HOST_TOOLCHAIN,则set(INTERN_BUILD_MOBILE ON),而INTERN_BUILD_MOBILE一旦打开,则:
#只有编译caffe2 mobile的时候才是OFF,其它时候都是ON,也就是都会编译ATen的op
set(INTERN_BUILD_ATEN_OPS ON)set(BUILD_PYTHON OFF)
set(BUILD_CAFFE2_OPS OFF)
set(USE_DISTRIBUTED OFF)
set(FEATURE_TORCH_MOBILE ON)
set(NO_API ON)
set(USE_FBGEMM OFF)
set(USE_QNNPACK OFF)
set(INTERN_DISABLE_ONNX ON)
set(INTERN_USE_EIGEN_BLAS ON)
set(INTERN_DISABLE_MOBILE_INTERP ON)

5,CMake的配置

CMake的过程中会对系统环境进行检查,主要用来检测:

  • 是否支持AVX2(perfkernels有依赖);
  • 是否支持AVX512(fbgemm有依赖);
  • 寻找BLAS实现,如果目标是Mobile平台,使用Eigen;如果不是Mobile,则寻找MKL、openblas(找不到不会报错,但程序运行时会提示:gels : Lapack library not found in compile time);
  • Protobuf;
  • python解释器;
  • NNPACK(NNPACK backend 是x86-64);
  • OpenMP(是MKL-DNN的依赖);
  • NUMA;
  • pybind11;
  • CUDA;
  • ONNX;
  • MAGMA(基于GPU等设备的blas/lapack实现);
  • metal(苹果生态);
  • NEON(ARM生态,这里肯定是检测不到相关的硬件了);
  • MKL-DNN(Intel的深度学习库);
  • ATen parallel backend: NATIVE;
  • Sleef(thirdparty下的三方库);
  • RT : /usr/lib/x86_64-linux-gnu/librt.so ;
  • FFTW3 : /usr/lib/x86_64-linux-gnu/libfftw3.so;
  • OpenSSL: /usr/lib/x86_64-linux-gnu/libcrypto.so;
  • MPI;

CMake构建的时候会使用python脚本(tools/codegen/gen.py)生成一些cpp源文件,这个python脚本对yaml、dataclasses模块有依赖,因此,在开始编译前,你需要安装这些包:

root@gemfield:~# pip3 install setuptools
root@gemfield:~# pip3 install pyyaml
root@gemfield:~# pip3 install dataclasses

PyTorch官方预编译动态库的编译选项

如果对官方编译的库所选用的编译开关感兴趣的话,可以使用如下的python命令获得这些信息:

>>> print(torch.__config__.show())
PyTorch built with:- GCC 7.3- C++ Version: 201402- Intel(R) Math Kernel Library Version 2020.0.1 Product Build 20200208 for Intel(R) 64 architecture applications- Intel(R) MKL-DNN v1.5.0 (Git Hash e2ac1fac44c5078ca927cb9b90e1b3066a0b2ed0)- OpenMP 201511 (a.k.a. OpenMP 4.5)- NNPACK is enabled- CPU capability usage: AVX2- CUDA Runtime 10.1- NVCC architecture flags: -gencode;arch=compute_37,code=sm_37;-gencode;arch=compute_50,code=sm_50;-gencode;arch=compute_60,code=sm_60;-gencode;arch=compute_61,code=sm_61;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_75,code=sm_75;-gencode;arch=compute_37,code=compute_37- CuDNN 7.6.3- Magma 2.5.2- Build settings: BLAS=MKL, BUILD_TYPE=Release, CXX_FLAGS= -Wno-deprecated -fvisibility-inlines-hidden -DUSE_PTHREADPOOL -fopenmp -DNDEBUG -DUSE_FBGEMM -DUSE_QNNPACK -DUSE_PYTORCH_QNNPACK -DUSE_XNNPACK -DUSE_VULKAN_WRAPPER -O2 -fPIC -Wno-narrowing -Wall -Wextra -Werror=return-type -Wno-missing-field-initializers -Wno-type-limits -Wno-array-bounds -Wno-unknown-pragmas -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-unused-result -Wno-unused-local-typedefs -Wno-strict-overflow -Wno-strict-aliasing -Wno-error=deprecated-declarations -Wno-stringop-overflow -Wno-error=pedantic -Wno-error=redundant-decls -Wno-error=old-style-cast -fdiagnostics-color=always -faligned-new -Wno-unused-but-set-variable -Wno-maybe-uninitialized -fno-math-errno -fno-trapping-math -Werror=format -Wno-stringop-overflow, PERF_WITH_AVX=1, PERF_WITH_AVX2=1, PERF_WITH_AVX512=1, USE_CUDA=ON, USE_EXCEPTION_PTR=1, USE_GFLAGS=OFF, USE_GLOG=OFF, USE_MKL=ON, USE_MKLDNN=ON, USE_MPI=OFF, USE_NCCL=ON, USE_NNPACK=ON, USE_OPENMP=ON, USE_STATIC_DISPATCH=OFF

编译最小尺度的CPU版本静态库(MKL后端)

在这个最小尺度的CPU版本里,Gemfield将会选择MKL作为LAPACK的实现。此外,Gemfield将会首先禁用CUDA,这是自然而然的。其次Gemfield还要禁用caffe2(因为目的是编译libtorch),这会连带着禁用caffe2的op。整体要禁用的模块还有:

  • caffe2;
  • 可执行文件;
  • python;
  • test;
  • numa;
  • 分布式(DISTRIBUTED);
  • ROCM;
  • GLOO;
  • MPI;
  • CUDA;

1,安装MKL

既然选择了MKL,第一步就是要安装它。MKL使用的是ISSL授权(Intel Simplified Software License):

root@gemfield:~# wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB
root@gemfield:~# apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB
root@gemfield:~# echo deb https://apt.repos.intel.com/mkl all main > /etc/apt/sources.list.d/intel-mkl.list
root@gemfield:~# apt update
root@gemfield:~# apt install intel-mkl-64bit-2020.4-912

2,使用CMake构建

命令如下:

cmake
-DCMAKE_VERBOSE_MAKEFILE:BOOL=1
-DUSE_CUDA=OFF
-DBUILD_CAFFE2=OFF
-DBUILD_PYTHON:BOOL=OFF
-DBUILD_CAFFE2_OPS=OFF
-DUSE_DISTRIBUTED=OFF
-DBUILD_TEST=OFF
-DBUILD_BINARY=OFF
-DBUILD_MOBILE_BENCHMARK=0
-DBUILD_MOBILE_TEST=0
-DUSE_ROCM=OFF
-DUSE_GLOO=OFF
-DUSE_LEVELDB=OFF
-DUSE_MPI:BOOL=OFF
-DBUILD_CUSTOM_PROTOBUF:BOOL=OFF
-DUSE_OPENMP:BOOL=OFF
-DBUILD_SHARED_LIBS:BOOL=OFF
-DCMAKE_BUILD_TYPE:STRING=Release
-DPYTHON_EXECUTABLE:PATH=`which python3`
-DCMAKE_INSTALL_PREFIX:PATH=../libtorch_cpu_mkl
../pytorch

然后使用如下的命令进行编译:

cmake --build . --target install -- "-j8"

编译成功后,生成的静态库有(23个,其中一个是链接文件):

lib/libprotobuf.a
lib/libsleef.a
lib/libclog.a
lib/libcpuinfo.a
lib/libnnpack.a
lib/libasmjit.a
lib/libmkldnn.a
lib/libpytorch_qnnpack.a
lib/libcaffe2_protos.a
lib/libprotobuf-lite.a
lib/libfbgemm.a
lib/libc10.a
lib/libpthreadpool.a
lib/libtorch_cpu.a
lib/libdnnl.a
lib/libqnnpack.a
lib/libprotoc.a
lib/libXNNPACK.a
lib/libtorch.a
lib/libonnx_proto.a
lib/libfmt.a
lib/libonnx.a
lib/libfoxi_loader.a

或者你也想编译Caffe2的话,就开启BUILD_CAFFE2编译开关:

cmake
-DCMAKE_VERBOSE_MAKEFILE:BOOL=1
-DBUILD_CAFFE2=ON
-DBUILD_CAFFE2_OPS=ON
-DUSE_OPENMP=ON
-DUSE_MKLDNN=ON
-DUSE_GFLAGS=OFF
-DUSE_GLOG=OFF
-DUSE_CUDA=OFF
-DBUILD_PYTHON:BOOL=OFF
-DUSE_DISTRIBUTED=OFF
-DBUILD_TEST=OFF
-DBUILD_BINARY=OFF
-DBUILD_MOBILE_BENCHMARK=0
-DBUILD_MOBILE_TEST=0
-DUSE_ROCM=OFF
-DUSE_GLOO=OFF
-DUSE_LEVELDB=OFF
-DUSE_MPI:BOOL=OFF
-DBUILD_SHARED_LIBS:BOOL=OFF
-DCMAKE_BUILD_TYPE:STRING=Release
-DPYTHON_EXECUTABLE:PATH=`which python3`
-DCMAKE_INSTALL_PREFIX:PATH=../libtorch_cpu_caffe2
../pytorch

生成的静态库有26个,除了上面的非caffe2版本,还多出来了perfkernel模块生成的:

libCaffe2_perfkernels_avx.a
libCaffe2_perfkernels_avx2.a
libCaffe2_perfkernels_avx512.a

这三个库包含了如下的API,实现了一些FMA操作:

caffe2::EmbeddingLookupIdx_int32_t_float_float_false__avx2_fma
caffe2::EmbeddingLookupIdx_int32_t_float_float_true__avx2_fma
caffe2::EmbeddingLookupIdx_int32_t_half_float_false__avx2_fma
caffe2::EmbeddingLookupIdx_int32_t_half_float_true__avx2_fma
caffe2::EmbeddingLookupIdx_int32_t_uint8_t_float_false__avx2_fma
caffe2::EmbeddingLookupIdx_int32_t_uint8_t_float_true__avx2_fma
caffe2::EmbeddingLookupIdx_int64_t_float_float_false__avx2_fma
caffe2::EmbeddingLookupIdx_int64_t_float_float_true__avx2_fma
caffe2::EmbeddingLookupIdx_int64_t_half_float_false__avx2_fma
caffe2::EmbeddingLookupIdx_int64_t_half_float_true__avx2_fma
caffe2::EmbeddingLookupIdx_int64_t_uint8_t_float_false__avx2_fma
caffe2::EmbeddingLookupIdx_int64_t_uint8_t_float_true__avx2_fma
caffe2::EmbeddingLookup_int32_t_float_float_false__avx2_fma
caffe2::EmbeddingLookup_int32_t_float_float_true__avx2_fma
caffe2::EmbeddingLookup_int32_t_half_float_false__avx2_fma
caffe2::EmbeddingLookup_int32_t_half_float_true__avx2_fma
caffe2::EmbeddingLookup_int32_t_uint8_t_float_false__avx2_fma
caffe2::EmbeddingLookup_int32_t_uint8_t_float_true__avx2_fma
caffe2::EmbeddingLookup_int64_t_float_float_false__avx2_fma
caffe2::EmbeddingLookup_int64_t_float_float_true__avx2_fma
caffe2::EmbeddingLookup_int64_t_half_float_false__avx2_fma
caffe2::EmbeddingLookup_int64_t_half_float_true__avx2_fma
caffe2::EmbeddingLookup_int64_t_uint8_t_float_false__avx2_fma
caffe2::EmbeddingLookup_int64_t_uint8_t_float_true__avx2_fma
caffe2::Fused8BitRowwiseEmbeddingLookupIdx_int32_t_uint8_t_float_false__avx2_fma
caffe2::Fused8BitRowwiseEmbeddingLookupIdx_int64_t_uint8_t_float_false__avx2_fma
caffe2::Fused8BitRowwiseEmbeddingLookup_int32_t_uint8_t_float_false__avx2_fma
caffe2::Fused8BitRowwiseEmbeddingLookup_int64_t_uint8_t_float_false__avx2_fma
caffe2::TypedAxpy__avx2_fma
caffe2::TypedAxpy__avx_f16c
caffe2::TypedAxpyHalffloat__avx2_fma
caffe2::TypedAxpyHalffloat__avx_f16c
caffe2::TypedAxpy_uint8_float__avx2_fma

此外,SELECTED_OP_LIST可以减少要编译的OP,但本文不讨论——因为Gemfield编译的静态库要满足不同模型的推理。

3,使用pytorch静态库(MKL后端版本)

如何让自己的程序链接该静态库呢?由于MKL依赖openmp,因此编译的命令行参数要打开(-fopenmp )。而PyTorch的代码又需要依赖以下的MKL静态库:

/opt/intel/mkl/lib/intel64/libmkl_intel_lp64.a
#/opt/intel/mkl/lib/intel64/libmkl_sequential.a
/opt/intel/mkl/lib/intel64/libmkl_gnu_thread.a
/opt/intel/mkl/lib/intel64/libmkl_core.a

以及依赖系统上的这几个共享库:

/usr/lib/x86_64-linux-gnu/libpthread.so
/usr/lib/x86_64-linux-gnu/libm.so
/usr/lib/x86_64-linux-gnu/libdl.so

因此你的程序需要链接这些库(除了链接pytorch那二十几个静态库外)才能完成编译。感觉很复杂是吧?忘记这些吧,使用我们开源的libdeepvac库吧。libdeepvac封装了libtorch,提供更简化的C++中使用PyTorch模型的方法。

编译最小尺度的CPU版本静态库(Eigen后端)

在这个最小尺度的CPU版本里,Gemfield将会选择Eigen来作为LAPACK的实现。使用Eigen来作为LAPACK实现的话,需要打开INTERN_USE_EIGEN_BLAS。看见INTERN前缀了吧,这提示我们不应该这样来使用这个开关。并且由于在PyTorch中,Eigen是为mobile平台而设计使用的,因此要想在x86_64 Linux使用,就需要改下pytorch仓库中的CMake文件:一共2处。

1,修改PyTorch的CMake文件

第一处,修改cmake/Dependencies.cmake:

#if(NOT INTERN_BUILD_MOBILE)
#  set(AT_MKL_ENABLED 0)
#  set(AT_MKL_MT 0)
#  set(USE_BLAS 1)
#  if(NOT (ATLAS_FOUND OR OpenBLAS_FOUND OR MKL_FOUND OR VECLIB_FOUND OR GENERIC_BLAS_FOUND))
#    message(WARNING "Preferred BLAS (" ${BLAS} ") cannot be found, now searching for a general BLAS library")
#    find_package(BLAS)
#    if(NOT BLAS_FOUND)
#      set(USE_BLAS 0)
#    endif()
#  endif()
#
#  if(MKL_FOUND)
#    add_definitions(-DTH_BLAS_MKL)
#    if("${MKL_THREADING}" STREQUAL "SEQ")
#      add_definitions(-DTH_BLAS_MKL_SEQ=1)
#    endif()
#    if(MSVC AND MKL_LIBRARIES MATCHES ".*libiomp5md.lib.*")
#      add_definitions(-D_OPENMP_NOFORCE_MANIFEST)
#      set(AT_MKL_MT 1)
#    endif()
#    set(AT_MKL_ENABLED 1)
#  endif()
if(INTERN_USE_EIGEN_BLAS)# Eigen BLAS for Mobileset(USE_BLAS 1)set(AT_MKL_ENABLED 0)include(${CMAKE_CURRENT_LIST_DIR}/External/EigenBLAS.cmake)list(APPEND Caffe2_DEPENDENCY_LIBS eigen_blas)
endif()

第二处,修改cmake/External/EigenBLAS.cmake:

root@gemfield:~# git diff cmake/External/EigenBLAS.cmake
......set(__EIGEN_BLAS_INCLUDED TRUE)
-
-if(NOT INTERN_BUILD_MOBILE OR NOT INTERN_USE_EIGEN_BLAS)
+if(NOT INTERN_USE_EIGEN_BLAS)return()endif()

此外,像MKL后端那样,Gemfield要照例禁用CUDA、caffe2、caffe2的op。

2,使用CMake进行构建

命令如下(注意开启了INTERN_USE_EIGEN_BLAS):

cmake
-DINTERN_USE_EIGEN_BLAS=ON
-DCMAKE_VERBOSE_MAKEFILE:BOOL=1
-DBUILD_CAFFE2=OFF
-DBUILD_CAFFE2_OPS=OFF
-DBUILD_PYTHON:BOOL=OFF
-DUSE_DISTRIBUTED=OFF
-DBUILD_TEST=OFF
-DBUILD_BINARY=OFF
-DBUILD_MOBILE_BENCHMARK=0
-DBUILD_MOBILE_TEST=0
-DUSE_ROCM=OFF
-DUSE_GLOO=OFF
-DUSE_CUDA=OFF
-DUSE_LEVELDB=OFF
-DUSE_MPI:BOOL=OFF
-DBUILD_CUSTOM_PROTOBUF:BOOL=OFF
-DUSE_OPENMP:BOOL=OFF
-DBUILD_SHARED_LIBS:BOOL=OFF
-DCMAKE_BUILD_TYPE:STRING=Release
-DPYTHON_EXECUTABLE:PATH=`which python3`
-DCMAKE_INSTALL_PREFIX:PATH=../libtorch_cpu_eigen
../pytorch

然后使用如下的命令进行编译:

cmake --build . --target install -- "-j8"

编译出来的静态库如下所示(24个,注意多出来libeigen_blas.a):

lib/libprotobuf.a
lib/libsleef.a
lib/libclog.a
lib/libcpuinfo.a
lib/libeigen_blas.a
lib/libnnpack.a
lib/libasmjit.a
lib/libmkldnn.a
lib/libpytorch_qnnpack.a
lib/libcaffe2_protos.a
lib/libprotobuf-lite.a
lib/libfbgemm.a
lib/libc10.a
lib/libpthreadpool.a
lib/libtorch_cpu.a
lib/libdnnl.a
lib/libqnnpack.a
lib/libprotoc.a
lib/libXNNPACK.a
lib/libtorch.a
lib/libonnx_proto.a
lib/libfmt.a
lib/libonnx.a
lib/libfoxi_loader.a

3,使用pytorch静态库(Eigen后端版本)

和MKL后端不同,你的程序需要从链接MKL转而去链接libeigen_blas.a,更简单了。如果使用libdeepvac库的话,这都是无感的。

和MKL后端的静态库进行了下粗略的性能比较,在6核intel处理器的系统上,对20个目标进行CNN+LSTM的计算下,MKL版本消耗了11秒,而Eigen版本消耗了20秒。所以还是推荐使用MKL

编译最小尺度的CUDA版本静态库(无MAGMA版本)

1,配置

相关的配置如下:

  • 要编译CUDA版本,必须启用USE_CUDA;
  • 另外,回落到CPU的时候,我们依然需要有对应的LAPACK实现,这里还是选择MKL,安装方法见前文;
  • 还有一个地方需要注意:是否使用MAGMA。这里Gemfield先不使用。

2,CUDA架构

同CPU版本相比,编译CUDA版本的最大不同就是要指定CUDA架构(开普勒、麦克斯韦、帕斯卡、图灵、安培等)。要编译特定CUDA架构的目标,需要给NVCC编译器传递特定架构的号码,如7.0。特定架构的号码有两种情况:PTX和非PTX。比如7.0 和 7.0 PTX。

非PTX版本是实际的二进制文件,只能做到主版本号兼容。比如compute capability 3.0 的编译产物只能运行在compute-capability 3.x的架构上(开普勒架构),而不能运行在compute-capability 5.x (麦克斯韦) 或者 6.x (帕斯卡) 设备上。

而PTX版本编译出来的是JIT的中间代码,可以做到前向兼容——也就是旧设备的目标可以运行在新设备上。因为编译的时候可以指定多个架构号码,因此一个技巧就是,总是在最高的版本号上加上PTX,以获得前向兼容,比如:3.5;5.0;5.2;6.0;6.1;7.0;7.5;7.5+PTX。

另外,在不兼容的CUDA设备上运行你的程序会出现“no kernel image is available for execution on the device”错误。编译PyTorch的时候,这个CUDA架构号码来自三种方式:

  • 自动检测本地机器上的设备号;
  • 检测不到,则使用默认的一组;
  • 用户通过TORCH_CUDA_ARCH_LIST环境变量指定。TORCH_CUDA_ARCH_LIST环境变量,比如TORCH_CUDA_ARCH_LIST="3.5 5.2 6.0 6.1+PTX",决定了要编译的pytorch支持哪些cuda架构。支持的架构越多,最后的库越大;
#cuda9
export TORCH_CUDA_ARCH_LIST="3.5;5.0;5.2;6.0;6.1;7.0;7.0+PTX"#cuda10
export TORCH_CUDA_ARCH_LIST="3.5;5.0;5.2;6.0;6.1;7.0;7.5;7.5+PTX"#cuda11
export TORCH_CUDA_ARCH_LIST="3.5;5.0;5.2;6.0;6.1;7.0;7.5;8.0;8.0+PTX"#cuda11.1
export TORCH_CUDA_ARCH_LIST="5.0;7.0;8.0;8.6;8.6+PTX"

一些市面上常见显卡的compute-capability号码如下所示:

3,使用CMake进行构建

cmake命令如下(注意打开了USE_CUDA):

cmake
-DCMAKE_VERBOSE_MAKEFILE:BOOL=1
-DUSE_CUDA=ON
-DBUILD_CAFFE2=OFF
-DBUILD_CAFFE2_OPS=OFF
-DUSE_DISTRIBUTED=OFF
-DBUILD_TEST=OFF
-DBUILD_BINARY=OFF
-DBUILD_MOBILE_BENCHMARK=0
-DBUILD_MOBILE_TEST=0
-DUSE_ROCM=OFF
-DUSE_GLOO=OFF
-DUSE_LEVELDB=OFF
-DUSE_MPI:BOOL=OFF
-DBUILD_PYTHON:BOOL=OFF
-DBUILD_CUSTOM_PROTOBUF:BOOL=OFF
-DUSE_OPENMP:BOOL=OFF
-DBUILD_SHARED_LIBS:BOOL=OFF
-DCMAKE_BUILD_TYPE:STRING=Release
-DPYTHON_EXECUTABLE:PATH=`which python3`
-DCMAKE_INSTALL_PREFIX:PATH=../libtorch_cuda
../pytorch

在CUDA版本中,CMake构建时还会检测:

  • -- CUDA detected: 10.2
  • -- CUDA nvcc is: /usr/local/cuda/bin/nvcc
  • -- CUDA toolkit directory: /usr/local/cuda
  • -- cuDNN: v7.6.5 (include: /usr/include, library: /usr/lib/x86_64-linux-gnu/libcudnn.so)
  • -- Autodetected CUDA architecture(s): 3.5;5.0;5.2;6.0;6.1;7.0;7.5;7.5+PTX;
  • -- Found CUDA with FP16 support, compiling with torch.cuda.HalfTensor;

然后使用如下的命令进行编译:

cmake --build . --target install -- "-j8"

编译出如下的静态库(26个):

libasmjit.a
libc10.a
libc10_cuda.a
libcaffe2_protos.a
libclog.a
libcpuinfo.a
libdnnl.a
libfbgemm.a
libfmt.a
libfoxi_loader.a
libmkldnn.a
libnccl_static.a
libnnpack.a
libonnx.a
libonnx_proto.a
libprotobuf.a
libprotobuf-lite.a
libprotoc.a
libpthreadpool.a
libpytorch_qnnpack.a
libqnnpack.a
libsleef.a
libtorch.a
libtorch_cpu.a
libtorch_cuda.a
libXNNPACK.a

可以看到相比CPU版本多出了libtorch_cuda.a、 libc10_cuda.a、libnccl_static.a这3个静态库。

4,使用pytorch静态库(CUDA版本)

和CPU版本类似,但是区别是还要链接NVIDIA的cuda运行时的库(这部分是动态库)。此外,如果你的程序初始化的时候报错:“PyTorch is not linked with support for cuda devices”,说明你没有whole_archive c10_cuda.a静态库。

如果你编译自己程序的时候遇到了cannot find -lnvToolsExt、cannot find -lcudart这样的错误,你还需要设置下环境变量让链接器能够找到cuda运行时的库:

root@gemfield:~# export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/cuda-10.2/targets/x86_64-linux/lib/

总之,你如果使用了libdeepvac封装的话(需要打开USE_CUDA),就没有这么多问题了。

5,性能

做了一个快速的不严谨的推理性能测试。使用了cuda版本后,还是之前的那个系统,还是之前那个测试任务(对20个目标进行CNN+LSTM的计算下),CUDA版本消耗了不到一秒。

最后

在你使用pytorch静态库的时候,或多或少还会遇到一些问题。但是,何必自讨苦吃呢,使用我们封装了libtorch的libdeepvac库吧:

DeepVAC/libdeepvac​github.com

pytorch 矩阵相乘_编译PyTorch静态库相关推荐

  1. pytorch 矩阵相乘_深入浅出PyTorch(算子篇)

    Tensor 自从张量(Tensor)计算这个概念出现后,神经网络的算法就可以看作是一系列的张量计算.所谓的张量,它原本是个数学概念,表示各种向量或者数值之间的关系.PyTorch的张量(torch. ...

  2. pytorch 矩阵相乘_深度学习 — — PyTorch入门(三)

    点击关注我哦 autograd和动态计算图可以说是pytorch中非常核心的部分,我们在之前的文章中提到:autograd其实就是反向求偏导的过程,而在求偏导的过程中,链式求导法则和雅克比矩阵是其实现 ...

  3. linux编译c++ 静态库,C/C++ 条件编译静态库

    ==>windows 下方法: 1.方法一:VS工程中中直接添加 1.1在VS的属性->常规->附加库目录,添上文件夹的路径:例如:lib/x64: 1.2输入的附加依赖项,添加上库 ...

  4. cmake 编译 linux 库,使用CMake编译跨平台静态库

    在开始介绍如何使用CMake编译跨平台的静态库以前,先讲讲我在没有使用CMake以前所趟过的坑.由于不少开源的程序,好比png,都是自带编译脚本的.咱们可使用下列脚原本进行编译:linux . / c ...

  5. PHP编译为静态库,Linux下将Tinyxml编译为静态库

    转载请注明来源:Linux下将Tinyxml编译为静态库 一个应用需要在linux服务器上运行,不能保证每个服务器都有应用依赖的库,又懒得每个服务器都去安装下,也不太现实,于是就将应用所用到的库全部编 ...

  6. iOS架构-C/C++lame库在Mac下编译通用静态库.a库(13)

    C/C++ 有很多成熟的库,还有很多特殊功能的库,有时候iOS 平台开发一些比较前沿或者冷门的功能时,iOS并没有提供解决方案,这时候就可以研究C/C++的一些库,为我们使用.但是在Xcode编译C/ ...

  7. Linux中gcc的编译、静态库和动态库的制作

    欢迎大家关注笔者,你的关注是我持续更博的最大动力 Linux中gcc的编译.静态库.动态库 文章目录: 1 gcc的编译过程 1.1 gcc的编译过程 1.2 gcc的常用参数 2 gcc 静态库的制 ...

  8. Linux基础——gcc编译、静态库与动态库(共享库)

    Linux基础--gcc编译.静态库与动态库(共享库) https://blog.csdn.net/daidaihema/article/details/80902012 Linux基础--gcc编译 ...

  9. 【转】iOS编译OpenSSL静态库(使用脚本自动编译)

    原文网址:https://www.jianshu.com/p/651513cab181 本篇文章为大家推荐两个脚本,用来iOS系统下编译OpenSSL通用库,如果想了解编译具体过程,请参看<iO ...

最新文章

  1. 【转】Service Intent must be explicit的解决方法
  2. 微信支付java helloworld_10行代码搞定微信支付(Java版)
  3. 送书福利 | 大数据智能:数据驱动的自然语言处理技术
  4. CentOS7—HAProxy安装与配置
  5. jQuery插件---ImageBox的使用
  6. halcon边缘检测的方法及各种方法的适用范围
  7. C++函数返回引用的含义
  8. 从零开始学习python编程-和尧名大叔一起从0开始学Python编程-循环
  9. javplayer 使用教程_「松下A6系列伺服使用手册」6.出现问题时
  10. 王垠系列博文(题名外挂URL)
  11. 软件设计师考试大纲(2004版)
  12. rpc调用过程原理分析以及Dubbo、Feign调用过程
  13. Python爬虫初探——天涯
  14. Echart用法介绍
  15. excel函数--if函数计算销售提成
  16. Power BI桌面版与Online版功能区别
  17. 语音中常用输入特征的提取过程:MFCC、FBank
  18. 离散数学——coq学习笔记(二)
  19. Varargs(可变个数形参)
  20. html文件只能打印一页,javascript – 使用window.print()打印巨大的表只打印一页

热门文章

  1. 一篇文章教你读懂Spring @Conditional注解
  2. linux shell数组动态在for中追加元素及其遍历
  3. vscode设置终端字体大小
  4. Ansible自动化运维企业实际应用场景分析
  5. (网址收藏)Golang模块之HTTP
  6. scala方法定义示例
  7. bat批处理启动QQ、微信、企业微信
  8. Scala 类中声明方法
  9. SpringBoot JWT工具类完整代码
  10. Linux零拷贝的原理