CUDA编程接口详解

CUDA编程接口详解

本文将详细介绍NVIDIA CUDA编程指南第3章(编程接口)中的核心概念,例如NVCC编译器、CUDA运行时、版本管理和兼容性、计算模式、模式切换以及Windows下的Tesla计算集群模式。以下是本文的大纲:
在这里插入图片描述

1. Compilation with NVCC

NVCC(NVIDIA CUDA Compiler,NVIDIA CUDA编译器)是用于编译CUDA C/C++代码的编译器。要使用NVCC编译CUDA代码,我们需要将CUDA源文件的扩展名设置为.cu,并使用以下命令进行编译:

nvcc -o my_program my_program.cu

在本节中,我们将详细介绍NVCC编译器的用法和选项。以下是本节的内容概述:

  • NVCC编译器简介
  • 常用编译选项
  • 示例:编译一个简单的CUDA程序
  • 示例:使用Makefile编译CUDA程序

1.1. NVCC编译器简介

NVCC是CUDA平台的核心组件之一,它将CUDA C/C++代码转换为GPU可执行的二进制代码。NVCC的运行过程分为两个阶段:首先,它将CUDA代码中的设备代码(device code)和主机代码(host code)分离;然后,它将设备代码交给PTX编译器(NVIDIA的GPU汇编编译器)进行编译,将主机代码交给C/C++编译器(例如GCC或者Clang)进行编译。

1.2. 常用编译选项

NVCC支持许多编译选项,例如:

  • -arch=sm_XX:设置目标架构,例如-arch=sm_35用于编译支持计算能力3.5的程序;
  • -c:仅编译,不进行链接;
  • -o:设置输出文件名;
  • -I:设置头文件搜索路径;
  • -L:设置库文件搜索路径;
  • -l:链接库文件。

1.3. 示例:编译一个简单的CUDA程序

假设我们有一个简单的CUDA程序vector_add.cu,其内容如下:

#include <iostream>
#include <cuda_runtime.h>

__global__ void vector_add(const float *A, const float *B, float *C, int N) {
    
    
    int i = blockDim.x * blockIdx.x + threadIdx.x;
    if (i < N) {
    
    
        C[i] = A[i] + B[i];
    }
}

int main() {
    
    
    // ...省略主机代码...
}

要使用NVCC编译这个程序,我们可以执行以下命令:

nvcc -o vector_add vector_add.cu

编译成功后,我们可以运行生成的可执行文件vector_add

1.4. 示例:使用Makefile编译CUDA程序

在实际项目中,我们通常需要编译多个源文件,并链接多个库文件。为了简化编译过程,我们可以使用Makefile来管理编译选项和依赖关系。以下是一个简单的Makefile示例:

# 设置NVCC编译器和编译选项
NVCC = nvcc
CFLAGS = -arch=sm_35 -I/usr/local/cuda/include

# 设置目标文件和源文件
TARGET = vector_add
SOURCES = vector_add.cu

# 编译规则
all: $(TARGET)

$(TARGET): $(SOURCES)
	$(NVCC) $(CFLAGS) -o $@ $^

clean:
	rm -f $(TARGET)

使用这个Makefile,我们可以直接运行make命令来编译CUDA程序。

2. CUDA Runtime

CUDA运行时(CUDA Runtime)是一个用于管理设备、内存和执行的高级API。它包括以下几个部分:

  • 设备管理:包括设备查询、设备选择、设备属性获取等功能;
  • 内存管理:包括设备内存分配、释放、数据传输等功能;
  • 执行管理:包括核函数调用、同步、流管理等功能。

在本节中,我们将详细介绍CUDA运行时API的使用方法和注意事项。以下是本节的内容概述:

  • 设备管理API
  • 内存管理API
  • 执行管理API

2.1. 设备管理API

设备管理API主要用于查询和设置CUDA设备。以下是一些常用的设备管理API:

  • cudaGetDeviceCount(int *count):获取可用CUDA设备的数量;
  • cudaGetDeviceProperties(cudaDeviceProp *prop, int device):获取指定设备的属性;
  • cudaSetDevice(int device):选择当前线程使用的设备;
  • cudaGetDevice(int *device):获取当前线程使用的设备。

2.2. 内存管理API

内存管理API主要用于分配、释放和传输设备内存。以下是一些常用的内存管理API:

  • cudaMalloc(void **devPtr, size_t size):分配设备内存;
  • cudaFree(void *devPtr):释放设备内存;
  • cudaMemcpy(void *dst, const void *src, size_t count, cudaMemcpyKind kind):传输内存数据。

2.3. 执行管理API

执行管理API主要用于调用核函数、同步和管理流。以下是一些常用的执行管理API:

  • __global__:定义核函数;
  • <<<...>>>():调用核函数;
  • cudaStreamCreate(cudaStream_t *pStream):创建流;
  • cudaStreamDestroy(cudaStream_t stream):销毁流;
  • cudaStreamSynchronize(cudaStream_t stream):同步流;
  • cudaDeviceSynchronize():同步设备。

3. Versioning and Compatibility

CUDA使用版本号表示其API和ABI(Application Binary Interface,应用程序二进制接口)的兼容性。CUDA的版本号由三部分组成:主版本号、次版本号和修订版本号,例如CUDA 10.2.89。

主版本号和次版本号用于表示API的兼容性。如果应用程序使用的CUDA版本与系统安装的CUDA版本在主版本号和次版本号上一致,则应用程序可以正常运行。修订版本号用于表示ABI的兼容性。如果应用程序使用的CUDA版本与系统安装的CUDA版本在修订版本号上一致或更低,则应用程序可以正常运行。

在本节中,我们将详细介绍CUDA版本管理和兼容性的概念和实践。以下是本节的内容概述:

  • CUDA版本号
  • API兼容性
  • ABI兼容性
  • 示例:检查CUDA版本

3.1. CUDA版本号

CUDA版本号由三部分组成:

  • 主版本号(Major version):表示CUDA平台的主要功能和架构变化;
  • 次版本号(Minor version):表示CUDA平台的次要功能和性能改进;
  • 修订版本号(Revision version):表示CUDA平台的细节修复和优化。

例如,CUDA 10.2.89表示主版本号为10,次版本号为2,修订版本号为89。

3.2. API兼容性

API兼容性是指一个CUDA程序可以在不同版本的CUDA平台上正常运行。API兼容性主要由主版本号和次版本号决定:

  • 如果一个CUDA程序使用的CUDA版本与系统安装的CUDA版本在主版本号和次版本号上一致,则该程序可以正常运行;
  • 如果一个CUDA程序使用的CUDA版本的主版本号高于系统安装的CUDA版本的主版本号,则该程序可能无法正常运行;
  • 如果一个CUDA程序使用的CUDA版本的主版本号低于系统安装的CUDA版本的主版本号,但次版本号高于或等于系统安装的CUDA版本的次版本号,则该程序可以正常运行;
  • 如果一个CUDA程序使用的CUDA版本的主版本号低于系统安装的CUDA版本的主版本号,且次版本号低于系统安装的CUDA版本的次版本号,则该程序可能无法正常运行。

3.3. ABI兼容性

ABI兼容性是指一个CUDA程序的二进制文件可以在不同版本的CUDA平台上正常运行。ABI兼容性主要由修订版本号决定:

  • 如果一个CUDA程序使用的CUDA版本与系统安装的CUDA版本在修订版本号上一致或更低,则该程序可以正常运行;
  • 如果一个CUDA程序使用的CUDA版本的修订版本号高于系统安装的CUDA版本的修订版本号,则该程序可能无法正常运行。

3.4. 示例:检查CUDA版本

要检查CUDA版本,我们可以使用以下方法:

  • 查看CUDA运行时库的版本:cat /usr/local/cuda/version.txt
  • 查看CUDA编译器的版本:nvcc --version
  • 查看CUDA驱动的版本:nvidia-smi
  • 在CUDA程序中获取版本信息:
#include <iostream>
#include <cuda_runtime.h>

int main() {
    
    
    int runtime_version = 0;
    int driver_version = 0;
    cudaRuntimeGetVersion(&runtime_version);
    cudaDriverGetVersion(&driver_version);
    std::cout << "CUDA Runtime Version: " << runtime_version << std::endl;
    std::cout << "CUDA Driver Version: " << driver_version << std::endl;
    return 0;
}

4. Compute Modes

CUDA支持多种计算模式,用于控制设备的访问权限。计算模式分为以下几种:

  • Default(默认):设备可以被多个线程和多个上下文(Context)同时访问;
  • Exclusive Process(独占进程):设备只能被一个进程访问,但可以被多个线程和多个上下文同时访问;
  • Prohibited(禁止):设备不能被任何线程或上下文访问。

在本节中,我们将详细介绍CUDA计算模式的概念和实践。以下是本节的内容概述:

  • 计算模式简介
  • 示例:设置计算模式
  • 示例:查询计算模式

4.1. 计算模式简介

计算模式是一种用于控制CUDA设备访问权限的机制。通过设置计算模式,我们可以控制设备在多个线程和多个上下文之间的共享行为。以下是计算模式的详细介绍:

  • Default(默认):设备可以被多个线程和多个上下文同时访问。这种模式适用于大多数情况,因为它允许多个CUDA程序并行运行;
  • Exclusive Process(独占进程):设备只能被一个进程访问,但可以被多个线程和多个上下文同时访问。这种模式适用于需要独占设备资源的高性能计算场景;
  • Prohibited(禁止):设备不能被任何线程或上下文访问。这种模式适用于需要禁用CUDA设备的安全或节能场景。

4.2. 示例:设置计算模式

要设置计算模式,我们可以使用以下方法:

  • 在CUDA程序中设置计算模式:
#include <iostream>
#include <cuda_runtime.h>

int main() {
    
    
    int device = 0;
    cudaSetDevice(device);

    // 设置计算模式为独占进程
    cudaError_t err = cudaDeviceSetSharedMemConfig(cudaComputeModeExclusiveProcess);
    if (err != cudaSuccess) {
    
    
        std::cerr << "Failed to set compute mode: " << cudaGetErrorString(err) << std::endl;
        return 1;
    }

    // ...执行其他CUDA操作...

    return 0;
}
  • 使用nvidia-smi命令行工具设置计算模式:
# 设置设备0的计算模式为独占进程
nvidia-smi -i 0 -c EXCLUSIVE_PROCESS

4.3. 示例:查询计算模式

要查询计算模式,我们可以使用以下方法:

  • 在CUDA程序中查询计算模式:
#include <iostream>
#include <cuda_runtime.h>

int main() {
    
    
    int device = 0;
    cudaSetDevice(device);

    // 获取计算模式
    cudaComputeMode compute_mode;
    cudaDeviceProp device_prop;
    cudaGetDeviceProperties(&device_prop, device);
    compute_mode = device_prop.computeMode;

    // 打印计算模式
    switch (compute_mode) {
    
    
        case cudaComputeModeDefault:
            std::cout << "Compute mode: Default" << std::endl;
            break;
        case cudaComputeModeExclusiveProcess:
            std::cout << "Compute mode: Exclusive Process" << std::endl;
            break;
        case cudaComputeModeProhibited:
            std::cout << "Compute mode: Prohibited" << std::endl;
            break;
        default:
            std::cerr << "Unknown compute mode" << std::endl;
            return 1;
    }

    return 0;
}
  • 使用nvidia-smi命令行工具查询计算模式:
# 查询设备0的计算模式
nvidia-smi -i 0 --query-gpu=compute_mode --format=csv

5. Mode Switches

CUDA的模式切换功能允许用户在系统运行时切换计算模式。模式切换通过nvidia-smi命令行工具实现,用户可以根据需要随时切换计算模式。

以下是一个模式切换的示例:

  1. 查看当前计算模式:
nvidia-smi -i 0 --query-gpu=compute_mode --format=csv
  1. 将计算模式切换为独占进程:
nvidia-smi -i 0 -c EXCLUSIVE_PROCESS
  1. 验证计算模式已更改:
nvidia-smi -i 0 --query-gpu=compute_mode --format=csv

需要注意的是,模式切换可能会影响正在运行的CUDA程序。在切换计算模式之前,请确保所有相关程序已关闭。

6. Tesla Compute Cluster Mode for Windows

Tesla计算集群模式(Tesla Compute Cluster Mode,简称TCC模式)是NVIDIA为Windows平台提供的一种高性能计算模式。TCC模式旨在提高Tesla GPU在高性能计算场景中的性能和稳定性,包括以下特性:

  • 优化的内存传输性能;
  • 支持大内存分页;
  • 支持Peer-to-Peer内存传输;
  • 更低的内核启动延迟;
  • 系统管理中断(SMI)隔离。

要启用TCC模式,用户需要使用NVIDIA控制面板或nvidia-smi命令行工具。启用TCC模式后,Tesla GPU将作为专用计算设备运行,无法用于图形显示。

需要注意的是,TCC模式仅适用于Windows平台上的Tesla GPU。其他平台和GPU系列不受影响。

猜你喜欢

转载自blog.csdn.net/kunhe0512/article/details/131017674