CUDA编程 - clock 学习记录

news2024/9/23 7:31:53

clock 学习记录

  • 一、完整代码
  • 二、核函数流程
  • 三、main 函数流程
  • 四、学习总结(共享内存的声明和使用):
    • 4.1、例子
    • 4.2、数据从全局内存复制到共享内存:

该程序利用CUDA并行计算能力,执行归约操作以找到每个块内的最小值,并使用 clock() 函数测量每个块的执行时间。主函数管理CUDA环境和内存,并处理计时数据以评估算法的性能

一、完整代码

// System includes
#include <assert.h>
#include <stdint.h>
#include <stdio.h>

// CUDA runtime
#include <cuda_runtime.h>

// helper functions and utilities to work with CUDA
#include <helper_cuda.h>
#include <helper_functions.h>

// This kernel computes a standard parallel reduction and evaluates the
// time it takes to do that for each block. The timing results are stored
// in device memory.
__global__ static void timedReduction(const float *input, float *output,
                                      clock_t *timer) {
  // __shared__ float shared[2 * blockDim.x];
  extern __shared__ float shared[];

  const int tid = threadIdx.x;
  const int bid = blockIdx.x;

  if (tid == 0) timer[bid] = clock();

  // Copy input.
  shared[tid] = input[tid];
  shared[tid + blockDim.x] = input[tid + blockDim.x];

  // Perform reduction to find minimum.
  for (int d = blockDim.x; d > 0; d /= 2) {
    __syncthreads();

    if (tid < d) {
      float f0 = shared[tid];
      float f1 = shared[tid + d];

      if (f1 < f0) {
        shared[tid] = f1;
      }
    }
  }

  // Write result.
  if (tid == 0) output[bid] = shared[0];

  __syncthreads();

  if (tid == 0) timer[bid + gridDim.x] = clock();
}

#define NUM_BLOCKS 64
#define NUM_THREADS 256

// It's interesting to change the number of blocks and the number of threads to
// understand how to keep the hardware busy.
//
// Here are some numbers I get on my G80:
//    blocks - clocks
//    1 - 3096
//    8 - 3232
//    16 - 3364
//    32 - 4615
//    64 - 9981
//
// With less than 16 blocks some of the multiprocessors of the device are idle.
// With more than 16 you are using all the multiprocessors, but there's only one
// block per multiprocessor and that doesn't allow you to hide the latency of
// the memory. With more than 32 the speed scales linearly.

// Start the main CUDA Sample here
int main(int argc, char **argv) {
  printf("CUDA Clock sample\n");

  // This will pick the best possible CUDA capable device
  int dev = findCudaDevice(argc, (const char **)argv);

  float *dinput = NULL;
  float *doutput = NULL;
  clock_t *dtimer = NULL;

  clock_t timer[NUM_BLOCKS * 2];
  float input[NUM_THREADS * 2];

  for (int i = 0; i < NUM_THREADS * 2; i++) {
    input[i] = (float)i;
  }

  checkCudaErrors(
      cudaMalloc((void **)&dinput, sizeof(float) * NUM_THREADS * 2));
  checkCudaErrors(cudaMalloc((void **)&doutput, sizeof(float) * NUM_BLOCKS));
  checkCudaErrors(
      cudaMalloc((void **)&dtimer, sizeof(clock_t) * NUM_BLOCKS * 2));

  checkCudaErrors(cudaMemcpy(dinput, input, sizeof(float) * NUM_THREADS * 2,
                             cudaMemcpyHostToDevice));

  timedReduction<<<NUM_BLOCKS, NUM_THREADS, sizeof(float) * 2 * NUM_THREADS>>>(
      dinput, doutput, dtimer);

  checkCudaErrors(cudaMemcpy(timer, dtimer, sizeof(clock_t) * NUM_BLOCKS * 2,
                             cudaMemcpyDeviceToHost));

  checkCudaErrors(cudaFree(dinput));
  checkCudaErrors(cudaFree(doutput));
  checkCudaErrors(cudaFree(dtimer));

  long double avgElapsedClocks = 0;

  for (int i = 0; i < NUM_BLOCKS; i++) {
    avgElapsedClocks += (long double)(timer[i + NUM_BLOCKS] - timer[i]);
  }

  avgElapsedClocks = avgElapsedClocks / NUM_BLOCKS;
  printf("Average clocks/block = %Lf\n", avgElapsedClocks);

  return EXIT_SUCCESS;
}

二、核函数流程

核函数 timedReduction:

__global__ static void timedReduction(const float *input, float *output,
                                      clock_t *timer) {
  extern __shared__ float shared[];

  const int tid = threadIdx.x;   // 线程在块内的索引
  const int bid = blockIdx.x;    // 块的索引

  // 记录每个块的开始时间
  if (tid == 0) 
    timer[bid] = clock();

  // 复制输入数据到共享内存中
  shared[tid] = input[tid];
  shared[tid + blockDim.x] = input[tid + blockDim.x];

  // 执行归约操作以找到最小值
  for (int d = blockDim.x; d > 0; d /= 2) {
    __syncthreads();

    if (tid < d) {
      float f0 = shared[tid];
      float f1 = shared[tid + d];

      if (f1 < f0) {
        shared[tid] = f1;
      }
    }
  }

  // 将结果写入全局内存
  if (tid == 0) 
    output[bid] = shared[0];

  __syncthreads();

  // 记录每个块的结束时间
  if (tid == 0) 
    timer[bid + gridDim.x] = clock();
}

语法解释:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
(这里假设每个线程负责复制两个元素,因此 blockDim.x 是块中的线程数。)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、main 函数流程

int main(int argc, char **argv) {
  // 初始化CUDA设备
  int dev = findCudaDevice(argc, (const char **)argv);

  // 主机和设备内存分配
  float *dinput = NULL;
  float *doutput = NULL;
  clock_t *dtimer = NULL;
  clock_t timer[NUM_BLOCKS * 2];
  float input[NUM_THREADS * 2];

  // 初始化输入数据
  for (int i = 0; i < NUM_THREADS * 2; i++) {
    input[i] = (float)i;
  }

  // CUDA内存分配
  checkCudaErrors(cudaMalloc((void **)&dinput, sizeof(float) * NUM_THREADS * 2));
  checkCudaErrors(cudaMalloc((void **)&doutput, sizeof(float) * NUM_BLOCKS));
  checkCudaErrors(cudaMalloc((void **)&dtimer, sizeof(clock_t) * NUM_BLOCKS * 2));

  // 将输入数据从主机复制到设备
  checkCudaErrors(cudaMemcpy(dinput, input, sizeof(float) * NUM_THREADS * 2,
                             cudaMemcpyHostToDevice));

  // 启动核函数
  timedReduction<<<NUM_BLOCKS, NUM_THREADS, sizeof(float) * 2 * NUM_THREADS>>>(
      dinput, doutput, dtimer);

  // 将计时数据从设备复制回主机
  checkCudaErrors(cudaMemcpy(timer, dtimer, sizeof(clock_t) * NUM_BLOCKS * 2,
                             cudaMemcpyDeviceToHost));

  // 释放分配的内存
  checkCudaErrors(cudaFree(dinput));
  checkCudaErrors(cudaFree(doutput));
  checkCudaErrors(cudaFree(dtimer));

  // 计算每个块的平均时钟周期数
  long double avgElapsedClocks = 0;
  for (int i = 0; i < NUM_BLOCKS; i++) {
    avgElapsedClocks += (long double)(timer[i + NUM_BLOCKS] - timer[i]);
  }
  avgElapsedClocks = avgElapsedClocks / NUM_BLOCKS;

  // 输出每个块的平均时钟周期数
  printf("Average clocks/block = %Lf\n", avgElapsedClocks);

  return EXIT_SUCCESS;
}

在这里插入图片描述

四、学习总结(共享内存的声明和使用):

在CUDA编程中,共享内存是一种特殊的内存类型,它是在块级别共享的。共享内存的主要优势在于其低延迟和高带宽,适合于需要快速访问和多次读写的数据。

4.1、例子

__global__ void reductionKernel(const float *input, float *output) {
    // 假设每个块的大小是 blockDim.x,即块内的线程数
    __shared__ float shared[256]; // 声明256个float类型的共享内存数组,大小在编译时确定
    
    int tid = threadIdx.x; // 线程在块内的索引
    int bid = blockIdx.x;  // 块的索引
    
    // 将数据从全局内存复制到共享内存
    shared[tid] = input[bid * blockDim.x + tid];
    __syncthreads(); // 确保所有线程都已经完成共享内存的数据复制
    
    // 归约操作以找到块内的最小值
    for (int stride = blockDim.x / 2; stride > 0; stride /= 2) {
        if (tid < stride) {
            shared[tid] = min(shared[tid], shared[tid + stride]);
        }
        __syncthreads(); // 确保所有线程完成本次循环的操作
    }
    
    // 将块内最小值写回全局内存
    if (tid == 0) {
        output[bid] = shared[0];
    }
}

4.2、数据从全局内存复制到共享内存:

shared[tid] = input[bid * blockDim.x + tid];

每个线程根据自己的 threadIdx.x 从全局输入数组 input 中读取数据,并将其存储在共享内存的 shared 数组中。bid * blockDim.x + tid 计算出每个线程在输入数组中的索引位置。

  • bid:表示当前线程所在的块的索引。在 CUDA 编程中,blockIdx.x 变量给出了当前线程块的全局索引。
  • blockDim.x:表示每个线程块中的线程数量。在 CUDA 中,blockDim.x 是一个内置变量,它给出了当前线程块的线程数量。

因此,bid * blockDim.x 就是当前块中的第一个线程在输入数组中的起始位置。这是因为每个线程块在处理输入数据时,通常会按照块大小分配数据段。例如,如果每个线程块有 blockDim.x = 256 个线程,则第一个线程块 (blockIdx.x = 0) 处理的输入数据范围是从索引 0 到 255。

  • tid:表示当前线程在其所属块中的索引。在 CUDA 编程中,threadIdx.x 变量给出了当前线程在其线程块中的局部索引。

因此,bid * blockDim.x + tid 就是当前线程在整个输入数组中的全局索引位置。每个线程根据其在块内的索引 tid 访问输入数组的对应元素,而 bid * blockDim.x 确定了当前块在输入数组中的起始位置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1927045.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PyTorch论文

2019-12 PyTorch: An Imperative Style, High-Performance Deep Learning Library 设计迎合4大趋势&#xff1a; 1. array-based (Tensor) 2. GPU加速 3. 自动求导 (Auto Differentiation) 4. 拥抱Python生态 4大设计原则&#xff1a; 1. 使用算法和数据开发者熟悉的Python做编…

IDEA Tomcat环境配置(CATALINA_BASE、乱码encoding)

IDEA Tomcat环境配置(CATALINA_BASE、乱码encoding) 基础配置 在idea中启动tomcat&#xff0c;默认会在用户目录生成一堆临时运行目录&#xff0c;因为他读的CATALINA_BASE为用户目录 如何处理 在tomcat配置里面的Stratup/Connection里面分别为Run和Debug在Pass environment v…

Profibus_DP转ModbusTCP网关模块连马保与上位机通讯

Profibus转ModbusTCP网关模块&#xff08;XD-ETHPB20&#xff09;广泛应用于工业自动化领域。例如&#xff0c;可以将Profibus网络中的传感器数据转换为ModbusTCP协议&#xff0c;实现数据的实时监控和远程控制。本文介绍了如何利用Profibus转ModbusTCP网关&#xff08;XD-ETHP…

【Linux】进程信号详解

一、信号 1. 信号的概念 Linux提供的让用户或进程给其他进程发送异步信息的一种方式&#xff0c;信号由进程发送的&#xff0c;属于软件中断。 2. 信号的作用 当 进程执行出现致命错误 或 进程所需的软件条件不具备 时&#xff0c;给操作系统提供的一种及时终止进程的机制当用…

【qt】考试系统项目

话不多说,先一睹芳颜 咱们的账号,题库和答案都是通过文件获取的. 话不多说,直接开干 目录 一.登录窗口1.界面设计2.邮箱验证3.登录验证 二.题库窗口1.考试计时2.布局管理器3.题库显示4.按钮布局5.计算分数 三.窗口交互四.完整代码五.结语 一.登录窗口 1.界面设计 这里添加背…

跟着操作,解决iPhone怎么清理内存难题

在如今智能手机功能日益强大的时代&#xff0c;我们使用手机拍照、录制视频、下载应用、存储文件等操作都会占用手机内存。当内存空间不足时&#xff0c;手机运行会变得缓慢&#xff0c;甚至出现卡顿、闪退等现象。因此&#xff0c;定期清理iPhone内存是非常必要的。那么&#…

人工智能与专家系统:构建智慧决策的未来

引言 随着信息技术的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经成为当今科技领域的一个重要分支&#xff0c;并在多个行业中展现出了巨大的潜力和影响力。人工智能通过模拟人类的智能行为&#xff0c;使计算机能够执行诸如学习、推理、解决问题和理解自然语言…

vite-服务端渲染(ssr)项目线上频繁刷新(踩坑记录)

今天来分享一个我在公司修改之前前端留下来的项目的坑。来说说大致的一个经过把&#xff0c;我老板说这个项目是之前的一个前端做的&#xff0c;用的是ssr服务端渲染的技术&#xff0c;不过他项目在线上会一直频繁的刷新&#xff0c;据说他想破脑袋都想不出来&#xff0c;最终因…

周鸿祎为什么建议Java、前端、大数据、PHP开发都要学一下大模型?_ai大模型全栈工程师跟java有关吗

ChatGPT的出现在全球掀起了AI大模型的浪潮&#xff0c;2023年可以被称为AI元年&#xff0c;AI大模型以一种野蛮的方式&#xff0c;闯入你我的生活之中。 从问答对话到辅助编程&#xff0c;从图画解析到自主创作&#xff0c;AI所展现出来的能力&#xff0c;超出了多数人的预料&…

【企业级监控】Zabbix监控网站并发连接数

Zabbix自定义监控项与触发器 文章目录 Zabbix自定义监控项与触发器资源列表基础环境前言一、什么是zabbix的Key值二、获取远程Key值2.1、获得主机的Key值2.2、被监控端安装Agent2.3、zabbix_get命令获取Agent数据举例2.3.1、zabbx_get获取cpu核心数2.3.2、获取目标主机系统和内…

windows中超详细深度学习环境配置之安装显卡驱动、cuda、cudnn、pytorch、torchvision、pycharm

超详细介绍安装Gpu版本的pytorch深度学习环境 一、显卡驱动安装1.1 下载驱动1.2 安装驱动 二、cuda安装2.1 下载cuda2.2 安装cuda2.3 检查cuda是否安装成功 三、安装cudnn3.1 cudnn下载3.2 cudnn安装 四、安装miniconda4.1 miniconda下载4.2 miniconda安装4.3 添加环境变量 五、…

数字营销以打造“会员体系”为主要目标的好处和优势

​蚓链数字化营销实践观察&#xff1a;在数字化时代&#xff0c;企业的营销方式发生了深刻的变革。会员体系作为一种常见的营销策略&#xff0c;在数字营销领域中发挥着越来越重要的作用。 首先&#xff0c;我们来总结一下会员体系的特点和优势 &#xff08;一&#xff09;个性…

图注意力网络

【图书推荐】《图神经网络基础、模型与应用实战》_搭建神经网络需要看什么书-CSDN博客 图注意力网络的由来和发展 图注意力网络&#xff08;GAT&#xff09;是一种图神经网络&#xff08;GNN&#xff09;模型&#xff0c;最早由Petar Velickovic等在2017年提出。它的设计灵感…

Java基础-I/O流

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 字节流 定义 说明 InputStream与OutputStream示意图 说明 InputStream的常用方法 说明 OutputStrea…

Qt基础 | Qt Creator的基本介绍与使用 | 在Visual Studio中创建Qt项目

文章目录 一、Qt Creator的基本介绍与使用1.新建一个项目2.项目的文件组成3.项目文件介绍3.1 项目管理文件3.2 界面文件3.3 主函数文件3.4 窗体相关的文件 4.项目的编译、调试与运行 二、在Visual Studio中创建Qt项目 Qt C开发环境的安装&#xff0c;请参考https://liujie.blog…

C1W1.LAB.Preprocessing+Word frequencies+Logistic_regression_model

理论课&#xff1a;C1W1.Sentiment Analysis with Logistic Regression 文章目录 预处理导入包Twitter dataset简介查看原始文本处理原始文本处理超链接、Twitter 标记和样式分词去除标点和停用词词干处理 process_tweet() 词频构建与可视化导入包加载数据集字典字典实例添加或…

cesium 实现地图环境功能 - 雨,雪,雾特效

需求背景解决效果Codeindex.vuefogEffect.tsrain.glslsnow.glslfog.glsl 需求背景 需要实现天气模拟&#xff0c;日照模拟功能&#xff0c;提高三维实景效果 解决效果 Code 注意&#xff1a;我以下glsl文件时基于 webgl1.0&#xff0c;即cesium&#xff0c;创建球的时候&…

ES的使用示例

1.安装 ES的安装对springboot的版本配置要求很高&#xff0c;需要根据如下的目录下载对应的版本。 查看自己项目所使用的springboot和spring的版本&#xff0c;对应下载文件。 下载链接地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-…

微软GraphRAG原理介绍(附带部分源码)

我们前几天写了一篇文章&#xff0c;简单跑了一下GraphRAG看了下效果&#xff0c;没看过这篇文章的可以看下https://www.luxinfeng.top/article/动手实操微软开源的GraphRAG。今天我们介绍一下GraphRAG的实现原理&#xff0c;关于实验对比的内容&#xff0c;我会在下一篇文章中…

48V电源架构解析

48V电源架构解析 48V系统的诞生 汽车在1918年引入蓄电池&#xff0c;到1920年逐渐普及&#xff0c;当时的电池电压是6V。后来&#xff0c;随着内燃机排量的增加以及高压缩比内燃机的出现&#xff0c;6V系统已经不能满足需求&#xff0c;于是在1950年引入了12V系统。大多数汽车…