地址:
众所周知,PyTorch项目作为一个C++工程,是基于CMake进行构建的。然而当你想基于CMake来构建PyTorch静态库时,你会发现:
静态编译相关的文档不全;
CMake文件bug太多,其整体结构比较糟糕。
由于重构整体的CMake结构需要很多的人力,一时半会还看不到优雅的解决方案,因此在这里,Gemfield先写篇文章来说明PyTorch的静态编译如何进行,以及各种注意事项。本文基于目前最新的(在最新的master分支上也没有问题)。
思绪再回来,本文以在上进行目标为x86_64Linux的构建为例,介绍使用CMake对PyTorch进行静态编译的2种最小尺度(这种最小尺度的定义是:不更改pytorch自身代码、不使用CMake文件中intern的开关、使得libtorch能够进行模型的前向推理、tensor的序列化反序列化、库尽可能小):
最小尺度CPU版本;
最小尺度CUDA版本。
另外,在阅读本文前,我们需要熟悉下几个线性代数库的名字。
BLAS(BasicLinearAlgebraSubprograms),是一个常见线性代数操作的API规范,偏向底层,主要内容有:向量相加、标量相乘、点积、线性组合、矩阵相乘等。
LAPACK(LinearAlgebraPackage)也是一个线性代数库的规范.基于BLAS规范,不同于BLAS的底层,LAPACK定位于高层。LAPACK定义矩阵分解的一些操作,如LU、LLt、QR、SVD、Schur,用来解决诸如找到矩阵的特征值、找到矩阵奇异值、求解线性方程组这样的问题。
BLAS/LAPACK既然是API规范,就应该有相应的实现,常用的有这些:
MKL,IntelMathKernelLibrary。用于Intel处理器的(注意和MKL-DNN库的区别,MKL-DNN是英特尔的一个独立的神经网络库:MKLforDeepNeuralNetworks);
ATLAS,AutomaticallyTunedLinearAlgebraSoftware,多平台的;
OpenBLAS,多平台的;
Accelerate,苹果macOS、iOS平台上的;
Eigen,这个库只有头文件,实现了BLAS和一部分LAPACK,集成到了PyTorch项目的thirdparty下;
cuBLAS,NVIDIA的BLAS实现,基于NVIDIA的GPU硬件(此外还有cuFFT、cuRAND,cuSPARSE等);
MAGMA,基于CUDA、HIP、IntelXeonPhi、OpenCL的BLAS/LAPACK实现;
还记得刚才提到过本文的目标是在上进行目标为x86_64Linux的构建,这种情况下,我们必须要使用一个LAPACK实现:openblas、MKL或者Eigen。否则,运行时会报错:“gels:Lapacklibrarynotfoundincompiletime”。本文使用MKL,并实验性质到介绍下如何使用Eigen。
02CMake编译开关默认值;
用户指定;
通过检测系统环境获得;
通过检测软件包的安装情况获得;
通过开关与开关之间的逻辑关系推导而来;
这些开关的值会影响:
中要添加的编译单元;
代码中的ifdef宏;
下面是一些主要的编译开关的初始值,这些值初始值要么为OFF,要么为ON,要么为空。如下所示:
1,默认关闭的
ATEN_NO_TEST,是否编译ATentestbinaries;
BUILD_BINARY,BuildC++binaries;
BUILD_DOCS,BuildCaffe2documentation;
BUILD_CAFFE2_MOBILE,Buildlibcaffe2formobile,也就是在libcaffe2和libtorchmobile中选择,目前已经废弃,默认使用libtorchmobile;
CAFFE2_USE_MSVC_STATIC_RUNTIME,UsingMSVCstaticruntimelibraries;
BUILD_TEST,BuildC++testbinaries(needgtestandgbenchmark);
BUILD_STATIC_RUNTIME_BENCHMARK,BuildC++binariesforstaticruntimebenchmarks(needgbenchmark);
BUILD_TENSOREXPR_BENCHMARK,BuildC++binariesfortensorexprbenchmarks(needgbenchmark);
BUILD_MOBILE_BENCHMARK,BuildC++testbinariesformobile(ARM)targets(needgtestandgbenchmark);
BUILD_MOBILE_TEST,BuildC++testbinariesformobile(ARM)targets(needgtestandgbenchmark);
BUILD_JNI,BuildJNIbindings;
BUILD_MOBILE_AUTOGRAD,Buildautogradfunctioninmobilebuild(正在开发中);
INSTALL_TEST,InstalltestbinariesifBUILD_TESTison;
USE_CPP_CODE_COVERAGE,CompileC/C++withcodecoverageflags;
USE_ASAN,UseAddressSanitizer;
USE_TSAN,UseThreadSanitizer;
CAFFE2_STATIC_LINK_CUDA,StaticallylinkCUDAlibraries;
USE_STATIC_CUDNN,UsecuDNNstaticlibraries;
USE_KINETO,UseKinetoprofilinglibrary;
USE_FAKELOWP,UseFakeLowpoperators;
USE_FFMPEG;
USE_GFLAGS;
USE_GLOG;
USE_LEVELDB;
USE_LITE_PROTO,Useliteprotobufinsteadoffull;
USE_LMDB;
USE_PYTORCH_METAL,UseMetalforPyTorchiOSbuild;
USE_NATIVE_ARCH,Use-march=native;
USE_STATIC_NCCL;
USE_SYSTEM_NCCL,Usesystem-wideNCCL;
USE_NNAPI;
USE_NVRTC,_CUDAison;
USE_OBSERVERS,Useobserversmodule;
USE_OPENCL;
USE_OPENCV;
USE_PROF,Useprofiling;
USE_REDIS;
USE_ROCKSDB;
USE_SNPE,使用高通的神经网络引擎;
USE_SYSTEM_EIGEN_INSTALL,UsesystemEigeninsteadoftheoneunderthird_party;
USE_TENSORRT,UsingNvidiaTensorRTlibrary;
USE_VULKAN,UseVulkanGPUback;
USE_VULKAN_API,UseVulkanGPUbackv2;
USE_VULKAN_SHADERC_RUNTIME,UseVulkanShadercompilationruntime(Needsshaderclib);
USE_VULKAN_RELAXED_PRECISION,UseVulkanrelaxedprecision(mediump);
USE_ZMQ;
USE_ZSTD;
USE_MKLDNN_CBLAS,UseCBLASinMKLDNN;
USE_TBB;
HAVE_SOVERSION,WhethertoaddSOVERSIONtothesharedobjects;
USE_SYSTEM_LIBS,Useallavailablesystem-providedlibraries;
USE_SYSTEM_CPUINFO,Usesystem-providedcpuinfo;
USE_SYSTEM_SLEEF,Usesystem-providedsleef;
USE_SYSTEM_GLOO,Usesystem-providedgloo;
USE_SYSTEM_FP16,Usesystem-providedfp16;
USE_SYSTEM_PTHREADPOOL,Usesystem-providedpthreadpool;
USE_SYSTEM_PSIMD,Usesystem-providedpsimd;
USE_SYSTEM_FXDIV,Usesystem-providedfxdiv;
USE_SYSTEM_BENCHMARK,Usesystem-providedgooglebenchmark;
USE_SYSTEM_ONNX,Usesystem-providedonnx;
USE_SYSTEM_XNNPACK,Usesystem-providedxnnpack。
2,默认打开的
BUILD_CUSTOM_PROTOBUF,BuildanduseCaffe2'sownprotobufunderthird_party;
BUILD_PYTHON,BuildPythonbinaries;
BUILD_CAFFE2,MasterflagtobuildCaffe2;
BUILD_CAFFE2_OPS,BuildCaffe2operators;
BUILD_SHARED_LIBS,;
CAFFE2_LINK_LOCAL_PROTOBUF,Ifset,;
COLORIZE_OUTPUT,Colorizeoutputduringcompilation;
USE_CUDA;
USE_CUDNN;
USE_ROCM;
USE_FBGEMM,UseFBGEMM(quantized8-bitserveroperators);
USE_METAL,UseMetalforCaffe2iOSbuild;
USE_NCCL,须在UNIX上,且USE_CUDA或USE_ROCM是打开的;
USE_NNPACK;
USE_NUMPY;
USE_OPENMP,UseOpenMPforparallelcode;
USE_QNNPACK;UseQNNPACK(quantized8-bitoperators);
USE_PYTORCH_QNNPACK,UseATen/QNNPACK(quantized8-bitoperators);
USE_VULKAN_WRAPPER,UseVulkanwrapper;
USE_XNNPACK,
USE_DISTRIBUTED;
USE_MPI,_DISTRIBUTEDison;
USE_GLOO,OnlyavailableifUSE_DISTRIBUTEDison;
USE_TENSORPIPE,OnlyavailableifUSE_DISTRIBUTEDison;
ONNX_ML,EnabletraditionalONNXMLAPI;
USE_NUMA;
USE_VALGRIND;
USE_MKLDNN;
BUILDING_WITH_TORCH_LIBS,TellcmakeifCaffe2isbeingbuiltalongsidetorchlibs。
3,默认为空的
SELECTED_OP_LIST,Pathtot;
OP_DEPENDENCY,Pathtotheyamlfilethatcontainstheopdepencygraphforcustombuild。
03CMake编译开关的平台修正编译开关的初始值并不是一成不变的,即使没有用户的手工指定,那么CMake也会通过检测硬件环境、系统环境、包依赖来进行修改。比如下面这样:
1,操作系统修正
USE_DISTRIBUTED,如果不是Linux/Win32,则关闭;
USE_LIBUV,macOS上,且手工打开USE_DISTRIBUTED,则打开;
USE_NUMA,如果不是Linux,则关闭;
USE_VALGRIND,如果不是Linux,则关闭;
USE_TENSORPIPE,如果是Windows,则关闭;
USE_KINETO,如果是windows,则关闭;
如果是构建Android、iOS等移动平台上的libtorch,则:
set(BUILD_PYTHONOFF)set(BUILD_CAFFE2_OPSOFF)set(USE_DISTRIBUTEDOFF)set(FEATURE_TORCH_MOBILEON)set(NO_APION)set(USE_FBGEMMOFF)set(USE_QNNPACKOFF)set(INTERN_DISABLE_ONNXON)set(INTERN_USE_EIGEN_BLASON)set(INTERN_DISABLE_MOBILE_INTERPON)
2,CPU架构修正
USE_MKLDNN,如果不是64位x86_64,则关闭;
USE_FBGEMM,如果不是64位x86_64,则关闭;如果不支持AVX512指令集,则关闭;
USE_KINETO,如果是手机平台,则关闭;
USE_GLOO,如果不是64位x86_64,则关闭;
3,软件包依赖修正
USE_DISTRIBUTED,在Windows上,如果找不到libuv,则关闭;
USE_GLOO,在Windows上,如果找不到libuv,则关闭;
USE_KINETO,如果没有USE_CUDA,则关闭;
MKL相关,不再赘述;
NNPACK家族相关的((QNNPACK,PYTORCH_QNNPACK,XNNPACK)),不再赘述;
USE_BLAS,会被相关依赖修正;
USE_PTHREADPOOL,会被相关依赖修正;
USE_LAPACK,如果LAPACK包不能被找到,则关闭;且运行时会导致出错:“gels:Lapacklibrarynotfoundincompiletime”;
4,用户手工指令的修正
如果手工打开了USE_SYSTEM_LIBS,则:
set(USE_SYSTEM_CPUINFOON)set(USE_SYSTEM_SLEEFON)set(USE_SYSTEM_GLOOON)set(BUILD_CUSTOM_PROTOBUFOFF)set(USE_SYSTEM_EIGEN_INSTALLON)set(USE_SYSTEM_FP16ON)set(USE_SYSTEM_PTHREADPOOLON)set(USE_SYSTEM_PSIMDON)set(USE_SYSTEM_FXDIVON)set(USE_SYSTEM_BENCHMARKON)set(USE_SYSTEM_ONNXON)set(USE_SYSTEM_XNNPACKON)
如果设置环境变量BUILD_PYTORCH_MOBILE_WITH_HOST_TOOLCHAIN,则set(INTERN_BUILD_MOBILEON),而INTERN_BUILD_MOBILE一旦打开,则:
pip3installsetuptoolsroot@gemfield:~pip3installdataclasses04yTorch官方预编译动态库的编译选项
如果对官方编译的库所选用的编译开关感兴趣的话,可以使用如下的python命令获得这些信息:
print(torch.__config__.show())PyTorchbuiltwith:-++Version:201402-Intel(R)(R)64architectureapplications-Intel(R)(GitHashe2ac1fac44c5078ca927cb9b90e1b3066a0b2ed0)-OpenMP201511()-NNPACKisenabled-CPUcapabilityusage::-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-: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=OFF05编译最小尺度的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授权(IntelSimplifiedSoftwareLicense):
root@gemfield:~aptupdateroot@gemfield:~/opt/intel/mkl/lib/intel64/libmkl_/opt/intel/mkl/lib/intel64/libmkl_gnu_/opt/intel/mkl/lib/intel64/libmkl_
以及依赖系统上的这几个共享库:
06编译最小尺度的CPU版本静态库(Eigen后端)在这个最小尺度的CPU版本里,Gemfield将会选择Eigen来作为LAPACK的实现。使用Eigen来作为LAPACK实现的话,需要打开INTERN_USE_EIGEN_BLAS。看见INTERN前缀了吧,这提示我们不应该这样来使用这个开关。并且由于在PyTorch中,Eigen是为mobile平台而设计使用的,因此要想在x86_64Linux使用,就需要改下pytorch仓库中的CMake文件:一共2处。
1,修改PyTorch的CMake文件
第一处,修改cmake/:
set(AT_MKL_ENABLED0)set(USE_BLAS1)message(WARNING"PreferredBLAS("${BLAS}")cannotbefound,nowsearchingforageneralBLASlibrary")if(NOTBLAS_FOUND)if()add_definitions(-DTH_BLAS_MKL)add_definitions(-DTH_BLAS_MKL_SEQ=1)if(MSVCANDMKL_LIBRARIESMATCHES".*libiomp5md\\.lib.*")set(AT_MKL_MT1)set(AT_MKL_ENABLED1)EigenBLASforMobileset(USE_BLAS1)set(AT_MKL_ENABLED0)include(${CMAKE_CURRENT_LIST_DIR}/External/)list(APPENDCaffe2_DEPENDENCY_LIBSeigen_blas)if()第二处,修改
cmake/External/:
root@gemfield:~cuda9exportTORCH_CUDA_ARCH_LIST="3.5;5.0;5.2;6.0;6.1;7.0;7.0+PTX"cuda11exportTORCH_CUDA_ARCH_LIST="3.5;5.0;5.2;6.0;6.1;7.0;7.5;8.0;8.0+PTX"exportLIBRARY_PATH=$LIBRARY_PATH:/usr/local//targets/x86_64-linux/lib/
总之,你如果使用了libdeepvac封装的话(需要打开USE_CUDA),就没有这么多问题了。
5,性能
做了一个快速的不严谨的推理性能测试。使用了cuda版本后,还是之前的那个系统,还是之前那个测试任务(对20个目标进行CNN+LSTM的计算下),CUDA版本消耗了不到一秒。
在你使用pytorch静态库的时候,或多或少还会遇到一些问题。但是,何必自讨苦吃呢,使用我们封装了libtorch的libdeepvac库吧:





