cuda - 给GPU带来负担的小程序?
问题描述
为测试目的增加 GPU 负担和增加能耗的最有效方法是什么?
我确实希望程序尽可能小。是否有特定的内核函数可以完成这项工作?
任何关于 Metal 或 Cuda 的建议都是完美的。
解决方案
我在这里草拟一个可能的解决方案。您将需要一些实验来最大化 GPU 的热负载。一般来说,数据移动在能量上是昂贵的,比现代处理器中的计算要昂贵得多。因此,对大量数据进行洗牌会增加功耗。同时,我们希望计算单元对功耗的贡献增加。乘数往往是最大的耗电者;在现代处理器中,我们可能希望以 FMA(融合乘加)单元为目标。
各种 GPU 的双精度数学运算吞吐量较低,其他 GPU 的半精度数学运算吞吐量较低。因此,我们希望专注于负载计算部分的单精度数学。我们希望能够轻松更改计算与内存活动的比率。一种方法是使用以霍纳方案为基本构建块的多项式的展开评估,使用POLY_DEPTH
步骤。REPS
我们在一个循环中重复时间。在循环之前,我们从全局内存中检索源数据,在循环终止后,我们将结果存储到全局内存中。通过改变REPS
我们可以尝试不同的计算/内存平衡设置。
人们可以进一步试验指令级并行性、数据模式(因为乘法器的功耗通常因位模式而异),并通过使用 CUDA 流来实现内核执行和 PCIe 数据传输的重叠来添加 PCIe 活动。下面我只是使用了一些随机常数作为乘数数据。
显然,我们希望用大量线程填充 GPU。为此,我们可以使用一个相当小的THREADS_PER_BLK
值,为我们填充每个 SM 提供细粒度。我们可能希望将块数选择为 SM 数量的倍数以尽可能均匀地分配负载,或者使用一个MAX_BLOCKS
平均除以常见 SM 计数的值。我们应该接触多少源内存和目标内存取决于实验:我们可以将LEN
元素数组定义为块数的倍数。最后,我们要执行这样定义和配置的内核ITER
次数,以创建一段时间的连续负载。
请注意,当我们施加负载时,GPU 会发热,这反过来会进一步增加其功耗。要实现最大热负载,需要运行负载生成应用程序 5 分钟或更长时间。进一步注意,GPU 电源管理可能会动态降低时钟频率和电压以降低功耗,并且功率上限可能会在您达到热限制之前启动。根据 GPU,您可以将功率上限设置为高于该nvidia-smi
实用程序默认使用的功率上限。
根据 TechPowerUp 的 GPU-Z 实用程序的报告,下面的程序使我的 Quadro P2000 保持在功率上限,GPU 负载为 98%,内存控制器负载为 83%-86%。它肯定需要对其他 GPU 进行调整。
#include <stdlib.h>
#include <stdio.h>
#define THREADS_PER_BLK (128)
#define MAX_BLOCKS (65520)
#define LEN (MAX_BLOCKS * 1024)
#define POLY_DEPTH (30)
#define REPS (2)
#define ITER (100000)
// Macro to catch CUDA errors in CUDA runtime calls
#define CUDA_SAFE_CALL(call) \
do { \
cudaError_t err = call; \
if (cudaSuccess != err) { \
fprintf (stderr, "Cuda error in file '%s' in line %i : %s.\n",\
__FILE__, __LINE__, cudaGetErrorString(err) ); \
exit(EXIT_FAILURE); \
} \
} while (0)
// Macro to catch CUDA errors in kernel launches
#define CHECK_LAUNCH_ERROR() \
do { \
/* Check synchronous errors, i.e. pre-launch */ \
cudaError_t err = cudaGetLastError(); \
if (cudaSuccess != err) { \
fprintf (stderr, "Cuda error in file '%s' in line %i : %s.\n",\
__FILE__, __LINE__, cudaGetErrorString(err) ); \
exit(EXIT_FAILURE); \
} \
/* Check asynchronous errors, i.e. kernel failed (ULF) */ \
err = cudaDeviceSynchronize(); \
if (cudaSuccess != err) { \
fprintf (stderr, "Cuda error in file '%s' in line %i : %s.\n",\
__FILE__, __LINE__, cudaGetErrorString( err) ); \
exit(EXIT_FAILURE); \
} \
} while (0)
__global__ void burn (const float * __restrict__ src,
float * __restrict__ dst, int len)
{
int stride = gridDim.x * blockDim.x;
int tid = blockDim.x * blockIdx.x + threadIdx.x;
for (int i = tid; i < len; i += stride) {
float p = src[i] + 1.0;
float q = src[i] + 3.0f;
for (int k = 0; k < REPS; k++) {
#pragma unroll POLY_DEPTH
for (int j = 0; j < POLY_DEPTH; j++) {
p = fmaf (p, 0.68073987f, 0.8947237f);
q = fmaf (q, 0.54639739f, 0.9587058f);
}
}
dst[i] = p + q;
}
}
int main (int argc, char *argv[])
{
float *d_a, *d_b;
/* Allocate memory on device */
CUDA_SAFE_CALL (cudaMalloc((void**)&d_a, sizeof(d_a[0]) * LEN));
CUDA_SAFE_CALL (cudaMalloc((void**)&d_b, sizeof(d_b[0]) * LEN));
/* Initialize device memory */
CUDA_SAFE_CALL (cudaMemset(d_a, 0x00, sizeof(d_a[0]) * LEN)); // zero
CUDA_SAFE_CALL (cudaMemset(d_b, 0xff, sizeof(d_b[0]) * LEN)); // NaN
/* Compute execution configuration */
dim3 dimBlock(THREADS_PER_BLK);
int threadBlocks = (LEN + (dimBlock.x - 1)) / dimBlock.x;
if (threadBlocks > MAX_BLOCKS) threadBlocks = MAX_BLOCKS;
dim3 dimGrid(threadBlocks);
printf ("burn: using %d threads per block, %d blocks, %f GB\n",
dimBlock.x, dimGrid.x, 2e-9*LEN*sizeof(d_a[0]));
for (int k = 0; k < ITER; k++) {
burn<<<dimGrid,dimBlock>>>(d_a, d_b, LEN);
CHECK_LAUNCH_ERROR();
}
CUDA_SAFE_CALL (cudaFree(d_a));
CUDA_SAFE_CALL (cudaFree(d_b));
return EXIT_SUCCESS;
}
推荐阅读
- javascript - 从维基百科 API 中提取纯文本作为段落到我的网站
- c# - 在 C# 中缓存多个 XML 文件
- node.js - “docker-compose up”错误:无法启动服务提取器:OCI 运行时创建失败:container_linux.go:344
- python - 在pyspark中创建rdd的rdd
- reactjs - 在 React 中添加了另一个页面但无法导航
- c - 如何解决以下代码中的逻辑错误
- python - 具有“持续时间”变量的累积图
- angular - Angular Amplify init Cognito:默认情况下缺少配置文件配置
- python - 当 nan==nan 为 False 时,为什么 (nan,)==(nan,) 为 True?
- firebase - 如何向firebase中的列表字段添加值?