From fb8be035ec9f4e55db4deb62cf9e6de8fabdb6eb Mon Sep 17 00:00:00 2001 From: Zhou Wei <1183042833@qq.com> Date: Wed, 22 Sep 2021 16:30:04 +0800 Subject: [PATCH] [cherry-pick2.2]support extern third_party lapack API on Linux/Windows/Mac (#35897) ATT, cherry-pick #35690 --- cmake/external/lapack.cmake | 70 +++++++++++++++++++ cmake/third_party.cmake | 3 +- paddle/fluid/operators/CMakeLists.txt | 2 +- paddle/fluid/operators/math/CMakeLists.txt | 1 + .../fluid/operators/math/lapack_function.cc | 35 ++++++++++ paddle/fluid/operators/math/lapack_function.h | 27 +++++++ paddle/fluid/platform/dynload/CMakeLists.txt | 3 + .../fluid/platform/dynload/dynamic_loader.cc | 12 ++++ .../fluid/platform/dynload/dynamic_loader.h | 1 + paddle/fluid/platform/dynload/lapack.cc | 31 ++++++++ paddle/fluid/platform/dynload/lapack.h | 69 ++++++++++++++++++ python/setup.py.in | 23 ++++-- 12 files changed, 271 insertions(+), 6 deletions(-) create mode 100644 cmake/external/lapack.cmake create mode 100644 paddle/fluid/operators/math/lapack_function.cc create mode 100644 paddle/fluid/operators/math/lapack_function.h create mode 100644 paddle/fluid/platform/dynload/lapack.cc create mode 100644 paddle/fluid/platform/dynload/lapack.h diff --git a/cmake/external/lapack.cmake b/cmake/external/lapack.cmake new file mode 100644 index 00000000000..9f690b3808b --- /dev/null +++ b/cmake/external/lapack.cmake @@ -0,0 +1,70 @@ +# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +INCLUDE (ExternalProject) + +SET(LAPACK_PREFIX_DIR ${THIRD_PARTY_PATH}/lapack) +SET(LAPACK_SOURCE_DIR ${THIRD_PARTY_PATH}/lapack/src/extern_lapack) +SET(LAPACK_INSTALL_DIR ${THIRD_PARTY_PATH}/install/lapack) +SET(LAPACK_INCLUDE_DIR ${LAPACK_SOURCE_DIR}) +SET(LAPACK_LIB_DIR ${LAPACK_INSTALL_DIR}/lib) + +# Note(zhouwei): lapack need fortan compiler which many machines don't have, so use precompiled library. +# use lapack tag v3.10.0 on 06/28/2021 https://github.com/Reference-LAPACK/lapack +if(LINUX) + SET(LAPACK_VER "lapack_lnx_v3.10.0.20210628" CACHE STRING "" FORCE) + SET(LAPACK_URL "https://paddlepaddledeps.bj.bcebos.com/${LAPACK_VER}.tar.gz" CACHE STRING "" FORCE) + SET(LAPACK_URL_MD5 71f8cc8237a8571692f3e07f9a4f25f6) + SET(GNU_RT_LIB_1 "${LAPACK_LIB_DIR}/libquadmath.so.0") + SET(GFORTRAN_LIB "${LAPACK_LIB_DIR}/libgfortran.so.3") + SET(BLAS_LIB "${LAPACK_LIB_DIR}/libblas.so.3") + SET(LAPACK_LIB "${LAPACK_LIB_DIR}/liblapack.so.3") +elseif(WIN32) + # Refer to [lapack-for-windows] http://icl.cs.utk.edu/lapack-for-windows/lapack/#lapacke + SET(LAPACK_VER "lapack_win_v3.10.0.20210628" CACHE STRING "" FORCE) + SET(LAPACK_URL "https://paddlepaddledeps.bj.bcebos.com/${LAPACK_VER}.zip" CACHE STRING "" FORCE) + SET(LAPACK_URL_MD5 590d080392dcd5abbd5dca767a50b63a) + SET(GNU_RT_LIB_1 "${LAPACK_LIB_DIR}/libquadmath-0.dll") + SET(GNU_RT_LIB_2 "${LAPACK_LIB_DIR}/libgcc_s_seh-1.dll") + SET(GFORTRAN_LIB "${LAPACK_LIB_DIR}/libgfortran-3.dll") + SET(BLAS_LIB "${LAPACK_LIB_DIR}/libblas.dll") + SET(LAPACK_LIB "${LAPACK_LIB_DIR}/liblapack.dll") +else() + SET(LAPACK_VER "lapack_mac_v3.10.0.20210628" CACHE STRING "" FORCE) + SET(LAPACK_URL "https://paddlepaddledeps.bj.bcebos.com/${LAPACK_VER}.tar.gz" CACHE STRING "" FORCE) + SET(LAPACK_URL_MD5 427aecf8dee8523de3566ca8e47944d7) + SET(GNU_RT_LIB_1 "${LAPACK_LIB_DIR}/libquadmath.0.dylib") + SET(GNU_RT_LIB_2 "${LAPACK_LIB_DIR}/libgcc_s.1.dylib") + SET(GFORTRAN_LIB "${LAPACK_LIB_DIR}/libgfortran.5.dylib") + SET(BLAS_LIB "${LAPACK_LIB_DIR}/libblas.3.dylib") + SET(LAPACK_LIB "${LAPACK_LIB_DIR}/liblapack.3.dylib") +endif() + +ExternalProject_Add( + extern_lapack + ${EXTERNAL_PROJECT_LOG_ARGS} + URL ${LAPACK_URL} + URL_MD5 ${LAPACK_URL_MD5} + PREFIX ${LAPACK_PREFIX_DIR} + DOWNLOAD_DIR ${LAPACK_SOURCE_DIR} + DOWNLOAD_NO_PROGRESS 1 + PATCH_COMMAND "" + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory ${LAPACK_SOURCE_DIR} ${LAPACK_LIB_DIR} + BUILD_BYPRODUCTS ${BLAS_LIB} + BUILD_BYPRODUCTS ${LAPACK_LIB} +) + diff --git a/cmake/third_party.cmake b/cmake/third_party.cmake index 6487b5062c4..44463f29923 100644 --- a/cmake/third_party.cmake +++ b/cmake/third_party.cmake @@ -210,9 +210,10 @@ include(external/threadpool)# download threadpool include(external/dlpack) # download dlpack include(external/xxhash) # download, build, install xxhash include(external/warpctc) # download, build, install warpctc +include(external/lapack) # download, build, install lapack list(APPEND third_party_deps extern_eigen3 extern_gflags extern_glog extern_boost extern_xxhash) -list(APPEND third_party_deps extern_zlib extern_dlpack extern_warpctc extern_threadpool) +list(APPEND third_party_deps extern_zlib extern_dlpack extern_warpctc extern_threadpool extern_lapack) include(cblas) # find first, then download, build, install openblas diff --git a/paddle/fluid/operators/CMakeLists.txt b/paddle/fluid/operators/CMakeLists.txt index b9025560f6d..d641c5c1354 100644 --- a/paddle/fluid/operators/CMakeLists.txt +++ b/paddle/fluid/operators/CMakeLists.txt @@ -118,7 +118,7 @@ endif() cc_library(common_infer_shape_functions SRCS common_infer_shape_functions.cc DEPS operator) -set(COMMON_OP_DEPS ${COMMON_OP_DEPS} selected_rows_functor selected_rows +set(COMMON_OP_DEPS ${COMMON_OP_DEPS} selected_rows_functor selected_rows lapack_function lod_tensor maxouting unpooling pooling lod_rank_table context_project sequence_pooling segment_pooling executor device_memory_aligment generator) set(COMMON_OP_DEPS ${COMMON_OP_DEPS} dynload_warpctc) diff --git a/paddle/fluid/operators/math/CMakeLists.txt b/paddle/fluid/operators/math/CMakeLists.txt index 25cea2a6711..d8d79bc4086 100644 --- a/paddle/fluid/operators/math/CMakeLists.txt +++ b/paddle/fluid/operators/math/CMakeLists.txt @@ -78,6 +78,7 @@ else() math_library(beam_search DEPS math_function) endif() math_library(fc DEPS blas) +math_library(lapack_function DEPS dynload_lapack) math_library(matrix_bit_code) diff --git a/paddle/fluid/operators/math/lapack_function.cc b/paddle/fluid/operators/math/lapack_function.cc new file mode 100644 index 00000000000..54033a444a6 --- /dev/null +++ b/paddle/fluid/operators/math/lapack_function.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "paddle/fluid/operators/math/lapack_function.h" +#include "paddle/fluid/platform/dynload/lapack.h" + +namespace paddle { +namespace operators { +namespace math { + +// LU (for example) +template <> +void lapackLu(int m, int n, double *a, int lda, int *ipiv, int *info) { + platform::dynload::dgetrf_(&m, &n, a, &lda, ipiv, info); +} + +template <> +void lapackLu(int m, int n, float *a, int lda, int *ipiv, int *info) { + platform::dynload::sgetrf_(&m, &n, a, &lda, ipiv, info); +} + +} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/fluid/operators/math/lapack_function.h b/paddle/fluid/operators/math/lapack_function.h new file mode 100644 index 00000000000..694da4603ba --- /dev/null +++ b/paddle/fluid/operators/math/lapack_function.h @@ -0,0 +1,27 @@ +// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +namespace paddle { +namespace operators { +namespace math { + +// LU (for example) +template +void lapackLu(int m, int n, T *a, int lda, int *ipiv, int *info); + +} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/fluid/platform/dynload/CMakeLists.txt b/paddle/fluid/platform/dynload/CMakeLists.txt index eed3568f1d8..c0d4b349a9e 100644 --- a/paddle/fluid/platform/dynload/CMakeLists.txt +++ b/paddle/fluid/platform/dynload/CMakeLists.txt @@ -45,4 +45,7 @@ endif() if (WITH_MKLML) cc_library(dynload_mklml SRCS mklml.cc DEPS dynamic_loader mklml) endif() + +cc_library(dynload_lapack SRCS lapack.cc DEPS dynamic_loader) +add_dependencies(dynload_lapack extern_lapack) # TODO(TJ): add iomp, mkldnn? diff --git a/paddle/fluid/platform/dynload/dynamic_loader.cc b/paddle/fluid/platform/dynload/dynamic_loader.cc index bf2dc7aaba1..a83f085f7d2 100644 --- a/paddle/fluid/platform/dynload/dynamic_loader.cc +++ b/paddle/fluid/platform/dynload/dynamic_loader.cc @@ -51,6 +51,8 @@ DEFINE_string( DEFINE_string(mklml_dir, "", "Specify path for loading libmklml_intel.so."); +DEFINE_string(lapack_dir, "", "Specify path for loading liblapack.so."); + DEFINE_string(op_dir, "", "Specify path for loading user-defined op library."); #ifdef PADDLE_WITH_HIP @@ -478,6 +480,16 @@ void* GetMKLMLDsoHandle() { #endif } +void* GetLAPACKDsoHandle() { +#if defined(__APPLE__) || defined(__OSX__) + return GetDsoHandleFromSearchPath(FLAGS_lapack_dir, "liblapack.3.dylib"); +#elif defined(_WIN32) + return GetDsoHandleFromSearchPath(FLAGS_lapack_dir, "liblapack.dll"); +#else + return GetDsoHandleFromSearchPath(FLAGS_lapack_dir, "liblapack.so.3"); +#endif +} + void* GetOpDsoHandle(const std::string& dso_name) { return GetDsoHandleFromSearchPath(FLAGS_op_dir, dso_name); } diff --git a/paddle/fluid/platform/dynload/dynamic_loader.h b/paddle/fluid/platform/dynload/dynamic_loader.h index 08f0aec8b01..82c36d9e224 100644 --- a/paddle/fluid/platform/dynload/dynamic_loader.h +++ b/paddle/fluid/platform/dynload/dynamic_loader.h @@ -39,6 +39,7 @@ void* GetNCCLDsoHandle(); void* GetHCCLDsoHandle(); void* GetTensorRtDsoHandle(); void* GetMKLMLDsoHandle(); +void* GetLAPACKDsoHandle(); void* GetOpDsoHandle(const std::string& dso_name); void* GetNvtxDsoHandle(); void* GetCUFFTDsoHandle(); diff --git a/paddle/fluid/platform/dynload/lapack.cc b/paddle/fluid/platform/dynload/lapack.cc new file mode 100644 index 00000000000..eeebe240874 --- /dev/null +++ b/paddle/fluid/platform/dynload/lapack.cc @@ -0,0 +1,31 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/fluid/platform/dynload/lapack.h" +#include + +namespace paddle { +namespace platform { +namespace dynload { + +std::once_flag lapack_dso_flag; +void* lapack_dso_handle = nullptr; + +#define DEFINE_WRAP(__name) DynLoad__##__name __name + +LAPACK_ROUTINE_EACH(DEFINE_WRAP); + +} // namespace dynload +} // namespace platform +} // namespace paddle diff --git a/paddle/fluid/platform/dynload/lapack.h b/paddle/fluid/platform/dynload/lapack.h new file mode 100644 index 00000000000..ffb3d3e0f67 --- /dev/null +++ b/paddle/fluid/platform/dynload/lapack.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include +#include "paddle/fluid/platform/dynload/dynamic_loader.h" +#include "paddle/fluid/platform/port.h" + +// Note(zhouwei): because lapack doesn't provide appropriate header file. +// should expose API statement yourself. + +// getrf_(For example) +extern "C" void dgetrf_(int *m, int *n, double *a, int *lda, int *ipiv, + int *info); +extern "C" void sgetrf_(int *m, int *n, float *a, int *lda, int *ipiv, + int *info); + +namespace paddle { +namespace platform { +namespace dynload { + +extern std::once_flag lapack_dso_flag; +extern void *lapack_dso_handle; + +/** + * The following macro definition can generate structs + * (for each function) to dynamic load lapack routine + * via operator overloading. + */ +#define DYNAMIC_LOAD_LAPACK_WRAP(__name) \ + struct DynLoad__##__name { \ + template \ + auto operator()(Args... args) -> DECLARE_TYPE(__name, args...) { \ + using lapackFunc = decltype(&::__name); \ + std::call_once(lapack_dso_flag, []() { \ + lapack_dso_handle = paddle::platform::dynload::GetLAPACKDsoHandle(); \ + }); \ + static void *p_##_name = dlsym(lapack_dso_handle, #__name); \ + return reinterpret_cast(p_##_name)(args...); \ + } \ + }; \ + extern DynLoad__##__name __name + +#define DECLARE_DYNAMIC_LOAD_LAPACK_WRAP(__name) \ + DYNAMIC_LOAD_LAPACK_WRAP(__name) + +#define LAPACK_ROUTINE_EACH(__macro) \ + __macro(dgetrf_); \ + __macro(sgetrf_); + +LAPACK_ROUTINE_EACH(DECLARE_DYNAMIC_LOAD_LAPACK_WRAP); + +#undef DYNAMIC_LOAD_LAPACK_WRAP + +} // namespace dynload +} // namespace platform +} // namespace paddle diff --git a/python/setup.py.in b/python/setup.py.in index 1b2897f230f..d78d91a1d41 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -306,6 +306,19 @@ package_data['paddle.libs']= [] package_data['paddle.libs']=[('libwarpctc' if os.name != 'nt' else 'warpctc') + ext_name] shutil.copy('${WARPCTC_LIBRARIES}', libs_path) +package_data['paddle.libs']+=[ + os.path.basename('${LAPACK_LIB}'), + os.path.basename('${BLAS_LIB}'), + os.path.basename('${GFORTRAN_LIB}'), + os.path.basename('${GNU_RT_LIB_1}')] +shutil.copy('${BLAS_LIB}', libs_path) +shutil.copy('${LAPACK_LIB}', libs_path) +shutil.copy('${GFORTRAN_LIB}', libs_path) +shutil.copy('${GNU_RT_LIB_1}', libs_path) +if not sys.platform.startswith("linux"): + package_data['paddle.libs']+=[os.path.basename('${GNU_RT_LIB_2}')] + shutil.copy('${GNU_RT_LIB_2}', libs_path) + if '${WITH_MKL}' == 'ON': shutil.copy('${MKLML_SHARED_LIB}', libs_path) shutil.copy('${MKLML_SHARED_IOMP_LIB}', libs_path) @@ -385,13 +398,15 @@ if '${CMAKE_BUILD_TYPE}' == 'Release': if os.name != 'nt': # only change rpath in Release mode, since in Debug mode, ${FLUID_CORE_NAME}.xx is too large to be changed. if "@APPLE@" == "1": - command = "install_name_tool -id \"@loader_path/../libs/\" ${PADDLE_BINARY_DIR}/python/paddle/fluid/${FLUID_CORE_NAME}" + '.so' + commands = ["install_name_tool -id '@loader_path/../libs/' ${PADDLE_BINARY_DIR}/python/paddle/fluid/${FLUID_CORE_NAME}" + '.so'] + commands.append("install_name_tool -add_rpath '@loader_path/../libs/' ${PADDLE_BINARY_DIR}/python/paddle/fluid/${FLUID_CORE_NAME}" + '.so') else: - command = "patchelf --set-rpath '$ORIGIN/../libs/' ${PADDLE_BINARY_DIR}/python/paddle/fluid/${FLUID_CORE_NAME}" + '.so' + commands = ["patchelf --set-rpath '$ORIGIN/../libs/' ${PADDLE_BINARY_DIR}/python/paddle/fluid/${FLUID_CORE_NAME}" + '.so'] # The sw_64 not suppot patchelf, so we just disable that. if platform.machine() != 'sw_64' and platform.machine() != 'mips64': - if os.system(command) != 0: - raise Exception("patch ${FLUID_CORE_NAME}.%s failed, command: %s" % (ext_name, command)) + for command in commands: + if os.system(command) != 0: + raise Exception("patch ${FLUID_CORE_NAME}.%s failed, command: %s" % (ext_name, command)) ext_modules = [Extension('_foo', ['stub.cc'])] if os.name == 'nt': -- GitLab