实战GPU编程(python高性能计算)2:环境验证


2 环境验证

2.1 安装

参考:https://developer.nvidia.com/cuda-downloads

然后安装CuPy,再安装PyCUDA。

验证安装:

# nvidia-smi
Wed Aug 20 11:02:36 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 570.124.06             Driver Version: 570.124.06     CUDA Version: 12.8     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA L20                     Off |   00000000:02:00.0 Off |                    0 |
| N/A   49C    P8             26W /  350W |      14MiB /  46068MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A            2990      G   /usr/libexec/Xorg                         4MiB |
+-----------------------------------------------------------------------------------------+

# nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2025 NVIDIA Corporation
Built on Wed_Jan_15_19:21:50_PST_2025
Cuda compilation tools, release 12.8, V12.8.61
Build cuda_12.8.r12.8/compiler.35404655_0

如果找不到以上命令则需要设置环境变量:

export PATH=/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH

python验证:

>>> import cupy as cp
... print("CuPy version:", cp.__version__)
... print("CUDA runtime version:", cp.cuda.runtime.runtimeGetVersion())
... print("Our available device:", cp.cuda.runtime.getDeviceProperties(0)['name'])
...
CuPy version: 13.6.0
CUDA runtime version: 12080
Our available device: b'NVIDIA L20'
>>> dir(cp.cuda.runtime)
['CUDARuntimeError', 'CUDA_C_16F', 'CUDA_C_32F', 'CUDA_C_64F', 'CUDA_C_8I', 'CUDA_C_8U', 'CUDA_R_16F', 'CUDA_R_32F', 'CUDA_R_64F', 'CUDA_R_8I', 'CUDA_R_8U', 'MemPoolProps', 'PointerAttributes', '_ThreadLocal', '__builtins__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__pyx_capi__', '__pyx_unpickle_MemPoolProps', '__pyx_unpickle_PointerAttributes', '__pyx_unpickle__ThreadLocal', '__spec__', '__test__', '_deviceEnsurePeerAccess', '_export_enum', '_getCUDAMajorVersion', '_getLocalRuntimeVersion', '_launchHostFuncUnmanaged', '_sys', '_threading', 'check_status', 'createSurfaceObject', 'createTextureObject', 'cudaAddressModeBorder', 'cudaAddressModeClamp', 'cudaAddressModeMirror', 'cudaAddressModeWrap', 'cudaArrayDefault', 'cudaArraySurfaceLoadStore', 'cudaChannelFormatKindFloat', 'cudaChannelFormatKindNone', 'cudaChannelFormatKindSigned', 'cudaChannelFormatKindUnsigned', 'cudaCpuDeviceId', 'cudaDevAttrAsyncEngineCount', 'cudaDevAttrCanFlushRemoteWrites', 'cudaDevAttrCanMapHostMemory', 'cudaDevAttrCanUseHostPointerForRegisteredMem', 'cudaDevAttrClockRate', 'cudaDevAttrComputeMode', 'cudaDevAttrComputePreemptionSupported', 'cudaDevAttrConcurrentKernels', 'cudaDevAttrConcurrentManagedAccess', 'cudaDevAttrCooperativeLaunch', 'cudaDevAttrCooperativeMultiDeviceLaunch', 'cudaDevAttrDirectManagedMemAccessFromHost', 'cudaDevAttrEccEnabled', 'cudaDevAttrGPUDirectRDMAFlushWritesOptions', 'cudaDevAttrGPUDirectRDMASupported', 'cudaDevAttrGPUDirectRDMAWritesOrdering', 'cudaDevAttrGlobalL1CacheSupported', 'cudaDevAttrGlobalMemoryBusWidth', 'cudaDevAttrGpuOverlap', 'cudaDevAttrHostNativeAtomicSupported', 'cudaDevAttrHostRegisterReadOnlySupported', 'cudaDevAttrHostRegisterSupported', 'cudaDevAttrIntegrated', 'cudaDevAttrIsMultiGpuBoard', 'cudaDevAttrKernelExecTimeout', 'cudaDevAttrL2CacheSize', 'cudaDevAttrLocalL1CacheSupported', 'cudaDevAttrManagedMemory', 'cudaDevAttrMaxBlockDimX', 'cudaDevAttrMaxBlockDimY', 'cudaDevAttrMaxBlockDimZ', 'cudaDevAttrMaxBlocksPerMultiprocessor', 'cudaDevAttrMaxGridDimX', 'cudaDevAttrMaxGridDimY', 'cudaDevAttrMaxGridDimZ', 'cudaDevAttrMaxPitch', 'cudaDevAttrMaxRegistersPerBlock', 'cudaDevAttrMaxRegistersPerMultiprocessor', 'cudaDevAttrMaxSharedMemoryPerBlock', 'cudaDevAttrMaxSharedMemoryPerBlockOptin', 'cudaDevAttrMaxSharedMemoryPerMultiprocessor', 'cudaDevAttrMaxSurface1DLayeredLayers', 'cudaDevAttrMaxSurface1DLayeredWidth', 'cudaDevAttrMaxSurface1DWidth', 'cudaDevAttrMaxSurface2DHeight', 'cudaDevAttrMaxSurface2DLayeredHeight', 'cudaDevAttrMaxSurface2DLayeredLayers', 'cudaDevAttrMaxSurface2DLayeredWidth', 'cudaDevAttrMaxSurface2DWidth', 'cudaDevAttrMaxSurface3DDepth', 'cudaDevAttrMaxSurface3DHeight', 'cudaDevAttrMaxSurface3DWidth', 'cudaDevAttrMaxSurfaceCubemapLayeredLayers', 'cudaDevAttrMaxSurfaceCubemapLayeredWidth', 'cudaDevAttrMaxSurfaceCubemapWidth', 'cudaDevAttrMaxTexture1DLayeredLayers', 'cudaDevAttrMaxTexture1DLayeredWidth', 'cudaDevAttrMaxTexture1DLinearWidth', 'cudaDevAttrMaxTexture1DMipmappedWidth', 'cudaDevAttrMaxTexture1DWidth', 'cudaDevAttrMaxTexture2DGatherHeight', 'cudaDevAttrMaxTexture2DGatherWidth', 'cudaDevAttrMaxTexture2DHeight', 'cudaDevAttrMaxTexture2DLayeredHeight', 'cudaDevAttrMaxTexture2DLayeredLayers', 'cudaDevAttrMaxTexture2DLayeredWidth', 'cudaDevAttrMaxTexture2DLinearHeight', 'cudaDevAttrMaxTexture2DLinearPitch', 'cudaDevAttrMaxTexture2DLinearWidth', 'cudaDevAttrMaxTexture2DMipmappedHeight', 'cudaDevAttrMaxTexture2DMipmappedWidth', 'cudaDevAttrMaxTexture2DWidth', 'cudaDevAttrMaxTexture3DDepth', 'cudaDevAttrMaxTexture3DDepthAlt', 'cudaDevAttrMaxTexture3DHeight', 'cudaDevAttrMaxTexture3DHeightAlt', 'cudaDevAttrMaxTexture3DWidth', 'cudaDevAttrMaxTexture3DWidthAlt', 'cudaDevAttrMaxTextureCubemapLayeredLayers', 'cudaDevAttrMaxTextureCubemapLayeredWidth', 'cudaDevAttrMaxTextureCubemapWidth', 'cudaDevAttrMaxThreadsPerBlock', 'cudaDevAttrMaxThreadsPerMultiProcessor', 'cudaDevAttrMaxTimelineSemaphoreInteropSupported', 'cudaDevAttrMemoryClockRate', 'cudaDevAttrMemoryPoolSupportedHandleTypes', 'cudaDevAttrMemoryPoolsSupported', 'cudaDevAttrMultiGpuBoardGroupID', 'cudaDevAttrMultiProcessorCount', 'cudaDevAttrPageableMemoryAccess', 'cudaDevAttrPageableMemoryAccessUsesHostPageTables', 'cudaDevAttrPciBusId', 'cudaDevAttrPciDeviceId', 'cudaDevAttrPciDomainId', 'cudaDevAttrReserved92', 'cudaDevAttrReserved93', 'cudaDevAttrReserved94', 'cudaDevAttrReservedSharedMemoryPerBlock', 'cudaDevAttrSingleToDoublePrecisionPerfRatio', 'cudaDevAttrSparseCudaArraySupported', 'cudaDevAttrStreamPrioritiesSupported', 'cudaDevAttrSurfaceAlignment', 'cudaDevAttrTccDriver', 'cudaDevAttrTextureAlignment', 'cudaDevAttrTexturePitchAlignment', 'cudaDevAttrTotalConstantMemory', 'cudaDevAttrUnifiedAddressing', 'cudaDevAttrWarpSize', 'cudaFilterModeLinear', 'cudaFilterModePoint', 'cudaInvalidDeviceId', 'cudaIpcMemLazyEnablePeerAccess', 'cudaLimitDevRuntimePendingLaunchCount', 'cudaLimitDevRuntimeSyncDepth', 'cudaLimitMallocHeapSize', 'cudaLimitMaxL2FetchGranularity', 'cudaLimitPrintfFifoSize', 'cudaLimitStackSize', 'cudaMemAdviseSetAccessedBy', 'cudaMemAdviseSetPreferredLocation', 'cudaMemAdviseSetReadMostly', 'cudaMemAdviseUnsetAccessedBy', 'cudaMemAdviseUnsetPreferredLocation', 'cudaMemAdviseUnsetReadMostly', 'cudaMemAllocationTypePinned', 'cudaMemAttachGlobal', 'cudaMemAttachHost', 'cudaMemAttachSingle', 'cudaMemHandleTypeNone', 'cudaMemHandleTypePosixFileDescriptor', 'cudaMemLocationTypeDevice', 'cudaMemLocationTypeHost', 'cudaMemLocationTypeHostNuma', 'cudaMemLocationTypeHostNumaCurrent', 'cudaMemLocationTypeInvalid', 'cudaMemPoolAttrReleaseThreshold', 'cudaMemPoolAttrReservedMemCurrent', 'cudaMemPoolAttrReservedMemHigh', 'cudaMemPoolAttrUsedMemCurrent', 'cudaMemPoolAttrUsedMemHigh', 'cudaMemPoolReuseAllowInternalDependencies', 'cudaMemPoolReuseAllowOpportunistic', 'cudaMemPoolReuseFollowEventDependencies', 'cudaMemoryTypeDevice', 'cudaMemoryTypeHost', 'cudaReadModeElementType', 'cudaReadModeNormalizedFloat', 'cudaResourceTypeArray', 'cudaResourceTypeLinear', 'cudaResourceTypeMipmappedArray', 'cudaResourceTypePitch2D', 'destroySurfaceObject', 'destroyTextureObject', 'deviceCanAccessPeer', 'deviceDisablePeerAccess', 'deviceEnablePeerAccess', 'deviceGetAttribute', 'deviceGetByPCIBusId', 'deviceGetDefaultMemPool', 'deviceGetLimit', 'deviceGetMemPool', 'deviceGetPCIBusId', 'deviceSetLimit', 'deviceSetMemPool', 'deviceSynchronize', 'driverGetVersion', 'eventBlockingSync', 'eventCreate', 'eventCreateWithFlags', 'eventDefault', 'eventDestroy', 'eventDisableTiming', 'eventElapsedTime', 'eventInterprocess', 'eventQuery', 'eventRecord', 'eventSynchronize', 'free', 'freeArray', 'freeAsync', 'freeHost', 'getDevice', 'getDeviceCount', 'getDeviceProperties', 'graphDestroy', 'graphExecDestroy', 'graphInstantiate', 'graphLaunch', 'graphUpload', 'hostAlloc', 'hostAllocDefault', 'hostAllocMapped', 'hostAllocPortable', 'hostAllocWriteCombined', 'hostRegister', 'hostUnregister', 'ipcCloseMemHandle', 'ipcGetEventHandle', 'ipcGetMemHandle', 'ipcOpenEventHandle', 'ipcOpenMemHandle', 'is_hip', 'launchHostFunc', 'malloc', 'malloc3DArray', 'mallocArray', 'mallocAsync', 'mallocFromPoolAsync', 'mallocManaged', 'memAdvise', 'memGetInfo', 'memPoolCreate', 'memPoolDestroy', 'memPoolGetAttribute', 'memPoolSetAttribute', 'memPoolTrimTo', 'memPrefetchAsync', 'memcpy', 'memcpy2D', 'memcpy2DAsync', 'memcpy2DFromArray', 'memcpy2DFromArrayAsync', 'memcpy2DToArray', 'memcpy2DToArrayAsync', 'memcpy3D', 'memcpy3DAsync', 'memcpyAsync', 'memcpyDefault', 'memcpyDeviceToDevice', 'memcpyDeviceToHost', 'memcpyHostToDevice', 'memcpyHostToHost', 'memcpyPeer', 'memcpyPeerAsync', 'memoryTypeDevice', 'memoryTypeHost', 'memoryTypeManaged', 'memoryTypeUnregistered', 'memset', 'memsetAsync', 'pointerGetAttributes', 'profilerStart', 'profilerStop', 'runtimeGetVersion', 'setDevice', 'streamAddCallback', 'streamBeginCapture', 'streamCaptureModeGlobal', 'streamCaptureModeRelaxed', 'streamCaptureModeThreadLocal', 'streamCaptureStatusActive', 'streamCaptureStatusInvalidated', 'streamCaptureStatusNone', 'streamCreate', 'streamCreateWithFlags', 'streamDefault', 'streamDestroy', 'streamEndCapture', 'streamIsCapturing', 'streamLegacy', 'streamNonBlocking', 'streamPerThread', 'streamQuery', 'streamSynchronize', 'streamWaitEvent']

2.2 使用设备查询验证GPU

我们已经安装了 CUDA 驱动程序和工具包,并确认我们的系统能够识别 GPU。在深入了解细节之前,让我们先仔细看看机器中的硬件。设备查询让我们能够查看 GPU 的所有重要细节,例如它拥有多少个多处理器、可用内存、支持的计算能力以及其他影响 CUDA 编程的功能。
运行设备查询时,它会显示您的安装已成功,并帮助我们调整内核和库设置以匹配您的硬件。这使得我们的编程在未来更加健壮、可预测且性能更佳。

2.2.1 运行设备查询

CUDA 工具包附带一个名为 deviceQuery 的便捷示例二进制文件。此工具会打印系统中每个可见 GPU 的完整报告。
在大多数 Linux 设置中,我们可以在以下位置找到 deviceQuery:/usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery
如果没有找到,我们可以按如下方式构建 CUDA 示例目录:

cd /usr/local/cuda/samples/1_Utilities/deviceQuery
sudo make
./deviceQuery

实在不行还可以使用源码编译:

# git clone https://github.com/NVIDIA/cuda-samples
# cd cuda-samples/Samples/1_Utilities/build/deviceQuery
# make
# ./deviceQuery

输出会显示一长串属性,其中对我们最有用的几行是:
● 设备名称
● 全局内存总量
● 多处理器数量
● 每个 SM 的 CUDA 核心数
● 每个块的最大线程数
● 每个块的共享内存
● 计算能力

2.2.2 python验证设备属性

我们经常希望直接在 Python 中访问这些设置,尤其是在我们的代码需要适应不同的硬件时。使用 CuPy,我们可以以编程方式检查所需的一切。
现在,我们将打印出最重要的属性:

In [1]: import cupy as cp

In [2]: device = cp.cuda.Device(0)

In [3]: attributes = device.attributes

In [4]: attributes
Out[4]:
{'AsyncEngineCount': 2,
 'CanFlushRemoteWrites': 0,
 'CanMapHostMemory': 1,
 'CanUseHostPointerForRegisteredMem': 1,
 'ClockRate': 2520000,
 'ComputeMode': 0,
 'ComputePreemptionSupported': 1,
 'ConcurrentKernels': 1,
 'ConcurrentManagedAccess': 1,
 'CooperativeLaunch': 1,
 'CooperativeMultiDeviceLaunch': 1,
 'DirectManagedMemAccessFromHost': 0,
 'EccEnabled': 0,
 'GPUDirectRDMAFlushWritesOptions': 1,
 'GPUDirectRDMASupported': 0,
 'GPUDirectRDMAWritesOrdering': 0,
 'GlobalL1CacheSupported': 1,
 'GlobalMemoryBusWidth': 384,
 'GpuOverlap': 1,
 'HostNativeAtomicSupported': 0,
 'HostRegisterReadOnlySupported': 0,
 'HostRegisterSupported': 1,
 'Integrated': 0,
 'IsMultiGpuBoard': 0,
 'KernelExecTimeout': 1,
 'L2CacheSize': 75497472,
 'LocalL1CacheSupported': 1,
 'ManagedMemory': 1,
 'MaxBlockDimX': 1024,
 'MaxBlockDimY': 1024,
 'MaxBlockDimZ': 64,
 'MaxBlocksPerMultiprocessor': 24,
 'MaxGridDimX': 2147483647,
 'MaxGridDimY': 65535,
 'MaxGridDimZ': 65535,
 'MaxPitch': 2147483647,
 'MaxRegistersPerBlock': 65536,
 'MaxRegistersPerMultiprocessor': 65536,
 'MaxSharedMemoryPerBlock': 49152,
 'MaxSharedMemoryPerBlockOptin': 101376,
 'MaxSharedMemoryPerMultiprocessor': 102400,
 'MaxSurface1DLayeredLayers': 2048,
 'MaxSurface1DLayeredWidth': 32768,
 'MaxSurface1DWidth': 32768,
 'MaxSurface2DHeight': 65536,
 'MaxSurface2DLayeredHeight': 32768,
 'MaxSurface2DLayeredLayers': 2048,
 'MaxSurface2DLayeredWidth': 32768,
 'MaxSurface2DWidth': 131072,
 'MaxSurface3DDepth': 16384,
 'MaxSurface3DHeight': 16384,
 'MaxSurface3DWidth': 16384,
 'MaxSurfaceCubemapLayeredLayers': 2046,
 'MaxSurfaceCubemapLayeredWidth': 32768,
 'MaxSurfaceCubemapWidth': 32768,
 'MaxTexture1DLayeredLayers': 2048,
 'MaxTexture1DLayeredWidth': 32768,
 'MaxTexture1DLinearWidth': 268435456,
 'MaxTexture1DMipmappedWidth': 32768,
 'MaxTexture1DWidth': 131072,
 'MaxTexture2DGatherHeight': 32768,
 'MaxTexture2DGatherWidth': 32768,
 'MaxTexture2DHeight': 65536,
 'MaxTexture2DLayeredHeight': 32768,
 'MaxTexture2DLayeredLayers': 2048,
 'MaxTexture2DLayeredWidth': 32768,
 'MaxTexture2DLinearHeight': 65000,
 'MaxTexture2DLinearPitch': 2097120,
 'MaxTexture2DLinearWidth': 131072,
 'MaxTexture2DMipmappedHeight': 32768,
 'MaxTexture2DMipmappedWidth': 32768,
 'MaxTexture2DWidth': 131072,
 'MaxTexture3DDepth': 16384,
 'MaxTexture3DDepthAlt': 32768,
 'MaxTexture3DHeight': 16384,
 'MaxTexture3DHeightAlt': 8192,
 'MaxTexture3DWidth': 16384,
 'MaxTexture3DWidthAlt': 8192,
 'MaxTextureCubemapLayeredLayers': 2046,
 'MaxTextureCubemapLayeredWidth': 32768,
 'MaxTextureCubemapWidth': 32768,
 'MaxThreadsPerBlock': 1024,
 'MaxThreadsPerMultiProcessor': 1536,
 'MaxTimelineSemaphoreInteropSupported': 1,
 'MemoryClockRate': 10501000,
 'MemoryPoolSupportedHandleTypes': 9,
 'MemoryPoolsSupported': 1,
 'MultiGpuBoardGroupID': 0,
 'MultiProcessorCount': 128,
 'PageableMemoryAccess': 1,
 'PageableMemoryAccessUsesHostPageTables': 0,
 'PciBusId': 172,
 'PciDeviceId': 0,
 'PciDomainId': 0,
 'Reserved92': 0,
 'Reserved93': 0,
 'Reserved94': 0,
 'ReservedSharedMemoryPerBlock': 1024,
 'SingleToDoublePrecisionPerfRatio': 64,
 'SparseCudaArraySupported': 1,
 'StreamPrioritiesSupported': 1,
 'SurfaceAlignment': 512,
 'TccDriver': 0,
 'TextureAlignment': 512,
 'TexturePitchAlignment': 32,
 'TotalConstantMemory': 65536,
 'UnifiedAddressing': 1,
 'WarpSize': 32}

In [5]: properties = cp.cuda.runtime.getDeviceProperties(0)

In [6]: properties
Out[6]:
{'name': b'NVIDIA GeForce RTX 4090',
 'totalGlobalMem': 25241190400,
 'sharedMemPerBlock': 49152,
 'regsPerBlock': 65536,
 'warpSize': 32,
 'maxThreadsPerBlock': 1024,
 'maxThreadsDim': (1024, 1024, 64),
 'maxGridSize': (2147483647, 65535, 65535),
 'clockRate': 2520000,
 'totalConstMem': 65536,
 'major': 8,
 'minor': 9,
 'textureAlignment': 512,
 'texturePitchAlignment': 32,
 'multiProcessorCount': 128,
 'kernelExecTimeoutEnabled': 1,
 'integrated': 0,
 'canMapHostMemory': 1,
 'computeMode': 0,
 'maxTexture1D': 131072,
 'maxTexture2D': (131072, 65536),
 'maxTexture3D': (16384, 16384, 16384),
 'concurrentKernels': 1,
 'ECCEnabled': 0,
 'pciBusID': 172,
 'pciDeviceID': 0,
 'pciDomainID': 0,
 'tccDriver': 0,
 'memoryClockRate': 10501000,
 'memoryBusWidth': 384,
 'l2CacheSize': 75497472,
 'maxThreadsPerMultiProcessor': 1536,
 'isMultiGpuBoard': 0,
 'cooperativeLaunch': 1,
 'cooperativeMultiDeviceLaunch': 1,
 'deviceOverlap': 1,
 'maxTexture1DMipmap': 32768,
 'maxTexture1DLinear': 268435456,
 'maxTexture1DLayered': (32768, 2048),
 'maxTexture2DMipmap': (32768, 32768),
 'maxTexture2DLinear': (131072, 65000, 2097120),
 'maxTexture2DLayered': (32768, 32768, 2048),
 'maxTexture2DGather': (32768, 32768),
 'maxTexture3DAlt': (8192, 8192, 32768),
 'maxTextureCubemap': 32768,
 'maxTextureCubemapLayered': (32768, 2046),
 'maxSurface1D': 32768,
 'maxSurface1DLayered': (32768, 2048),
 'maxSurface2D': (131072, 65536),
 'maxSurface2DLayered': (32768, 32768, 2048),
 'maxSurface3D': (16384, 16384, 16384),
 'maxSurfaceCubemap': 32768,
 'maxSurfaceCubemapLayered': (32768, 2046),
 'surfaceAlignment': 512,
 'asyncEngineCount': 2,
 'unifiedAddressing': 1,
 'streamPrioritiesSupported': 1,
 'globalL1CacheSupported': 1,
 'localL1CacheSupported': 1,
 'sharedMemPerMultiprocessor': 102400,
 'regsPerMultiprocessor': 65536,
 'managedMemory': 1,
 'multiGpuBoardGroupID': 0,
 'hostNativeAtomicSupported': 0,
 'singleToDoublePrecisionPerfRatio': 64,
 'pageableMemoryAccess': 1,
 'concurrentManagedAccess': 1,
 'computePreemptionSupported': 1,
 'canUseHostPointerForRegisteredMem': 1,
 'sharedMemPerBlockOptin': 101376,
 'pageableMemoryAccessUsesHostPageTables': 0,
 'directManagedMemAccessFromHost': 0,
 'uuid': b'\xea\xce)\xa0\xbe\xf8\x9d\xfe\x06w\x96\xdb\x89\x84,\xc4',
 'luid': b'',
 'luidDeviceNodeMask': 0,
 'persistingL2CacheMaxSize': 51904512,
 'maxBlocksPerMultiProcessor': 24,
 'accessPolicyMaxWindowSize': 134213632,
 'reservedSharedMemPerBlock': 1024}

In [7]: properties['name']
Out[7]: b'NVIDIA GeForce RTX 4090'

In [8]: properties['major']
Out[8]: 8

In [9]: properties['minor']
Out[9]: 9

In [10]: print("Total Global Memory (MB):", device.mem_info[1] // (1024 * 1024))
Total Global Memory (MB): 24071

In [11]: print("Multiprocessors:", attributes['MultiProcessorCount'])
    ...: print("Max Threads per Block:", attributes['MaxThreadsPerBlock'])
    ...: print("Shared Memory per Block (KB):", attributes['MaxSharedMemoryPerBlock'] // 1024)
Multiprocessors: 128
Max Threads per Block: 1024
Shared Memory per Block (KB): 48

我们可以看到所有会影响代码的属性:线程块可以设置多大、可以分配多少内存以及支持哪些功能。

2.2.3 验证计算能力

CUDA 计算能力(Compute Capability) 是 NVIDIA GPU 的一个版本号,它定义了 GPU 的硬件特性和功能集。简单来说,它告诉我们一张 NVIDIA 显卡在执行 CUDA 应用程序时,支持哪些功能,以及它有哪些硬件上的限制(比如寄存器数量、共享内存大小等)。

这个版本号通常由两个数字表示,例如 8.6 或 7.5,其中第一个数字是主版本号(major),第二个数字是次版本号(minor)。

主版本号(Major Version):表示 GPU 架构的代号。例如,8.x 代表 Ampere 架构,7.x 代表 Volta 架构,6.x 代表 Pascal 架构。主版本号的升级通常意味着架构上的重大变化和新功能的引入。

次版本号(Minor Version):表示在同一代 GPU 架构内的功能改进或修订。例如,8.0 和 8.6 都属于 Ampere 架构,但 8.6 可能在某些方面(如性能、功能)有所优化。

不同的计算能力版本支持不同的 CUDA 功能。例如,某些高级功能(如 Tensor Core、异步操作、增强的指令集等)可能只在较高的计算能力版本上才可用。如果你想在程序中使用这些新功能,你的 GPU 必须支持相应的计算能力。

nvcc 编译器在编译 CUDA 代码时,会根据你指定的计算能力来生成优化的二进制代码。指定正确的计算能力可以帮助编译器更好地利用目标 GPU 的硬件特性,从而获得更好的性能。

计算能力决定了 GPU 的硬件参数,例如:

  • 每个多处理器(SM)的线程块(Thread Block)数量。
  • 每个线程块的最大线程数。
  • 每个线程块的共享内存(Shared Memory)大小。
  • 每个线程的寄存器(Register)数量。

这些参数直接影响到 CUDA 内核的设计和优化。开发者需要根据目标 GPU 的计算能力来调整网格(Grid)和线程块(Block)的维度,以及共享内存的使用,以避免资源耗尽。计算能力的每次提升都会解锁新的指令、更大的共享内存以及新的加速功能。

大多数现代 CUDA 库都要求计算能力达到 6.0 或更高版本。如果我们的 GPU 报告计算能力达到 7.5、8.6 或类似水平,则几乎可以支持所有当前的 CUDA 库,包括最新版本的 CuPy、PyCUDA 和深度学习框架。如果我们的设备报告计算能力非常低(例如 3.x 或更低版本),则某些功能可能无法使用。在这种情况下,我们会限制使用兼容的库,或者考虑升级硬件。

我们还可以在 PyCUDA 中查询设备属性:

>>> import pycuda.driver as drv
... import pycuda.autoinit
... device = drv.Device(0)
... print("Device Name:", device.name())
... print("Compute Capability:", f"{device.compute_capability()[0]}.{device.compute_capability()[1]}")
... print("Total Global Memory (MB):", device.total_memory() // (1024 * 1024))
... for key, value in device.get_attributes().items():
...    print(f"{key}: {value}")
...
Device Name: NVIDIA GeForce RTX 4090
Compute Capability: 8.9
Total Global Memory (MB): 24071
ASYNC_ENGINE_COUNT: 2
CAN_MAP_HOST_MEMORY: 1
CAN_USE_HOST_POINTER_FOR_REGISTERED_MEM: 1
CLOCK_RATE: 2520000
COMPUTE_CAPABILITY_MAJOR: 8
COMPUTE_CAPABILITY_MINOR: 9
COMPUTE_MODE: DEFAULT
COMPUTE_PREEMPTION_SUPPORTED: 1
CONCURRENT_KERNELS: 1
CONCURRENT_MANAGED_ACCESS: 1
DIRECT_MANAGED_MEM_ACCESS_FROM_HOST: 0
ECC_ENABLED: 0
GENERIC_COMPRESSION_SUPPORTED: 1
GLOBAL_L1_CACHE_SUPPORTED: 1
GLOBAL_MEMORY_BUS_WIDTH: 384
GPU_OVERLAP: 1
HANDLE_TYPE_POSIX_FILE_DESCRIPTOR_SUPPORTED: 1
HANDLE_TYPE_WIN32_HANDLE_SUPPORTED: 0
HANDLE_TYPE_WIN32_KMT_HANDLE_SUPPORTED: 0
HOST_NATIVE_ATOMIC_SUPPORTED: 0
INTEGRATED: 0
KERNEL_EXEC_TIMEOUT: 1
L2_CACHE_SIZE: 75497472
LOCAL_L1_CACHE_SUPPORTED: 1
MANAGED_MEMORY: 1
MAXIMUM_SURFACE1D_LAYERED_LAYERS: 2048
MAXIMUM_SURFACE1D_LAYERED_WIDTH: 32768
MAXIMUM_SURFACE1D_WIDTH: 32768
MAXIMUM_SURFACE2D_HEIGHT: 65536
MAXIMUM_SURFACE2D_LAYERED_HEIGHT: 32768
MAXIMUM_SURFACE2D_LAYERED_LAYERS: 2048
MAXIMUM_SURFACE2D_LAYERED_WIDTH: 32768
MAXIMUM_SURFACE2D_WIDTH: 131072
MAXIMUM_SURFACE3D_DEPTH: 16384
MAXIMUM_SURFACE3D_HEIGHT: 16384
MAXIMUM_SURFACE3D_WIDTH: 16384
MAXIMUM_SURFACECUBEMAP_LAYERED_LAYERS: 2046
MAXIMUM_SURFACECUBEMAP_LAYERED_WIDTH: 32768
MAXIMUM_SURFACECUBEMAP_WIDTH: 32768
MAXIMUM_TEXTURE1D_LAYERED_LAYERS: 2048
MAXIMUM_TEXTURE1D_LAYERED_WIDTH: 32768
MAXIMUM_TEXTURE1D_LINEAR_WIDTH: 268435456
MAXIMUM_TEXTURE1D_MIPMAPPED_WIDTH: 32768
MAXIMUM_TEXTURE1D_WIDTH: 131072
MAXIMUM_TEXTURE2D_ARRAY_HEIGHT: 32768
MAXIMUM_TEXTURE2D_ARRAY_NUMSLICES: 2048
MAXIMUM_TEXTURE2D_ARRAY_WIDTH: 32768
MAXIMUM_TEXTURE2D_GATHER_HEIGHT: 32768
MAXIMUM_TEXTURE2D_GATHER_WIDTH: 32768
MAXIMUM_TEXTURE2D_HEIGHT: 65536
MAXIMUM_TEXTURE2D_LINEAR_HEIGHT: 65000
MAXIMUM_TEXTURE2D_LINEAR_PITCH: 2097120
MAXIMUM_TEXTURE2D_LINEAR_WIDTH: 131072
MAXIMUM_TEXTURE2D_MIPMAPPED_HEIGHT: 32768
MAXIMUM_TEXTURE2D_MIPMAPPED_WIDTH: 32768
MAXIMUM_TEXTURE2D_WIDTH: 131072
MAXIMUM_TEXTURE3D_DEPTH: 16384
MAXIMUM_TEXTURE3D_DEPTH_ALTERNATE: 32768
MAXIMUM_TEXTURE3D_HEIGHT: 16384
MAXIMUM_TEXTURE3D_HEIGHT_ALTERNATE: 8192
MAXIMUM_TEXTURE3D_WIDTH: 16384
MAXIMUM_TEXTURE3D_WIDTH_ALTERNATE: 8192
MAXIMUM_TEXTURECUBEMAP_LAYERED_LAYERS: 2046
MAXIMUM_TEXTURECUBEMAP_LAYERED_WIDTH: 32768
MAXIMUM_TEXTURECUBEMAP_WIDTH: 32768
MAX_BLOCKS_PER_MULTIPROCESSOR: 24
MAX_BLOCK_DIM_X: 1024
MAX_BLOCK_DIM_Y: 1024
MAX_BLOCK_DIM_Z: 64
MAX_GRID_DIM_X: 2147483647
MAX_GRID_DIM_Y: 65535
MAX_GRID_DIM_Z: 65535
MAX_PERSISTING_L2_CACHE_SIZE: 51904512
MAX_PITCH: 2147483647
MAX_REGISTERS_PER_BLOCK: 65536
MAX_REGISTERS_PER_MULTIPROCESSOR: 65536
MAX_SHARED_MEMORY_PER_BLOCK: 49152
MAX_SHARED_MEMORY_PER_BLOCK_OPTIN: 101376
MAX_SHARED_MEMORY_PER_MULTIPROCESSOR: 102400
MAX_THREADS_PER_BLOCK: 1024
MAX_THREADS_PER_MULTIPROCESSOR: 1536
MEMORY_CLOCK_RATE: 10501000
MEMORY_POOLS_SUPPORTED: 1
MULTIPROCESSOR_COUNT: 128
MULTI_GPU_BOARD: 0
MULTI_GPU_BOARD_GROUP_ID: 0
PAGEABLE_MEMORY_ACCESS: 1
PAGEABLE_MEMORY_ACCESS_USES_HOST_PAGE_TABLES: 0
PCI_BUS_ID: 172
PCI_DEVICE_ID: 0
PCI_DOMAIN_ID: 0
READ_ONLY_HOST_REGISTER_SUPPORTED: 0
RESERVED_SHARED_MEMORY_PER_BLOCK: 1024
SINGLE_TO_DOUBLE_PRECISION_PERF_RATIO: 64
STREAM_PRIORITIES_SUPPORTED: 1
SURFACE_ALIGNMENT: 512
TCC_DRIVER: 0
TEXTURE_ALIGNMENT: 512
TEXTURE_PITCH_ALIGNMENT: 32
TOTAL_CONSTANT_MEMORY: 65536
UNIFIED_ADDRESSING: 1
WARP_SIZE: 32

这会显示所有可用属性,以便我们可以根据需要调整内核配置。
每当我们更换机器、GPU 或驱动程序时,运行新的设备查询始终是验证兼容性的第一步。通过这种做法,我们的 CUDA 编程将保持可靠、灵活,并与现有硬件完美匹配。

参考资料

2.3 在PyCUDA 中实现内核

我们已经使用 PyCUDA 编写了一个基本的向量加法内核,但现在我们想进一步了解 PyCUDA 如何让我们在 Python 脚本中插入和运行自定义 CUDA C 代码。我们希望熟悉整个主机-设备工作流程:分配内存、移动数据、编写更灵活的内核、动态编译、启动内核,然后检索和验证结果。
这个示例比我们之前的示例更好。在这里,我们将讨论参数化、尝试新的操作,并阐明为什么 PyCUDA 是 Python 和 CUDA 生态系统之间如此坚实的桥梁。

2.3.1 准备主机数据

我们将首先设置两个输入数组和一个用于输出的占位符。我们将开始在主机上使用 NumPy 来简化设置。

>>> import numpy as np
... N = 8
... a_host = np.arange(N, dtype=np.float32)
... b_host = np.arange(N, 0, -1).astype(np.float32)
... print("Input Array A:", a_host)
... print("Input Array B:", b_host)
...
Input Array A: [0. 1. 2. 3. 4. 5. 6. 7.]
Input Array B: [8. 7. 6. 5. 4. 3. 2. 1.]

为了清晰起见,我们选择较小的数组,但同样的工作流程也适用于更大的数据集。

2.3.2 配设备内存

使用 PyCUDA,我们将这些数组传输到 GPU。我们还为结果分配了空间。

>>> import pycuda.autoinit
... import pycuda.driver as drv
... import pycuda.gpuarray as gpuarray
... a_device = gpuarray.to_gpu(a_host)
... b_device = gpuarray.to_gpu(b_host)
... c_device = gpuarray.empty_like(a_device)
...

此时,a_device 和 b_device 位于 GPU 上,c_device 为结果保留空间。

2.3.3 编写自定义 CUDA C 内核

这正是 PyCUDA 的优势所在。我们可以将 CUDA C 代码以字符串形式嵌入,并在运行时进行编译。这样,我们的内核将逐个元素地添加两个数组,但我们也可以轻松地更改操作。
以下是 CUDA C 脚本:

... kernel_code = """
... __global__ void add_arrays(float *a, float *b, float *c, int n)
... {
...     int idx = threadIdx.x + blockDim.x * blockIdx.x;
...    if (idx < n)
...    {
...        c[idx] = a[idx] + b[idx];  // Try changing this operation!
...    }
... }
... """
... mod = SourceModule(kernel_code)
... add_arrays = mod.get_function("add_arrays")
...
>>> threads_per_block = 4
... blocks_per_grid = (N + threads_per_block - 1) // threads_per_block
...

CUDA 要求我们指定每个块中要运行的线程数以及要启动的块数。为简单起见,我们可以使用 4 作为块大小,并计算覆盖所有元素的网格大小。

2.3.4 启动内核并验证结果

然后,我们使用 PyCUDA 的函数接口调用内核。所有指针和参数都按照它们在内核中出现的顺序传递。

>>> add_arrays(
...    a_device, b_device, c_device, np.int32(N),
...    block=(threads_per_block, 1, 1), grid=(blocks_per_grid, 1)
... )

内核在 GPU 上异步运行,并行处理所有元素。
计算完成后,我们将结果从 GPU 复制回主机,并与 CPU 计算结果进行验证。

>>> c_host = c_device.get()
... print("Result Array C:", c_host)
... # Validate correctness
... expected = a_host + b_host
... if np.allclose(c_host, expected):
...    print("Result matches CPU computation.")
... else:
...    print("Mismatch found!")
...
Result Array C: [8. 8. 8. 8. 8. 8. 8. 8.]
Result matches CPU computation.

快速总结一下我们的工作:
● 我们首先分配所需大小的设备数组——无需担心主机内存。
● 使用 to_gpu() 和 .get() 时,PyCUDA 会自动处理主机到设备和设备到主机的移动。
● 我们的 CUDA C 代码在脚本运行时嵌入并编译,使我们能够快速迭代。
● 然后,我们控制块和网格的大小,从而获得应对任何规模或复杂度问题的灵活性。
● 最后,通过对照 CPU 版本检查结果,我们可以及早发现错误,并增强对 GPU 编程技能的信心。
这为我们提供了一种可靠、灵活的模式,可以使用 PyCUDA 在 Python 项目中嵌入和验证 CUDA C 内核。这项技能非常有用,对于我们接下来处理更具挑战性和创造性的 GPU 编程任务至关重要。

2.4 小结

总而言之,我们成功搭建了一个可靠且专业级的 GPU 编程环境,确保工作流程的每一步都保持稳健、可重复且能够满足我们的需求。我们首先加深了对 CUDA 驱动程序、CUDA 工具包和 NVIDIA 硬件之间关系的理解,确保所有组件都协调一致以实现最佳性能。我们学习了如何安装和验证工具包和驱动程序、更新环境变量,以及如何使用内置工具(例如 nvidia-smi 和 nvcc)来验证系统是否已准备好应对繁重的 GPU 工作负载。

通过运行 CUDA 设备查询并检查 GPU 属性(无论是在命令行还是在 Python 中),我们深入了解了设备的计算能力、可用内存和架构限制。这使我们能够调整代码和库选择,使其与硬件完美匹配。通过亲手使用 PyCUDA,我们练习了如何在 Python 中嵌入、编译和执行 CUDA C 内核,从而强化了主机-设备工作流程的各个方面。