cuda编程学习——卷积计算CUDA、Pytorch比较 干货向(六)

news2024/11/29 3:46:12

前言

参考资料:

高升博客
《CUDA C编程权威指南》
以及 CUDA官方文档
CUDA编程:基础与实践 樊哲勇
参考B站:蒙特卡洛加的树
在这里插入图片描述

文章所有代码可在我的GitHub获得,后续会慢慢更新

文章、讲解视频同步更新公众《AI知识物语》,B站:出门吃三碗饭

0:CUDA Pytorch关系

在这里插入图片描述
在这里插入图片描述
图片来源、详细文章参考点这里

卷积计算

在这里插入图片描述
在这里插入图片描述

1:CUDA卷积计算编程

代码概述:
(1) CHECK 用来debug错误检测(建议做好习惯)
(2)getThreadNum() 获取线程相关信息
(3)conv 卷积计算
(4)main函数里面
在CPU上开空间,定义数据,img和kernel(卷积核)
CPU数据拷贝到GPU计算
GPU计算 (运行核函数 conv)
计算结果GPU拷贝到CPU
输出
释放空间

#include<stdint.h>
#include<cuda.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#include <math.h>

const int NUM_REPEATS = 10;

#define CHECK(call)                                   \
do                                                    \
{                                                     \
    const cudaError_t error_code = call;              \
    if (error_code != cudaSuccess)                    \
    {                                                 \
        printf("CUDA Error:\n");                      \
        printf("    File:       %s\n", __FILE__);     \
        printf("    Line:       %d\n", __LINE__);     \
        printf("    Error code: %d\n", error_code);   \
        printf("    Error text: %s\n",                \
            cudaGetErrorString(error_code));          \
        exit(1);                                      \
    }                                                 \
} while (0)



static void HandleError(cudaError_t err,
    const char* file,

    int line)
{
    if (err != cudaSuccess)
    {
        printf("%s in %s at line %d\n",
            cudaGetErrorString(err),
            file, line);
        exit(EXIT_FAILURE);
    }
}

#define HANDLE_ERROR(err) (HandleError(err, __FILE__, __LINE__))

int getThreadNum()
{
    cudaDeviceProp prop;
    int count;

    CHECK(cudaGetDeviceCount(&count));
    printf("gpu num %d\n", count);
    CHECK(cudaGetDeviceProperties(&prop, 0));
    printf("max thread num: %d\n", prop.maxThreadsPerBlock);
    printf("max grid dimensions: %d, %d, %d)\n",
        prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2]);
    return prop.maxThreadsPerBlock;
}

__global__ void conv(float* img, float* kernel, float* result,
    int width, int height, int kernelSize)
{
    int ti = threadIdx.x;
    int bi = blockIdx.x;
    int id = (bi * blockDim.x + ti);
    if (id >= width * height)
    {
        return;
    }
    int row = id / width;
    int col = id % width;
    for (int i = 0; i < kernelSize; ++i)
    {
        for (int j = 0; j < kernelSize; ++j)
        {
            float imgValue = 0;
            int curRow = row - kernelSize / 2 + i;
            int curCol = col - kernelSize / 2 + j;
            if (curRow < 0 || curCol < 0 || curRow >= height || curCol >= width)
            {
            }
            else
            {
                imgValue = img[curRow * width + curCol];
            }
            result[id] += kernel[i * kernelSize + j] * imgValue;
        }

    }
}

int main()
{
    int width = 1000;
    int height = 1000;
    float* img = new float[width * height];
    for (int row = 0; row < height; ++row)
    {
        for (int col = 0; col < width; ++col)
        {
            img[col + row * width] = (col + row) % 256;
        }
    }

    int kernelSize = 3;
    float* kernel = new float[kernelSize * kernelSize];
    for (int i = 0; i < kernelSize * kernelSize; ++i)
    {
        kernel[i] = i % kernelSize - 1;
    }

    float* imgGpu;
    float* kernelGpu;
    float* resultGpu;

    CHECK(cudaMalloc((void**)&imgGpu, width * height * sizeof(float)));
    CHECK(cudaMalloc((void**)&kernelGpu, kernelSize * kernelSize * sizeof(float)));
    CHECK(cudaMalloc((void**)&resultGpu, width * height * sizeof(float)));

    CHECK(cudaMemcpy(imgGpu, img,
        width * height * sizeof(float), cudaMemcpyHostToDevice));
    CHECK(cudaMemcpy(kernelGpu, kernel,
        kernelSize * kernelSize * sizeof(float), cudaMemcpyHostToDevice));

    int threadNum = getThreadNum();
    int blockNum = (width * height - 0.5) / threadNum + 1;

    float t_sum = 0;
    float t2_sum = 0;
    for (int repeat = 0; repeat <= NUM_REPEATS; ++repeat)
    {
        cudaEvent_t start, stop;
        CHECK(cudaEventCreate(&start));
        CHECK(cudaEventCreate(&stop));
        CHECK(cudaEventRecord(start));
        cudaEventQuery(start);

        conv << <blockNum, threadNum >> >
            (imgGpu, kernelGpu, resultGpu, width, height, kernelSize);

        CHECK(cudaEventRecord(stop));
        CHECK(cudaEventSynchronize(stop));
        float elapsed_time;
        CHECK(cudaEventElapsedTime(&elapsed_time, start, stop));
        printf("Time = %g ms.\n", elapsed_time);

        if (repeat > 0)
        {
            t_sum += elapsed_time;
            t2_sum += elapsed_time * elapsed_time;
        }

        CHECK(cudaEventDestroy(start));
        CHECK(cudaEventDestroy(stop));
    }

    const float t_ave = t_sum / NUM_REPEATS;
    const float t_err = sqrt(t2_sum / NUM_REPEATS - t_ave * t_ave);
    printf("Time = %g +- %g ms.\n", t_ave, t_err);


    float* result = new float[width * height];
    CHECK(cudaMemcpy(result, resultGpu,
        width * height * sizeof(float), cudaMemcpyDeviceToHost));

    // visualization
    printf("img\n");
    for (int row = 0; row < 10; ++row)
    {
        for (int col = 0; col < 10; ++col)
        {
            printf("%2.0f ", img[col + row * width]);
        }
        printf("\n");
    }
    printf("kernel\n");
    for (int row = 0; row < kernelSize; ++row)
    {
        for (int col = 0; col < kernelSize; ++col)
        {
            printf("%2.0f ", kernel[col + row * kernelSize]);
        }
        printf("\n");
    }

    printf("result\n");
    for (int row = 0; row < 10; ++row)
    {
        for (int col = 0; col < 10; ++col)
        {
            printf("%2.0f ", result[col + row * width]);
        }
        printf("\n");
    }


    return 0;
}

运行时间:

计算1次
在这里插入图片描述
计算50次
在这里插入图片描述

2:Pytorch卷积计算

GPU

import time

import torch
import torch.nn.functional as F
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")


width = 1000;
height = 1000;
#img =torch.ones([width,height])
img =torch.randn([width,height])
img = img.to(device)

kernel = torch.tensor([[-1.0, 0.0, 1.0],
                       [-1.0, 0.0, 1.0],
                       [-1.0, 0.0, 1.0]])


#input = torch.reshape(input, (1, 1, 5, 5))
img = torch.reshape(img, (1, 1, width, height))
kernel = torch.reshape(kernel, (1, 1, 3, 3))
kernel = kernel.to(device)

output = F.conv2d(img, kernel, stride=1).to(device)
# torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1)
# 返回的是 s , 乘1000 为ms
start = time.perf_counter()

# output = F.conv2d(img, kernel, stride=1).to(device)
output = F.conv2d(img, kernel, stride=1).to(device)

end = time.perf_counter()

print("startime:",start)
print("endtime:",end)
print("total:",end-start)
print("output:size===>",output.shape)
print("output tensor:",output)

计算1次
在这里插入图片描述

计算50次
在这里插入图片描述

CPU

import time
import torch
import torch.nn.functional as F

width = 1000;
height = 1000;
#img =torch.ones([width,height])
img =torch.randn([width,height])

# print(img.shape)
# print(img)

kernel = torch.tensor([[-1.0, 0.0, 1.0],
                       [-1.0, 0.0, 1.0],
                       [-1.0, 0.0, 1.0]])
#input = torch.reshape(input, (1, 1, 5, 5))
img = torch.reshape(img, (1, 1, width, height))
kernel = torch.reshape(kernel, (1, 1, 3, 3))

# print(kernel.shape)

# torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1)

start = time.perf_counter()

# output = F.conv2d(img, kernel, stride=1).to(device)

output = F.conv2d(img, kernel, stride=1)

end = time.perf_counter()

print("startime:",start)
print("endtime:",end)
print("total:",end-start)
print("output:size===>",output.shape)
print("output tensor:",output)

计算1次
在这里插入图片描述

计算50次
在这里插入图片描述

性能对比

                               1epoch                  50epoch
                               
CUDA                           1.4-2.2ms == 1.6ms       9ms    

Pytorch(CPU)                 10ms                      290ms

Pytorch(GPU)                 0.1ms                    2.4ms

7:总结(优化性能)

优化性能必要条件:

(1)数据传输比例较小。
(2) 核函数的算术强度较高。
(3)核函数中定义的线程数目较多。

编程手段:

• 减少主机与设备之间的数据传输
• 提高核函数的算术强度
• 增大核函数的并行规模

8:拓展

(1)数据传输的比例
如果一个程序的目的仅仅是计算两个数组的和,那么 用GPU可能比用CPU还要慢。这是因为,花在数据传输(CPU与GPU之间)上的时间比计算(求和)本身还要多很多。GPU计算核心和设备内存之间数据传输的峰值理论带宽要 远高于 GPU 和 CPU 之间数据传输的带宽。
设计算任务不是做一次数组相加的计算,而是做10000次数组相加的计算,而且只需 要在程序的开始和结束部分进行数据传输,那么数据传输所占的比例将可以忽略不计。此时,整个 CUDA 程序的性能就大为提高。

(2)算术强度
数组相加的问题之 所以很难得到更高的加速比,是因为该问题的算术强度(arithmetic intensity)不高。一个 计算问题的算术强度指的是其中算术操作的工作量与必要的内存操作的工作量之比。
例如, 在数组相加的问题中,在对每一对数据进行求和时需要先将一对数据从设备内存中取出来, 然后对它们实施求和计算,最后再将计算的结果存放到设备内存。这个问题的算术强度其 实是不高的,因为在取两次数据、存一次数据的情况下只做了一次求和计算。在CUDA中,设备内存的读、写都是代价高昂(比较耗时)的。

(3)并行规模:
并行规模可用 GPU 中总的线程数目来衡量。
从硬件的角度来看,一个GPU由多个流多处理器(streaming multiprocessor,SM)构成,而每个SM中有若干CUDA核心。每个SM是相对独立的。从开普勒架构到伏特架 构,一个SM中最多能驻留(reside)的线程个数是 2048。对于图灵架构,该数目是 1024。 一块GPU中一般有几个到几十个SM(取决于具体的型号)。所以,一块GPU一共可以驻 留几万到几十万个线程。如果一个核函数中定义的线程数目远小于这个数的话,就很难得到很高的加速比。
在这里插入图片描述

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

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

相关文章

勿踩,电商实时聊天常见错误

实时聊天现在在电商企业与SaaS行业已经是必备的服务&#xff0c;他的实施很简单&#xff1a;您找到适合您的工具&#xff0c;将其打开并将其放在所有客户都可以看到的地方。但是无休止的互动冲击&#xff0c;措辞不佳或沟通不畅的问题&#xff0c;客户的24/7期望&#xff0c;在…

【Apache网页与安全优化】

一.介绍 在企业中&#xff0c;部署Apache后只采用默认的配置参数&#xff0c;会引发网站很多问题&#xff0c;换言之默认配置是针对以前较低的服务器配置的&#xff0c;以前的配置已经不适用当今互联网时代。为了适应企业需求&#xff0c;就需要考虑如何提升Apache的性能与稳定…

Feign入门使用 OpenFeign 日志增强 超时控制

一、概述 Feign是一个声明式的web服务的客户端&#xff0c;Feign就是参考Ribbon添加了注解接口的绑定器。 我们封装一些客户端类来包装对其他服务的依赖调用。Feign让我们只需要创建一个接口注解就能够实现操作。Feign集成了Ribbon 关于使用就是在接口添加特定注解就可以了。…

html:叫你如何编写第一个网页

<!DOCTYPE html> <!--声明--> <html lang"en"> <head><meta charset"UTF-8"><title>我的第一个网页</title>体部分&#xff1a;存放的是组成html代码部分 </head><BODY><!--html:HyperText Mark…

【Linux网络服务】Apache配置与应用

Apache配置与应用 一、构建虚拟Web主机1.1httpd服务支持的虚拟主机类型包括以下三种 二、基于域名的虚拟主机三、基于IP地址的虚拟主机四、基于端口的虚拟机五、Apache连接保持六、构建Web虚拟目录与用户授权限制七、日志分割 一、构建虚拟Web主机 虚拟Web主机指的是在同一台服…

PointNet++ 源码解读

1.从main函数开始&#xff1a; 1.1 确定使用的哪个GPU. 1.2 保存训练时的参数和日志 2. 加载数据 先找到存放训练和测试数据的目录&#xff0c;接下来加载相关的数据参数&#xff1a; 下面是执行的结果&#xff1a; 接下来为训练样本开始做准备&#xff1a; 给不同标签做上标记…

都2023年了,还有人在盲目自学黑客?

背景 经常逛CSDN和知乎&#xff0c;不理解的是&#xff0c;都2023年了&#xff0c;相关资源都这么多了&#xff0c;还有人不知道怎么学习网络安全。 本人从事网络安全工作5年&#xff0c;在几个大厂都工作过&#xff0c;安全服务、渗透测试工程师、售前、主机防御等职位都做过…

如何实现不同的VLAN之间进行通信?VLAN Mapping大作用就体现出来了!

你好&#xff0c;这里是网络技术联盟站。 今天给大家介绍一下VLAN Mapping&#xff0c;包括VLAN Mapping的概念、原理、应用&#xff0c;同时还会介绍华为设备和思科设备如何配置VLAN Mapping。 让我们直接开始 1. 介绍 VLAN&#xff08;Virtual Local Area Network&#x…

JVM垃圾回收篇之垃圾收集器

五种引用 强引用(不回收) 强引用不会被强制垃圾回收,即使发生OOM也绝对不回收.保护了数据的安全性 软引用(内存不足即回收) 软引用是用来描述一些还有用&#xff0c;但非必需的对象。只被软引用关联着的对象&#xff0c;在系统将要发生内存溢出异常前&#xff0c;会把这些对…

为什么有些情况下需要重写equals()和hashCode()方法?

目录 方法作用实战案例 方法作用 equals()&#xff1a;判断对象是否相等&#xff0c;比如判断是否能放入Set集合中 情况1&#xff1a;没有重写equals()方法&#xff1a;由于所有类的默认基类都是Object类&#xff0c;所以默认使用Object类的equals()方法&#xff0c;那就是对象…

局域网内网穿透技术

文章目录 一、内网穿透概述1、传统内网穿透介绍2、ZeroTier和Tailscale 二、ZeroTier1、概述1.1 介绍1.2 相关概念 2、ZeroTier简单使用3、Moon搭建3.1 介绍3.2 部署Moon服务3.3 使用 Moon 服务 4、流量转发与局域网访问4.1 概述4.2 转发服务器配置4.3 客户端配置 三、Tailscal…

vulnhub靶场渗透之SickOs1.2渗透教程(超级详细)

vulnhub靶场渗透之SickOs1.2渗透教程目录 0x01靶机概述 0x02靶场环境搭建 0x03靶机信息发现 0x04渗透靶机 使用第二种方法&#xff1a;msfvenom生成载荷模块curl上传至服务器触发 0x05本地提权 方法一&#xff1a;将当前用户&#xff08;www-data&#xff09;加入sudo组 提…

SSM编程---Day 05

目录 一、IOC &#xff08;一&#xff09;企业级系统的特点&#xff1a; &#xff08;二&#xff09;Spring框架的优点&#xff1a; &#xff08;三&#xff09;ApplicationContext的作用&#xff1a; &#xff08;四&#xff09;理解IOC原理、掌握IOC的配置 二、AOP&…

三种快速转换PDF为TXT的方法:简单、高效、免费

如何将PDF转换为TXT文件&#xff1f;在日常生活中&#xff0c;PDF和TXT是常见的文本格式。PDF格式文件具有稳定的布局和易于存储的特点。然而&#xff0c;许多在线下载的电子书通常是以PDF格式提供的&#xff0c;而电子阅读器通常不支持PDF格式&#xff0c;这就导致了无法方便地…

【makefile】顶层Makefile向下层Makefile传递参数

一个项目中为了方便管理&#xff0c;每一个模块都会配一个makefile 以便于管理&#xff0c;我们实际在编译的时候&#xff0c;可能只编译其中某一个模块。偶尔下层的Makefile会需要用到上层Makefile文件中的某一个变量或者函数。 下面主要介绍两种方法&#xff0c;分别使用inc…

Innodb底层原理与Mysql日志机制深入剖析

1.MySQL的内部组件结构 客户端执行sql语句&#xff0c;这时mysql会把sql语句发给server层连接器&#xff0c;连接器通过账号密码端口号进行连接数据库&#xff0c;验证成功后&#xff0c;然后在权限表里查询相应的权限&#xff0c;然后依赖于此时读取权限&#xff0c;连接器把s…

【项目】Q-ROBOT移动机器人设计与开发

本文主要记录Q-ROBOT移动机器人的开发流程~ github: GitHub - Qsx567/SLAMCarProject &#xff08;持续更新中~希望各位铁子走过路过给个小小的star&#xff01;灰常感谢&#xff01;&#xff09; 本项目为我设计的移动SLAM机器人&#xff0c;命名为&#xff1a; Q-Robot 机…

【Python Dash】零基础也能轻松掌握的学习路线与参考资料

Python Dash是一个可视化框架&#xff0c;可以帮助开发者快速构建交互式仪表板和应用程序。它基于Plotly.js库建立&#xff0c;提供了一种易于使用的Python界面&#xff0c;用户可以通过简单的Python代码创建仪表板和应用程序。本篇文章将介绍Python Dash的学习路线&#xff0c…

【学习日记2023.5.30】之 订单处理 订单状态定时处理_来单提醒_用户催单

文章目录 10. 订单处理10.1 Spring Task10.1.1 介绍10.1.2 cron表达式10.1.3 入门案例10.1.3.1 Spring Task使用步骤10.1.3.2 代码开发10.1.3.3 功能测试 10.1.4提交代码 10.2 订单状态定时处理10.2.1 需求分析10.2.2 代码开发10.2.3 功能测试 10.3 WebSocket10.3.1 介绍10.3.2…

互联网产品岗

文章目录 产品岗分类工具类社交类内容类平台类 职级划分工作流程refer: 产品岗分类 注&#xff1a;产品岗的分类并不绝对&#xff0c;因为大部分的产品都有符合属性&#xff0c;这里的分类主要便于理解&#xff08;越写越觉得很多东西没得分&#xff09; 工具类 定义&#x…