Cuda By Example - 8 (性能测量)

news2024/10/20 8:26:09

时间戳记录API

使用constant内存,究竟带来多少性能提升,如何尽可能精确的测量GPU完成某项任务所花的时间?CUDA提供了cudaEvent_t 以及 CUDA event API来做运行时间的测量。

cudaError_t cudaEventCreate(cudaEvent_t *event);

cudaError_t cudaEventRecord(cudaEvent_t event, cudaStream_t stream = 0);

cudaError_t cudaEventSynchronize(cudaEvent_t event);

cudaError_t cudaEventElapsedTime(float *ms, cudaEvent_t start, cudaEvent_t end);

cudaError_t cudaEventDestroy(cudaEvent_t event);

CUDA的event本质上是一个时间戳,用户通过调用cudaEventRecord指定记录当前时刻。完成某一时刻记录只需三步:

cudaEvent_t start;
cudaEventCreate(&start);
cudaEventRecord(start, 0);

不再使用时,也不要忘了清除event。

cudaEventDestroy(start);

要测量某一块代码运行的时间,除了记录起点的时间点,还需要记录结束的时间点。将两者相减,即可获得运行时间。需要注意的是,当我们在结束点调用cudaEventRecord后,并不能保证event的值马上就可用了。cudaEventRecord是异步,它没有等到GPU更新event的时间就返回了,所以在使用event之前,必须调用cudaEventSynchronize,确保event有效。

event记录的时间戳的差值由CUDA  API函数cudaEventElapsedTime来计算,结果由第一个参数传回。

添加时间测量到光线追踪

有了前面的经验,整个代码不需要做进一步解释了。贴出没有使用constant内存和使用了constant内存的代码,做个对照。

non constant内存代码:

#include "cuda.h"
#include "../common/book.h"
#include "../common/cpu_bitmap.h"

#define DIM 1024

#define rnd( x ) (x * rand() / RAND_MAX)
#define INF     2e10f

struct Sphere {
    float   r,b,g;
    float   radius;
    float   x,y,z;
    __device__ float hit( float ox, float oy, float *n ) {
        float dx = ox - x;
        float dy = oy - y;
        if (dx*dx + dy*dy < radius*radius) {
            float dz = sqrtf( radius*radius - dx*dx - dy*dy );
            *n = dz / sqrtf( radius * radius );
            return dz + z;
        }
        return -INF;
    }
};
#define SPHERES 20


__global__ void kernel( Sphere *s, unsigned char *ptr ) {
    // map from threadIdx/BlockIdx to pixel position
    int x = threadIdx.x + blockIdx.x * blockDim.x;
    int y = threadIdx.y + blockIdx.y * blockDim.y;
    int offset = x + y * blockDim.x * gridDim.x;
    float   ox = (x - DIM/2);
    float   oy = (y - DIM/2);

    float   r=0, g=0, b=0;
    float   maxz = -INF;
    for(int i=0; i<SPHERES; i++) {
        float   n;
        float   t = s[i].hit( ox, oy, &n );
        if (t > maxz) {
            float fscale = n;
            r = s[i].r * fscale;
            g = s[i].g * fscale;
            b = s[i].b * fscale;
            maxz = t;
        }
    } 

    ptr[offset*4 + 0] = (int)(r * 255);
    ptr[offset*4 + 1] = (int)(g * 255);
    ptr[offset*4 + 2] = (int)(b * 255);
    ptr[offset*4 + 3] = 255;
}


// globals needed by the update routine
struct DataBlock {
    unsigned char   *dev_bitmap;
    Sphere          *s;
};

int main( void ) {
    DataBlock   data;
    // capture the start time
    cudaEvent_t     start, stop;
    HANDLE_ERROR( cudaEventCreate( &start ) );
    HANDLE_ERROR( cudaEventCreate( &stop ) );
    HANDLE_ERROR( cudaEventRecord( start, 0 ) );

    CPUBitmap bitmap( DIM, DIM, &data );
    unsigned char   *dev_bitmap;
    Sphere          *s;


    // allocate memory on the GPU for the output bitmap
    HANDLE_ERROR( cudaMalloc( (void**)&dev_bitmap,
                              bitmap.image_size() ) );
    // allocate memory for the Sphere dataset
    HANDLE_ERROR( cudaMalloc( (void**)&s,
                              sizeof(Sphere) * SPHERES ) );

    // allocate temp memory, initialize it, copy to
    // memory on the GPU, then free our temp memory
    Sphere *temp_s = (Sphere*)malloc( sizeof(Sphere) * SPHERES );
    for (int i=0; i<SPHERES; i++) {
        temp_s[i].r = rnd( 1.0f );
        temp_s[i].g = rnd( 1.0f );
        temp_s[i].b = rnd( 1.0f );
        temp_s[i].x = rnd( 1000.0f ) - 500;
        temp_s[i].y = rnd( 1000.0f ) - 500;
        temp_s[i].z = rnd( 1000.0f ) - 500;
        temp_s[i].radius = rnd( 100.0f ) + 20;
    }
    HANDLE_ERROR( cudaMemcpy( s, temp_s,
                                sizeof(Sphere) * SPHERES,
                                cudaMemcpyHostToDevice ) );
    free( temp_s );

    // generate a bitmap from our sphere data
    dim3    grids(DIM/16,DIM/16);
    dim3    threads(16,16);
    kernel<<<grids,threads>>>( s, dev_bitmap );

    // copy our bitmap back from the GPU for display
    HANDLE_ERROR( cudaMemcpy( bitmap.get_ptr(), dev_bitmap,
                              bitmap.image_size(),
                              cudaMemcpyDeviceToHost ) );

    // get stop time, and display the timing results
    HANDLE_ERROR( cudaEventRecord( stop, 0 ) );
    HANDLE_ERROR( cudaEventSynchronize( stop ) );
    float   elapsedTime;
    HANDLE_ERROR( cudaEventElapsedTime( &elapsedTime,
                                        start, stop ) );
    printf( "Time to generate:  %3.1f ms\n", elapsedTime );

    HANDLE_ERROR( cudaEventDestroy( start ) );
    HANDLE_ERROR( cudaEventDestroy( stop ) );

    HANDLE_ERROR( cudaFree( dev_bitmap ) );
    HANDLE_ERROR( cudaFree( s ) );

    // display
    bitmap.display_and_exit();
}

constant内存代码:

#include "cuda.h"
#include "../common/book.h"
#include "../common/cpu_bitmap.h"

#define DIM 1024

#define rnd( x ) (x * rand() / RAND_MAX)
#define INF     2e10f

struct Sphere {
    float   r,b,g;
    float   radius;
    float   x,y,z;
    __device__ float hit( float ox, float oy, float *n ) {
        float dx = ox - x;
        float dy = oy - y;
        if (dx*dx + dy*dy < radius*radius) {
            float dz = sqrtf( radius*radius - dx*dx - dy*dy );
            *n = dz / sqrtf( radius * radius );
            return dz + z;
        }
        return -INF;
    }
};
#define SPHERES 20

__constant__ Sphere s[SPHERES];

__global__ void kernel( unsigned char *ptr ) {
    // map from threadIdx/BlockIdx to pixel position
    int x = threadIdx.x + blockIdx.x * blockDim.x;
    int y = threadIdx.y + blockIdx.y * blockDim.y;
    int offset = x + y * blockDim.x * gridDim.x;
    float   ox = (x - DIM/2);
    float   oy = (y - DIM/2);

    float   r=0, g=0, b=0;
    float   maxz = -INF;
    for(int i=0; i<SPHERES; i++) {
        float   n;
        float   t = s[i].hit( ox, oy, &n );
        if (t > maxz) {
            float fscale = n;
            r = s[i].r * fscale;
            g = s[i].g * fscale;
            b = s[i].b * fscale;
            maxz = t;
        }
    } 

    ptr[offset*4 + 0] = (int)(r * 255);
    ptr[offset*4 + 1] = (int)(g * 255);
    ptr[offset*4 + 2] = (int)(b * 255);
    ptr[offset*4 + 3] = 255;
}

// globals needed by the update routine
struct DataBlock {
    unsigned char   *dev_bitmap;
};

int main( void ) {
    DataBlock   data;
    // capture the start time
    cudaEvent_t     start, stop;
    HANDLE_ERROR( cudaEventCreate( &start ) );
    HANDLE_ERROR( cudaEventCreate( &stop ) );
    HANDLE_ERROR( cudaEventRecord( start, 0 ) );

    CPUBitmap bitmap( DIM, DIM, &data );
    unsigned char   *dev_bitmap;

    // allocate memory on the GPU for the output bitmap
    HANDLE_ERROR( cudaMalloc( (void**)&dev_bitmap,
                              bitmap.image_size() ) );

    // allocate temp memory, initialize it, copy to constant
    // memory on the GPU, then free our temp memory
    Sphere *temp_s = (Sphere*)malloc( sizeof(Sphere) * SPHERES );
    for (int i=0; i<SPHERES; i++) {
        temp_s[i].r = rnd( 1.0f );
        temp_s[i].g = rnd( 1.0f );
        temp_s[i].b = rnd( 1.0f );
        temp_s[i].x = rnd( 1000.0f ) - 500;
        temp_s[i].y = rnd( 1000.0f ) - 500;
        temp_s[i].z = rnd( 1000.0f ) - 500;
        temp_s[i].radius = rnd( 100.0f ) + 20;
    }
    HANDLE_ERROR( cudaMemcpyToSymbol( s, temp_s, 
                                sizeof(Sphere) * SPHERES) );
    free( temp_s );

    // generate a bitmap from our sphere data
    dim3    grids(DIM/16,DIM/16);
    dim3    threads(16,16);
    kernel<<<grids,threads>>>( dev_bitmap );

    // copy our bitmap back from the GPU for display
    HANDLE_ERROR( cudaMemcpy( bitmap.get_ptr(), dev_bitmap,
                              bitmap.image_size(),
                              cudaMemcpyDeviceToHost ) );

    // get stop time, and display the timing results
    HANDLE_ERROR( cudaEventRecord( stop, 0 ) );
    HANDLE_ERROR( cudaEventSynchronize( stop ) );
    float   elapsedTime;
    HANDLE_ERROR( cudaEventElapsedTime( &elapsedTime,
                                        start, stop ) );
    printf( "Time to generate:  %3.1f ms\n", elapsedTime );

    HANDLE_ERROR( cudaEventDestroy( start ) );
    HANDLE_ERROR( cudaEventDestroy( stop ) );

    HANDLE_ERROR( cudaFree( dev_bitmap ) );

    // display
    bitmap.display_and_exit();
}

以下是我在RTX 2060上运行得到的结果:

没有使用constant内存

使用constant内存之后

测试时数据有浮动。

我将球体个数改成400个后,两者的数据分别为~4ms, ~5ms。大约能带来20%左右的性能提升。

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

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

相关文章

架构设计笔记-22-论文

1.论企业应用系统的数据持久层架构设计 2.论企业信息化规划的实施与应用 3.论企业应用系统的分层架构风格 4.论分布式存储架构系统设计 5.论云原生架构及其应用 6.论企业集成架构设计及应用 7.论数据湖技术及其应用 8.论系统安全架构设计及其应用 9.论企业集成平台的理解与应用…

【双指针算法】快乐数

1.题目解析 2.算法分析 由图可知&#xff0c;不管是最后可以变成1的还是不可以变成1的都相当于形成环了&#xff0c;只是成环处值不一样 问题转变成&#xff0c;判断链表是否有环 采用双指针&#xff0c;快慢指针算法 1.定义快慢指针2.慢指针每次向后移动一步&#xff0c;快…

ES-入门-javaApi-文档-新增-删除

新增指定索引的文档数据的代码如下&#xff1a; package com.atgulgu.es.test;import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.http.HttpHost; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexRe…

UNI VFX Missiles Explosions for Visual Effect Graph

Unity URP和HDRP的通用视觉效果 使用在视觉效果图中制作的高性能GPU粒子系统。 无需进入视觉效果图编辑器即可轻松自定义VFX。 使用(VFX)事件——一个游戏对象可存储多个效果,这些效果可通过C#或视觉脚本触发。 总共32个事件(不包括“停止”事件)。 ❓ 什么是(VFX)事件?…

STM32Cubemx 配置ADC(HAL库)

一、ADC几种模式 1、扫描模式&#xff1a; 使用STM32CUBEMX配置了多通道后&#xff0c;这一项默认开启且无法设置成关闭。这个模式就是自动扫描你开启的所有通道进行转换&#xff0c;直至转换完。例如你开启了CH0、CH1、CH2、CH3这四个通道&#xff0c;启动转换后ADC会自动将这…

动态规划原理及算法题(1)

课程规划会分为四个阶段进行&#xff1a; 1.题目解析 2.讲解算法原理(动态规划的原理) 3.编写代码 4.空间优化 1. 第 N 个泰波那契数&#xff08;easy&#xff09; 泰波那契数相当于斐波那契数的孪生兄弟&#xff0c;是它的加强版。 1.题目解析 2.讲解算法原理 如果用动态规…

Java中的一些名词概念

**函数式接口:** 概念&#xff1a;一个接口中的抽象方法只有一个&#xff0c;那么这个接口就是一个函数式接口。形参: 形参变量是**功能函数里的变量**&#xff0c;只有<u>在被调用的时候才分配内存单元</u>&#xff0c;<u>调用结束后立即释放</u>。…

Unity使用Git及GitHub进行项目管理

git: 工作区,暂存区(存放临时要存放的内容),代码仓库区1.初始化 git init 此时展开隐藏项目,会出现.git文件夹 2.减小项目体积 touch .gitignore命令 创建.gitignore文件夹 gitignore文件夹的内容 gitignore中添加一下内容 # This .gitignore file should be place…

Java项目-基于Springboot的应急救援物资管理系统项目(源码+说明).zip

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

Ubuntu20.04编译安卓aosp 15源码编译到模拟器运行

背景&#xff1a; aosp15也开始悄悄在各个手机厂商开始酝酿了&#xff0c;感叹时间很快&#xff0c;今天也准备针对aosp15进行一下源码环境的搭建&#xff0c;整体aosp15的搭建和13/14其实没啥大的差别&#xff0c;只不过在lunch目标这个地方确实很大不同&#xff0c;还有就是…

HCIP-HarmonyOS Application Developer 习题(十四)

&#xff08;多选&#xff09;1、HarmonyOs为应用提供丰富的Al(Artificial Intelligence)能力&#xff0c;支持开箱即用。下列哪些是它拥有的AI能力? A、通用文字识别 B、词性标注 C、实体识别 D、语音播报 答案&#xff1a;ABCD 分析&#xff1a; AI能力简介二维码生成根据开…

工业级三防平板在工厂极端环境下保障稳定运行

在现代工业环境中&#xff0c;尤其是在工厂车间&#xff0c;设备和技术的稳定性直接关系到生产效率与产品质量。然而&#xff0c;极端的工作条件常常给电子设备的使用带来了不小的挑战。为此&#xff0c;市场上出现了一种专为工业应用设计大尺寸手持三防平板电脑。这种设备以其…

2024年十大前沿图像分割模型汇总:工作机制、优点和缺点介绍

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

antd vue 输入框高亮设置关键字

<highlight-textareaplaceholder"请输入主诉"type"textarea"v-model"formModel.mainSuit":highlightKey"schema.componentProps.highlightKey"></highlight-textarea> 参考链接原生input&#xff0c;textarea demo地址 …

【前端】如何制作一个自己的网站(11)

接上文。 除了前面的颜色样式外&#xff0c;字体样式和文本样式也是网页设计中的重要组成部分。 合适的字体和文本排版&#xff0c;不仅可以使页面更加美观&#xff0c;也可以提升用户体验。接下来&#xff0c;我们先来看看CSS如何设置字体样式。 字体样式 同时设置了字体样…

经典算法整理(Go语言实现)

经典算法系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 第一章 回溯算法 第二章 贪心算法 第三章 动态规划 第四章 单调栈 第五章 图论 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可…

机器学习课程学习周报十七

机器学习课程学习周报十七 文章目录 机器学习课程学习周报十七摘要Abstract一、机器学习部分1. 变分推断/推理1.1 证据下界1.2 q ( z ) {q(z)} q(z)的选取 2. VAE2.1 Auto-Encoder的简单回顾2.2 为什么提出VAE2.3 VAE的数学原理 3. Diffusion Model的数学原理3.1 Training算法…

React(五) 受控组件和非受控组件; 获取表单元素的值。高阶组件(重点),Portals; Fragment组件;严格模式StrictMode

文章目录 一、受控组件1. 什么是受控组件2. 收集input框内容3. 收集checkBox的值4. 下拉框select总结 二、非受控组件三、高阶组件1. 高阶组件的概念 (回顾高阶函数)2. 高阶组件应用&#xff1a;注入props(1) 高阶组件给---函数式组件注入props(2) 高阶组件给---类组件注入prop…

开源的存储引擎--cantian

cantian 上次稼先社会活动之后&#xff0c;在北京签售的时候见到了三位参天的核心人物。我有感于他们的热情、务实和坦诚&#xff0c;我觉得还应该在深入的做一些事情。至少可以安装体验一下&#xff0c;做做推广。毕竟现在务实的产品不多了&#xff0c;很多都是浮躁的宣传。为…

从一个事故中理解 Redis(几乎)所有知识点

作者&#xff1a;看破 一、简单回顾 事故回溯总结一句话&#xff1a; &#xff08;1&#xff09;因为大 KEY 调用量&#xff0c;随着白天自然流量趋势增长而增长&#xff0c;最终在业务高峰最高点期占满带宽使用 100%。 &#xfeff; &#xfeff; &#xff08;2&#xff…