dynamic_loader.cc 16.3 KB
Newer Older
1
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
Q
qijun 已提交
2 3 4 5 6 7 8 9 10 11 12 13

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. */
Y
Yi Wang 已提交
14
#include "paddle/fluid/platform/dynload/dynamic_loader.h"
15

Q
qijun 已提交
16
#include <string>
17
#include <vector>
18

Q
qijun 已提交
19 20
#include "gflags/gflags.h"
#include "glog/logging.h"
21
#include "paddle/fluid/platform/dynload/cupti_lib_path.h"
Y
Yi Wang 已提交
22
#include "paddle/fluid/platform/enforce.h"
Q
qijun 已提交
23

24 25 26 27




Q
qijun 已提交
28 29 30 31 32 33 34
DEFINE_string(cudnn_dir, "",
              "Specify path for loading libcudnn.so. For instance, "
              "/usr/local/cudnn/lib. If empty [default], dlopen "
              "will search cudnn from LD_LIBRARY_PATH");

DEFINE_string(cuda_dir, "",
              "Specify path for loading cuda library, such as libcublas, "
35 36
              "libcurand, libcusolver. For instance, /usr/local/cuda/lib64. "
              "If default, dlopen will search cuda from LD_LIBRARY_PATH");
Q
qijun 已提交
37

Y
Yu Yang 已提交
38
DEFINE_string(nccl_dir, "",
39 40
              "Specify path for loading nccl library, such as libnccl.so. "
              "For instance, /usr/local/cuda/lib64. If default, "
Y
Yu Yang 已提交
41 42
              "dlopen will search cuda from LD_LIBRARY_PATH");

43 44 45 46 47
DEFINE_string(hccl_dir, "",
              "Specify path for loading hccl library, such as libhccl.so. "
              "For instance, /usr/local/Ascend/ascend-toolkit/latest/fwkacllib/lib64/. If default, "
              "dlopen will search hccl from LD_LIBRARY_PATH");

48 49
DEFINE_string(cupti_dir, "", "Specify path for loading cupti.so.");

Y
Yan Chunwei 已提交
50 51 52 53
DEFINE_string(
    tensorrt_dir, "",
    "Specify path for loading tensorrt library, such as libnvinfer.so.");

54 55
DEFINE_string(mklml_dir, "", "Specify path for loading libmklml_intel.so.");

56 57
DEFINE_string(op_dir, "", "Specify path for loading user-defined op library.");

Y
Y_Xuan 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
#ifdef PADDLE_WITH_HIP

DEFINE_string(miopen_dir, "",
              "Specify path for loading libMIOpen.so. For instance, "
              "/opt/rocm/miopen/lib. If empty [default], dlopen "
              "will search miopen from LD_LIBRARY_PATH");

DEFINE_string(rocm_dir, "",
              "Specify path for loading rocm library, such as librocblas, "
              "libcurand, libcusolver. For instance, /opt/rocm/lib. "
              "If default, dlopen will search rocm from LD_LIBRARY_PATH");

DEFINE_string(rccl_dir, "",
              "Specify path for loading rccl library, such as librccl.so. "
              "For instance, /opt/rocm/rccl/lib. If default, "
              "dlopen will search rccl from LD_LIBRARY_PATH");
#endif

Q
qijun 已提交
76 77
namespace paddle {
namespace platform {
Q
qijun 已提交
78
namespace dynload {
79 80 81 82 83 84

struct PathNode {
  PathNode() {}
  std::string path = "";
};

85
static constexpr char cupti_lib_path[] = CUPTI_LIB_PATH;
86

87 88 89 90 91 92
// NOTE: In order to adapt to the default installation path of cuda
#if defined(_WIN32) && defined(PADDLE_WITH_CUDA)
static constexpr char cuda_lib_path[] = CUDA_TOOLKIT_ROOT_DIR "/bin";
#else
static constexpr char cuda_lib_path[] = "/usr/local/cuda/lib64";
#endif
93 94

static PathNode s_py_site_pkg_path;
95

P
peizhilin 已提交
96
#if defined(_WIN32) && defined(PADDLE_WITH_CUDA)
Z
Zhou Wei 已提交
97
static constexpr char* win_cudnn_lib = "cudnn64_" CUDNN_MAJOR_VERSION ".dll";
98 99 100
static constexpr char* win_cublas_lib =
    "cublas64_" CUDA_VERSION_MAJOR CUDA_VERSION_MINOR
    ".dll;cublas64_" CUDA_VERSION_MAJOR ".dll";
Z
Zhou Wei 已提交
101 102 103 104 105 106 107 108
#if CUDA_VERSION >= 11000
static constexpr char* win_curand_lib =
    "curand64_" CUDA_VERSION_MAJOR CUDA_VERSION_MINOR
    ".dll;curand64_" CUDA_VERSION_MAJOR ".dll;curand64_10.dll";
static constexpr char* win_cusolver_lib =
    "cusolver64_" CUDA_VERSION_MAJOR CUDA_VERSION_MINOR
    ".dll;cusolver64_" CUDA_VERSION_MAJOR ".dll;cusolver64_10.dll";
#else
109 110 111
static constexpr char* win_curand_lib =
    "curand64_" CUDA_VERSION_MAJOR CUDA_VERSION_MINOR
    ".dll;curand64_" CUDA_VERSION_MAJOR ".dll";
112
static constexpr char* win_cusolver_lib =
113 114
    "cusolver64_" CUDA_VERSION_MAJOR CUDA_VERSION_MINOR
    ".dll;cusolver64_" CUDA_VERSION_MAJOR ".dll";
Z
Zhou Wei 已提交
115
#endif  // CUDA_VERSION
P
peizhilin 已提交
116 117
#endif

Q
qijun 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
static inline std::string join(const std::string& part1,
                               const std::string& part2) {
  // directory separator
  const char sep = '/';
  if (!part2.empty() && part2.front() == sep) {
    return part2;
  }
  std::string ret;
  ret.reserve(part1.size() + part2.size() + 1);
  ret = part1;
  if (!ret.empty() && ret.back() != sep) {
    ret += sep;
  }
  ret += part2;
  return ret;
}

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
static inline std::vector<std::string> split(
    const std::string& str, const std::string separator = " ") {
  std::vector<std::string> str_list;
  std::string::size_type firstPos;
  firstPos = str.find_first_not_of(separator, 0);
  std::string::size_type lastPos;
  lastPos = str.find_first_of(separator, firstPos);
  while (std::string::npos != firstPos && std::string::npos != lastPos) {
    str_list.push_back(str.substr(firstPos, lastPos - firstPos));
    firstPos = str.find_first_not_of(separator, lastPos);
    lastPos = str.find_first_of(separator, firstPos);
  }
  if (std::string::npos == lastPos) {
    str_list.push_back(str.substr(firstPos, lastPos - firstPos));
  }
  return str_list;
}

153 154 155 156 157
void SetPaddleLibPath(const std::string& py_site_pkg_path) {
  s_py_site_pkg_path.path = py_site_pkg_path;
  VLOG(3) << "Set paddle lib path : " << py_site_pkg_path;
}

158 159 160 161 162 163 164 165 166 167 168 169 170 171
static inline void* GetDsoHandleFromSpecificPath(const std::string& spec_path,
                                                 const std::string& dso_name,
                                                 int dynload_flags) {
  void* dso_handle = nullptr;
  if (!spec_path.empty()) {
    // search xxx.so from custom path
    VLOG(3) << "Try to find library: " << dso_name
            << " from specific path: " << spec_path;
    std::string dso_path = join(spec_path, dso_name);
    dso_handle = dlopen(dso_path.c_str(), dynload_flags);
  }
  return dso_handle;
}

172 173
static inline void* GetDsoHandleFromDefaultPath(const std::string& dso_path,
                                                int dynload_flags) {
Q
qijun 已提交
174
  // default search from LD_LIBRARY_PATH/DYLD_LIBRARY_PATH
175
  // and /usr/local/lib path
176
  void* dso_handle = dlopen(dso_path.c_str(), dynload_flags);
177 178
  VLOG(3) << "Try to find library: " << dso_path
          << " from default system path.";
Q
qijun 已提交
179

180
// TODO(chenweihang): This path is used to search which libs?
Q
qijun 已提交
181 182 183 184
// DYLD_LIBRARY_PATH is disabled after Mac OS 10.11 to
// bring System Integrity Projection (SIP), if dso_handle
// is null, search from default package path in Mac OS.
#if defined(__APPLE__) || defined(__OSX__)
185 186 187
  if (nullptr == dso_handle) {
    dso_handle =
        dlopen(join("/usr/local/cuda/lib/", dso_path).c_str(), dynload_flags);
Q
qijun 已提交
188 189
  }
#endif
190 191

  return dso_handle;
Q
qijun 已提交
192 193
}

194 195 196 197 198 199 200 201 202 203 204 205 206 207
/*
 * We define three priorities for dynamic library search:
 *
 * First: Search for the path specified by the user
 * Second: Search the system default path
 * Third: Search for a special path corresponding to
 *        a specific library to adapt to changes and easy to expand.
 */

static inline void* GetDsoHandleFromSearchPath(
    const std::string& config_path, const std::string& dso_name,
    bool throw_on_error = true,
    const std::vector<std::string>& extra_paths = std::vector<std::string>(),
    const std::string& warning_msg = std::string()) {
D
dzhwinter 已提交
208
#if !defined(_WIN32)
Q
qijun 已提交
209
  int dynload_flags = RTLD_LAZY | RTLD_LOCAL;
D
dzhwinter 已提交
210 211 212
#else
  int dynload_flags = 0;
#endif  // !_WIN32
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
  std::vector<std::string> dso_names = split(dso_name, ";");
  void* dso_handle = nullptr;
  for (auto dso : dso_names) {
    // 1. search in user config path by FLAGS
    dso_handle = GetDsoHandleFromSpecificPath(config_path, dso, dynload_flags);
    // 2. search in extra paths
    if (nullptr == dso_handle) {
      for (auto path : extra_paths) {
        VLOG(3) << "extra_paths: " << path;
        dso_handle = GetDsoHandleFromSpecificPath(path, dso, dynload_flags);
      }
    }
    // 3. search in system default path
    if (nullptr == dso_handle) {
      dso_handle = GetDsoHandleFromDefaultPath(dso, dynload_flags);
228
    }
229
    if (nullptr != dso_handle) break;
230
  }
Q
qijun 已提交
231

232
  // 4. [If Failed for All dso_names] logging warning if exists
233 234 235 236
  if (nullptr == dso_handle && !warning_msg.empty()) {
    LOG(WARNING) << warning_msg;
  }

237
  // 5. [If Failed for All dso_names] logging or throw error info
238 239
  if (nullptr == dso_handle) {
    auto error_msg =
240 241 242 243 244 245 246 247 248 249 250 251 252
        "The third-party dynamic library (%s) that Paddle depends on is not "
        "configured correctly. (error code is %s)\n"
        "  Suggestions:\n"
        "  1. Check if the third-party dynamic library (e.g. CUDA, CUDNN) "
        "is installed correctly and its version is matched with paddlepaddle "
        "you installed.\n"
        "  2. Configure third-party dynamic library environment variables as "
        "follows:\n"
        "  - Linux: set LD_LIBRARY_PATH by `export LD_LIBRARY_PATH=...`\n"
        "  - Windows: set PATH by `set PATH=XXX;%PATH%`\n"
        "  - Mac: set  DYLD_LIBRARY_PATH by `export DYLD_LIBRARY_PATH=...` "
        "[Note: After Mac OS 10.11, using the DYLD_LIBRARY_PATH is "
        "impossible unless System Integrity Protection (SIP) is disabled.]";
D
dzhwinter 已提交
253 254 255 256 257
#if !defined(_WIN32)
    auto errorno = dlerror();
#else
    auto errorno = GetLastError();
#endif  // !_WIN32
258 259
    if (throw_on_error) {
      // NOTE: Special error report case, no need to change its format
260 261
      PADDLE_THROW(
          platform::errors::PreconditionNotMet(error_msg, dso_name, errorno));
262 263
    } else {
      LOG(WARNING) << string::Sprintf(error_msg, dso_name, errorno);
Q
qijun 已提交
264 265
    }
  }
266 267

  return dso_handle;
Q
qijun 已提交
268 269
}

270
void* GetCublasDsoHandle() {
Q
qijun 已提交
271
#if defined(__APPLE__) || defined(__OSX__)
272
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libcublas.dylib");
P
peizhilin 已提交
273
#elif defined(_WIN32) && defined(PADDLE_WITH_CUDA)
274 275
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, win_cublas_lib, true,
                                    {cuda_lib_path});
Y
Y_Xuan 已提交
276 277
#elif PADDLE_WITH_HIP
  return GetDsoHandleFromSearchPath(FLAGS_rocm_dir, "librocblas.so");
Q
qijun 已提交
278
#else
279
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libcublas.so");
Q
qijun 已提交
280 281 282
#endif
}

283
void* GetCUDNNDsoHandle() {
Q
qijun 已提交
284
#if defined(__APPLE__) || defined(__OSX__)
285 286 287 288 289 290 291 292
  std::string mac_warn_meg(
      "Note: [Recommend] copy cudnn into /usr/local/cuda/ \n "
      "For instance, sudo tar -xzf "
      "cudnn-7.5-osx-x64-v5.0-ga.tgz -C /usr/local \n sudo "
      "chmod a+r /usr/local/cuda/include/cudnn.h "
      "/usr/local/cuda/lib/libcudnn*");
  return GetDsoHandleFromSearchPath(FLAGS_cudnn_dir, "libcudnn.dylib", false,
                                    {}, mac_warn_meg);
P
peizhilin 已提交
293
#elif defined(_WIN32) && defined(PADDLE_WITH_CUDA)
294 295 296 297 298
  std::string win_warn_meg(
      "Note: [Recommend] copy cudnn into CUDA installation directory. \n "
      "For instance, download cudnn-10.0-windows10-x64-v7.6.5.32.zip from "
      "NVIDIA's official website, \n"
      "then, unzip it and copy it into C:\\Program Files\\NVIDIA GPU Computing "
299
      "Toolkit\\CUDA\\v10.0\n"
300 301 302 303
      "You should do this according to your CUDA installation directory and "
      "CUDNN version.");
  return GetDsoHandleFromSearchPath(FLAGS_cudnn_dir, win_cudnn_lib, true,
                                    {cuda_lib_path}, win_warn_meg);
Y
Y_Xuan 已提交
304 305
#elif PADDLE_WITH_HIP
  return GetDsoHandleFromSearchPath(FLAGS_miopen_dir, "libMIOpen.so", false);
Q
qijun 已提交
306
#else
307
  return GetDsoHandleFromSearchPath(FLAGS_cudnn_dir, "libcudnn.so", false,
308
                                    {cuda_lib_path});
Q
qijun 已提交
309 310 311
#endif
}

312
void* GetCUPTIDsoHandle() {
313
#if defined(__APPLE__) || defined(__OSX__)
314 315
  return GetDsoHandleFromSearchPath(FLAGS_cupti_dir, "libcupti.dylib", false,
                                    {cupti_lib_path});
316
#else
317 318
  return GetDsoHandleFromSearchPath(FLAGS_cupti_dir, "libcupti.so", false,
                                    {cupti_lib_path});
319 320 321
#endif
}

322
void* GetCurandDsoHandle() {
Q
qijun 已提交
323
#if defined(__APPLE__) || defined(__OSX__)
324
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libcurand.dylib");
P
peizhilin 已提交
325
#elif defined(_WIN32) && defined(PADDLE_WITH_CUDA)
326 327
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, win_curand_lib, true,
                                    {cuda_lib_path});
Y
Y_Xuan 已提交
328 329
#elif PADDLE_WITH_HIP
  return GetDsoHandleFromSearchPath(FLAGS_rocm_dir, "libhiprand.so");
Q
qijun 已提交
330
#else
331
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libcurand.so");
Q
qijun 已提交
332 333 334
#endif
}

G
Guo Sheng 已提交
335 336 337 338
void* GetCusolverDsoHandle() {
#if defined(__APPLE__) || defined(__OSX__)
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libcusolver.dylib");
#elif defined(_WIN32) && defined(PADDLE_WITH_CUDA)
339 340
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, win_cusolver_lib, true,
                                    {cuda_lib_path});
G
Guo Sheng 已提交
341 342 343 344 345
#else
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libcusolver.so");
#endif
}

346 347
void* GetNVRTCDsoHandle() {
#if defined(__APPLE__) || defined(__OSX__)
348
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libnvrtc.dylib", false);
Y
Y_Xuan 已提交
349 350
#elif PADDLE_WITH_HIP
  return GetDsoHandleFromSearchPath(FLAGS_rocm_dir, "libhiprtc.so");
351
#else
352
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libnvrtc.so", false);
353 354 355 356 357
#endif
}

void* GetCUDADsoHandle() {
#if defined(__APPLE__) || defined(__OSX__)
358
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libcuda.dylib", false);
Y
Y_Xuan 已提交
359 360
#elif PADDLE_WITH_HIP
  return GetDsoHandleFromSearchPath(FLAGS_rocm_dir, "libhip_hcc.so");
361
#else
362
  return GetDsoHandleFromSearchPath(FLAGS_cuda_dir, "libcuda.so", false);
363 364 365
#endif
}

366
void* GetWarpCTCDsoHandle() {
367 368 369
  std::string warpctc_dir = "";
  if (!s_py_site_pkg_path.path.empty()) {
    warpctc_dir = s_py_site_pkg_path.path;
370
  }
Q
qijun 已提交
371
#if defined(__APPLE__) || defined(__OSX__)
372
  return GetDsoHandleFromSearchPath(warpctc_dir, "libwarpctc.dylib");
P
peizhilin 已提交
373
#elif defined(_WIN32)
374
  return GetDsoHandleFromSearchPath(warpctc_dir, "warpctc.dll");
Q
qijun 已提交
375
#else
376
  return GetDsoHandleFromSearchPath(warpctc_dir, "libwarpctc.so");
Q
qijun 已提交
377 378 379
#endif
}

380
void* GetNCCLDsoHandle() {
381 382 383 384
  std::string warning_msg(
      "You may need to install 'nccl2' from NVIDIA official website: "
      "https://developer.nvidia.com/nccl/nccl-download"
      "before install PaddlePaddle.");
Y
Yu Yang 已提交
385
#if defined(__APPLE__) || defined(__OSX__)
386 387
  return GetDsoHandleFromSearchPath(FLAGS_nccl_dir, "libnccl.dylib", true, {},
                                    warning_msg);
Y
Y_Xuan 已提交
388 389
#elif defined(PADDLE_WITH_HIP) && defined(PADDLE_WITH_RCCL)
  return GetDsoHandleFromSearchPath(FLAGS_rccl_dir, "librccl.so", true);
Y
Yu Yang 已提交
390
#else
391 392
  return GetDsoHandleFromSearchPath(FLAGS_nccl_dir, "libnccl.so", true, {},
                                    warning_msg);
Y
Yu Yang 已提交
393 394
#endif
}
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
void* GetHCCLDsoHandle() {
  std::string warning_msg(
      "You may need to install 'hccl2' from Huawei official website: "
      "before install PaddlePaddle.");
#if defined(__APPLE__) || defined(__OSX__)
  return GetDsoHandleFromSearchPath(FLAGS_nccl_dir, "libnccl.dylib", true, {},
                                    warning_msg);
#elif defined(PADDLE_WITH_HIP) && defined(PADDLE_WITH_RCCL)
  return GetDsoHandleFromSearchPath(FLAGS_rccl_dir, "librccl.so", true);

#elif defined(PADDLE_WITH_ASCEND_CL)
  return GetDsoHandleFromSearchPath(FLAGS_hccl_dir, "libhccl.so", true, {},
                                    warning_msg);
#else
  return GetDsoHandleFromSearchPath(FLAGS_nccl_dir, "libnccl.so", true, {},
                                    warning_msg);
#endif
}


Y
Yu Yang 已提交
415

Y
Yan Chunwei 已提交
416 417 418
void* GetTensorRtDsoHandle() {
#if defined(__APPLE__) || defined(__OSX__)
  return GetDsoHandleFromSearchPath(FLAGS_tensorrt_dir, "libnvinfer.dylib");
419 420
#elif defined(_WIN32)
  return GetDsoHandleFromSearchPath(FLAGS_mklml_dir, "nvinfer.dll");
Y
Yan Chunwei 已提交
421 422 423 424 425
#else
  return GetDsoHandleFromSearchPath(FLAGS_tensorrt_dir, "libnvinfer.so");
#endif
}

426 427 428
void* GetMKLMLDsoHandle() {
#if defined(__APPLE__) || defined(__OSX__)
  return GetDsoHandleFromSearchPath(FLAGS_mklml_dir, "libmklml_intel.dylib");
P
peizhilin 已提交
429 430
#elif defined(_WIN32)
  return GetDsoHandleFromSearchPath(FLAGS_mklml_dir, "mklml.dll");
431 432 433 434 435
#else
  return GetDsoHandleFromSearchPath(FLAGS_mklml_dir, "libmklml_intel.so");
#endif
}

436 437
void* GetOpDsoHandle(const std::string& dso_name) {
#if defined(__APPLE__) || defined(__OSX__)
G
GaoWei8 已提交
438 439
  PADDLE_THROW(platform::errors::Unimplemented(
      "Create custom cpp op outside framework do not support Apple."));
440
#elif defined(_WIN32) && defined(PADDLE_WITH_CUDA)
G
GaoWei8 已提交
441 442
  PADDLE_THROW(platform::errors::Unimplemented(
      "Create custom cpp op outside framework do not support Windows."));
443 444 445 446 447
#else
  return GetDsoHandleFromSearchPath(FLAGS_op_dir, dso_name);
#endif
}

Q
qijun 已提交
448
}  // namespace dynload
Q
qijun 已提交
449 450
}  // namespace platform
}  // namespace paddle