Files
paddlepaddle--paddle/paddle/cinn/backends/nvrtc/nvrtc_util.cc
T
2026-07-13 12:40:42 +08:00

373 lines
13 KiB
C++

// Copyright (c) 2021 CINN 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/cinn/backends/nvrtc/nvrtc_util.h"
#include <cuda.h>
#include <cuda_runtime.h>
#include <nvrtc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include "paddle/cinn/backends/codegen_cuda_dev.h"
#include "paddle/cinn/backends/cuda_util.h"
#include "paddle/cinn/backends/nvrtc/header_generator.h"
#include "paddle/cinn/common/common.h"
#include "paddle/cinn/runtime/flags.h"
#include "paddle/cinn/utils/string.h"
#include "paddle/common/enforce.h"
PD_DECLARE_string(cinn_nvcc_cmd_path);
PD_DECLARE_string(nvidia_package_dir);
PD_DECLARE_bool(nvrtc_compile_to_cubin);
PD_DECLARE_bool(cinn_nvrtc_cubin_with_fmad);
PD_DECLARE_string(cuda_cccl_dir);
namespace cinn {
namespace backends {
namespace nvrtc {
static bool TryLocatePath(const std::string& path) {
struct stat st;
return stat(path.c_str(), &st) == 0;
}
static std::vector<std::string> GetNvidiaAllIncludePath(
const std::string& nvidia_package_dir) {
std::vector<std::string> include_paths;
const std::string delimiter = "/";
// Expand this list if necessary.
#if CUDA_VERSION >= 13000 && defined(__linux__)
const std::vector<std::string> sub_modules = {"cu13",
"cublas",
"cuda_cupti",
"cudnn",
"cufft",
"cufile",
"cusparse",
"cusparselt",
"cusolver",
"cuda_nvrtc",
"curand",
"nccl",
"nvjitlink",
"nvtx",
"cuda_runtime"};
#else
const std::vector<std::string> sub_modules = {"cuda_cccl",
"cublas",
"cudnn",
"cufft",
"cusparse",
"cusolver",
"cuda_nvrtc",
"curand",
"cuda_runtime"};
#endif
for (auto& sub_module : sub_modules) {
std::string path =
nvidia_package_dir + delimiter + sub_module + delimiter + "include";
include_paths.push_back(path);
}
#if CUDA_VERSION >= 13000 && defined(__linux__)
include_paths.push_back(nvidia_package_dir + delimiter + "cu13/include/cccl");
include_paths.push_back(nvidia_package_dir + delimiter +
"cu13/include/nvtx3");
#endif
return include_paths;
}
std::string Compiler::operator()(const std::string& code,
bool include_headers) {
if (runtime::CanUseNvccCompiler()) {
return CompileWithNvcc(code);
}
return CompileCudaSource(code, include_headers);
}
Compiler::Compiler() {
if (FLAGS_nvrtc_compile_to_cubin) {
compile_to_cubin_ = true;
}
VLOG(4) << "FLAGS_nvrtc_compile_to_cubin: " << FLAGS_nvrtc_compile_to_cubin
<< ", compile_to_cubin_: " << compile_to_cubin_;
}
bool Compiler::compile_to_cubin() { return compile_to_cubin_; }
std::vector<std::string> Compiler::FindCUDAIncludePaths() {
const std::string delimiter = "/";
std::string cuda_include_path;
const char* cuda_path_env = std::getenv("CUDA_PATH");
if (cuda_path_env != nullptr) {
cuda_include_path += cuda_path_env;
cuda_include_path += delimiter + "include";
VLOG(4) << "FindCUDAIncludePaths from CUDA_PATH: " << cuda_include_path;
return {cuda_include_path};
}
#if defined(__linux__)
if (!FLAGS_nvidia_package_dir.empty() &&
TryLocatePath(FLAGS_nvidia_package_dir)) {
VLOG(4) << "FindCUDAIncludePaths from nvidia_package_dir: "
<< FLAGS_nvidia_package_dir;
return GetNvidiaAllIncludePath(FLAGS_nvidia_package_dir);
}
cuda_include_path = "/usr/local/cuda/include";
if (TryLocatePath(cuda_include_path)) {
VLOG(4) << "FindCUDAIncludePaths from " << cuda_include_path;
return {cuda_include_path};
}
#endif
std::stringstream ss;
ss << "Cannot find cuda include path."
<< "CUDA_PATH is not set or CUDA is not installed in the default "
"installation path."
<< "In other than linux, it is necessary to set CUDA_PATH.";
PADDLE_THROW(::common::errors::Fatal(ss.str()));
return {cuda_include_path};
}
std::vector<std::string> Compiler::FindCINNRuntimeIncludePaths() {
return {Context::Global().runtime_include_dir()};
}
std::string Compiler::CompileCudaSource(const std::string& code,
bool include_headers) {
const auto& header_gen = JitSafeHeaderGenerator::GetInstance();
std::vector<std::string> compile_options;
std::vector<const char*> param_cstrings{};
nvrtcProgram prog;
std::string cc = "75";
int major, minor;
cudaError_t e1 =
cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, 0);
cudaError_t e2 =
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, 0);
if (e1 == cudaSuccess && e2 == cudaSuccess) {
cc = std::to_string(major) + std::to_string(minor);
} else {
LOG(WARNING) << "cannot detect compute capability from your device, "
<< "fall back to compute_75.";
}
if (compile_to_cubin_) {
compile_options.push_back("-arch=sm_" + cc);
std::string enable_fmad =
FLAGS_cinn_nvrtc_cubin_with_fmad ? "true" : "false";
compile_options.push_back("--fmad=" + enable_fmad);
} else {
compile_options.push_back("-arch=compute_" + cc);
}
#if CUDA_VERSION >= 13000 && defined(__linux__)
compile_options.push_back("-std=c++17");
#else
compile_options.push_back("-std=c++14");
#endif
compile_options.push_back("-default-device");
if (include_headers) { // prepare include headers
auto cuda_headers = FindCUDAIncludePaths();
auto cinn_headers = FindCINNRuntimeIncludePaths();
std::vector<std::string> include_paths;
for (auto& header : cuda_headers) {
VLOG(5) << "add include-path: " << header;
include_paths.push_back("--include-path=" + header);
}
for (auto& header : cinn_headers) {
include_paths.push_back("--include-path=" + header);
}
compile_options.insert(
std::end(compile_options), include_paths.begin(), include_paths.end());
}
for (const auto& option : compile_options) {
param_cstrings.push_back(option.c_str());
}
VLOG(3) << "compile options: " << utils::Join(compile_options, " ");
NVRTC_CALL(nvrtcCreateProgram(&prog,
code.c_str(),
nullptr,
header_gen.size(),
header_gen.headers().data(),
header_gen.include_names().data()));
nvrtcResult compile_res =
nvrtcCompileProgram(prog, param_cstrings.size(), param_cstrings.data());
if (compile_res != NVRTC_SUCCESS) {
std::string new_code = code;
std::string from = CodeGenCudaDev::GetSourceHeader();
size_t pos = new_code.find(from);
if (pos != std::string::npos) {
new_code.replace(
pos, from.length(), CodeGenCudaDev::GetGeneralSourceHeader());
}
NVRTC_CALL(nvrtcCreateProgram(&prog,
new_code.c_str(),
nullptr,
header_gen.size(),
header_gen.headers().data(),
header_gen.include_names().data()));
compile_res =
nvrtcCompileProgram(prog, param_cstrings.size(), param_cstrings.data());
}
{ // get log
size_t log_size;
NVRTC_CALL(nvrtcGetProgramLogSize(prog, &log_size));
std::string log;
log.resize(log_size);
NVRTC_CALL(nvrtcGetProgramLog(prog, &log[0]));
PADDLE_ENFORCE_EQ(
compile_res,
NVRTC_SUCCESS,
::common::errors::Fatal("NVRTC compilation failed: %s", log));
}
size_t size;
std::string data;
if (compile_to_cubin_) {
NVRTC_CALL(nvrtcGetCUBINSize(prog, &size));
data.resize(size);
NVRTC_CALL(nvrtcGetCUBIN(prog, &data[0]));
} else {
NVRTC_CALL(nvrtcGetPTXSize(prog, &size));
data.resize(size);
NVRTC_CALL(nvrtcGetPTX(prog, &data[0]));
}
NVRTC_CALL(nvrtcDestroyProgram(&prog));
return data;
}
std::string Compiler::CompileWithNvcc(const std::string& cuda_c) {
// read dir source
std::string dir = "./source";
if (access(dir.c_str(), 0) == -1) {
PADDLE_ENFORCE_NE(
mkdir(dir.c_str(), 7),
-1,
::common::errors::PermissionDenied(
"Failed to create directory %s. Please check the permissions.",
dir.c_str()));
}
// get unique prefix name
prefix_name_ = dir + "/" + cinn::common::UniqName("rtc_tmp");
auto cuda_c_file = prefix_name_ + ".cu";
std::ofstream ofs(cuda_c_file, std::ios::out);
PADDLE_ENFORCE_EQ(ofs.is_open(),
true,
::common::errors::Unavailable(
"Failed to open file %s. Please check if the file path "
"is correct and the file is accessible.",
cuda_c_file.c_str()));
ofs << cuda_c;
ofs.close();
CompileToPtx();
CompileToCubin();
return prefix_name_ + ".cubin";
}
void Compiler::CompileToPtx() {
auto include_dir = cinn::common::Context::Global().runtime_include_dir();
std::string include_dir_str = "";
for (auto dir : include_dir) {
if (include_dir_str.empty()) {
include_dir_str = dir;
} else {
include_dir_str += ":" + dir;
}
}
std::string options = std::string("export PATH=") + FLAGS_cinn_nvcc_cmd_path +
std::string(":$PATH && nvcc -std=c++14 --ptx -O3 -I ") +
include_dir_str;
options += " -arch=" + GetDeviceArch();
options += " -o " + prefix_name_ + ".ptx";
options += " " + prefix_name_ + ".cu";
VLOG(2) << "Nvcc Compile Options : " << options;
PADDLE_ENFORCE_EQ(
system(options.c_str()),
0,
::common::errors::InvalidArgument("Failed to execute command: %s. Please "
"check the command and try again.",
options.c_str()));
}
void Compiler::CompileToCubin() {
std::string options = std::string("export PATH=") + FLAGS_cinn_nvcc_cmd_path +
std::string(":$PATH && nvcc --cubin -O3");
options += " -arch=" + GetDeviceArch();
options += " -o " + prefix_name_ + ".cubin";
options += " " + prefix_name_ + ".ptx";
VLOG(2) << "Nvcc Compile Options : " << options;
PADDLE_ENFORCE_EQ(
system(options.c_str()),
0,
::common::errors::InvalidArgument("Failed to execute command: %s. Please "
"check the command and try again.",
options.c_str()));
}
std::string Compiler::GetDeviceArch() {
int major = 0, minor = 0;
if (cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, 0) ==
cudaSuccess &&
cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, 0) ==
cudaSuccess) {
return "sm_" + std::to_string(major) + std::to_string(minor);
} else {
LOG(WARNING) << "cannot detect compute capability from your device, "
<< "fall back to compute_30.";
return "sm_30";
}
}
std::string Compiler::ReadFile(const std::string& file_name,
std::ios_base::openmode mode) {
// open cubin file
std::ifstream ifs(file_name, mode);
PADDLE_ENFORCE_EQ(ifs.is_open(),
true,
::common::errors::Unavailable(
"Failed to open file %s. Please check if the file path "
"is correct and the file is accessible.",
file_name.c_str()));
ifs.seekg(std::ios::end);
auto len = ifs.tellg();
ifs.seekg(0);
// read cubin file
std::string file_data(len, ' ');
ifs.read(&file_data[0], len);
ifs.close();
return std::move(file_data);
}
} // namespace nvrtc
} // namespace backends
} // namespace cinn