首页 > 其他分享 >CUDA学习详细教程之指针篇

CUDA学习详细教程之指针篇

时间:2023-06-28 16:55:49浏览次数:68  
标签:p2 教程 p1 int 地址 num CUDA 指针

  近期,我在写深度学习相关的C++部署时候,往往想更好的提速,将更多类似矩阵相关的运算交给CUDA处理,以此提高推理速度。然,cuda编程入门较为复杂,想简单对图像预处理,通过指针指向减少host到device时间,或模型预测后device上处理完后,数据在device到host输出结果等,这些将会涉及很多知识。基于此,我将会出一个系列的CUDA教程,可帮助读者从零基础熟练掌握cuda使用。最重要的是,因我更多是应用型工作,我的教程将直接简单说明CUDA应用,并附有代码,可直接上手实战。我的想法是不要掌握很多cuda繁杂冗余理论,直接上手使用,最终可将深度学习模型相关cpu计算方法搬运到cuda核函数计算。我的教程,主要分为以下几个内容:
1.预备知识,指针
2.visual studio搭建环境
3.cuda相关理论
4.索引与实战
5.内存相关实战
6.cuda部署yolo模型

...

 

 

一:指针篇

①.指针与cuda编程的关系

cuda将使用stream方式控制计算,基本使用指针的方式进行数据拷贝或者开辟内存等,为此学习cuda编码需了解指针相关基础。

具体代码展示示例如下:

cuda个人编写示例:  
 CUDA_CHECK(cudaMallocHost((void**)&img_buffer_host, max_image_size * 3));
  // prepare input data in device memory
 CUDA_CHECK(cudaMalloc((void**)&img_buffer_device, max_image_size * 3));

cuda源码示例:
extern __host__ cudaError_t CUDARTAPI cudaMallocHost(void **ptr, size_t size);
inline __device__  cudaError_t CUDARTAPI cudaMalloc(void **p, size_t s) 
{ 
  return cudaErrorUnknown;
}

以上代码为内存分配示例,可看出使用了指针,因此我们调用cuda相关函数需要大量涉及指针相关方法。

 

②.指针定义:指针是一个值为内存地址的变量或者数据对象。指针变量的值是地址。

如:*p表示定义指针,实际p是指针,指针变量名为p,那么我们可以通过 p = &a,将a的地址赋值给p,因为指针存存的是地址,从而我们可以通过对p进行操作,对a进行修改等操作。

 

 

 ③.指针就离不开*运算符跟&运算符

*运算符,叫间接运算符或叫解引用运算符,找出存储在地址的值。*运算符后面跟着一个指针名或者地址时,*给出存储在指针所指向地址的值。

如:*ptr表示指针ptr对应地址的值,与val=*ptr是等量的,而ptr=&val是等量的,&val表示变量val的地址。

&运算符,叫做地址运算符。后跟变量名时,&给出该变量的地址。

如:变量val,&val表示变量val的地址,若定义*p的指针,可将指针指向变量使用p=&val。

④指针示例

此部分将给出指针几个列子,我将根据这些列子说明指针实际用法与表示含义,以下列子均经过验证,可使用的方法。

在进行指针示例演示代码之前,我将在此贴出公用代码,此代码或许下面某些示例需要使用。

void Print_Pointers(int* ptr,int N) {
    for (int i = 0; i < N; i++)
    {
        std::cout << "order:\t" << i << "\tptr_value:\t" << *ptr << "\tphysical address:" << ptr << std::endl;
        ptr++;
    }
}

a.指针赋值方法探索

指针赋值共探索2种方法,其一在创建指针时候赋值,其二在创建指针完成后赋值,以下代码将指针指向数组,arr数组变量名表示地址,因此可直接赋值;指针p赋值也需要指向变量a的地址,因此需使用&符号获取变量a的地址,然后将地址赋给指针p。

代码如下:

void pointer_1() {
    /*  探索指针赋值方法 */
    const int N = 6;
    int arr[N];
    for (int i = 0; i < N; i++) arr[i] = i + 1; //数组赋值
    //指针第一种赋值方法
    int* ptr=nullptr ; 
    ptr = arr;
    //指针第二种赋值方法
    int *ptr2 = arr;
    
    std::cout << "output ptr1 " << std::endl;
    Print_Pointers(ptr, N);
    std::cout << "\n\noutput ptr2 " << std::endl;
    Print_Pointers(ptr2,N);

    //单独变量赋值
    int a = 20;
    int* p = &a;  
    std::cout << "\n\noutput p value: \t" <<*p<<"\tphysical address:\t"<<p<< std::endl;

}

结果显示:

b.指针基础-数组读取

 指针的初始化、解引用操作以及自增,看一个有趣的语句“int *ptr = arr;”,该语句用数组名初始化指针。在这里数组名代表的是数组第一个元素的地址,之后在循环内程序会递增指针以指向数组的后面几个元素。

代码如下:

void pointer_1() {
    const int N = 6;
    int arr[N];
    for (int i = 0; i < N; i++) arr[i] = i + 1; //数组赋值
    int* ptr = arr; //构建指针
    for (int i = 0; i < 5; i++)
    {
        std::cout << "ptr_value_" <<i<< ":\t" << *ptr << std::endl;;
        ptr++;  
    } 
}

结果显示:

 

 

 

c.指针指向地址的值的探索

此部分探索地址指针指向的值变化:

1.通过指针更改值,将指针赋值为num的地址,通过更改*p的指针来更改指针指向num地址的值*p=*p+20,该物理地址的值更改,而num存的值的物理地址为p,因此也更改了num的值;

2.通过变量更改值,将num=30,更改num的值,而指针p保存指向num的地址,地址不变情况,通过*p查看地址的值,因此为num更改的值30。

代码如下:

void pointer_3() {
    int num = 4;
    int* p = &num;
    cout << "*p:\t" <<*p<<"\t p address:\t"<<p<<"\tnum value:\t"<<num<<"\tnum address:\t"<<num << endl;

    *p = *p + 20; //通过指针更改地址的值
    cout << "*p:\t" << *p << "\t p address:\t" << p << "\tnum value:\t" << num << "\tnum address:\t" << num << endl;
    num = 30; //更改变量值
    cout << "*p:\t" << *p << "\t p address:\t" << p << "\tnum value:\t" << num << "\tnum address:\t" << num << endl;


}

 

结果显示:

 

 

 

 

④指向指针的指针

定义:指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。    指针的指针就是将指针的地址存放在另一个指针里面。

通俗解释:一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的物理位置。

C++ 中指向指针的指针

一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。

a.指针使用初步探索

探索指针的指针 值与地址之间的关系:

1.指针的指针赋值,结果中看指针的指针p2与p3打印值*p2/*p3与地址p2/p3输出一致 ,则赋值方法与指针赋值差不多,但需要&符号赋值。

2.指针的指针p2说明,前提p1指针指向地址000000E9AC0FF914,并保存地址,本身p1指针有自己地址,为此p2指针指向p1指针的地址,*p2表示p1的值也为num的地址。

3.指针p2的**p2与*p2说明,*p2是指针p1的值,而p1的值是num的地址,在前加*表示取p1值(num)的值为num值4。

实际理解与单指针*p1一样,只需记住此时p1的值为num的地址,而本身p1存在自身地址保存值(num地址)。

void pointer_4() {
    int num = 4;
    int* p1 = &num;
    //指针的指针第一种赋值方法
    int** p2=&p1 ;
    //指针的指针第二种赋值方法
    int** p3;
    p3 = &p1;

    cout << "num value:\t" << num << "\t num address:\t" << &num << endl;
    cout << "p1 value:\t" << *p1 << "\t p1 address:\t" << p1 << endl;
    cout << "p2 value:\t" << *p2 << "\t p2 address:\t" << p2 << endl;
    cout << "p3 value:\t" << *p3 << "\t p3 address:\t" << p3 << endl;

    cout << "p2 value:\t" << **p2 << "\t p2 address:\t" << *p2  << endl; 
}

 

结果显示:

 

 b.指针的指针的图文解释

假设变量num的值为4,定义指针*p1与指针的指针**p2,p1指向num,p2指向p1,其示意图如下:

解释如下: 

  1.p2与p1本质是指针,因此需指向地址,从图中可看出p2的值value3指向address2,p1的值value2指向address1。

  2.p2、p1与num本身有自己的地址,分别为address3、address2与address1。

  3.定义的**p2与*p定义完后,p2与p1分别为地址address3与address2,即p2=address3,p1=address2。

  4.使用*获得p2与p1指向指针的地址的值,从图可看出p1的值value指向地址address1,且地址address1的值为value1=4,则  *p1=4;

同理,p2的值value3指向地址address2,且address2的值为value2=address1,则*p2=address1。

  5.如何通过p2获得num的值呢?从4可看出*p2=address1,而address1的值为value1=4,则需再次使用*(*p2)的方式再次获得address1的值即可,

则**p2=value1=4。

标签:p2,教程,p1,int,地址,num,CUDA,指针
From: https://www.cnblogs.com/tangjunjun/p/17511846.html

相关文章

  • 原神脚本,轻松挂机原神,永久软件+详细教程,无需998元的收费端游软件
    原神脚本,轻松挂机原神,永久软件+详细教程高效刷材料,快速完成任务,轻松代肝原神!市场供不应求的软件,秒跳剧情,秒找宝箱,神童等任务点一键传送,日入百元!附详细教程,电脑配置要求较高。注意事项:任何挂机项目都存在小概率封号风险,请保持正常心态对待。选择正规途径获得软件,远离不可靠来源!”......
  • Premiere Pro模板导入教程 如何导入.mogrt文件?
    在使用AdobePremierePro编辑视频时,你可以使用现成的模板来快速制作和修改视频效果。其中一种常用的模板是.mogrt文件,它是PremierePro自带的模板格式。如果你想要使用这些模板,就需要将它们导入到PremierePro中。mogrt文件如何打开?这里小编为大家带来了pr模板导入教程,一起来看......
  • Spring Boot:快速入门教程
      来源  https://www.cnblogs.com/xifengxiaoma/p/11019240.html  SpringBoot:快速入门教程......
  • Gitee通过本地git上传大于10M的文件教程
    Gitee通过网页端默认上传的文件需要小于10M,如上传10M-100M的文件需要通过本地git上传,此时需要使用git指令操作,步骤如下。首先创建一个文件夹,打开后右键使用gitbash功能分别进行简要步骤说明如下1-8,详细可见下图操作说明。1、gitinit初始化;2、gitremote绑定gitee仓库3、git......
  • 《WPF应用开发项目教程》pdf版电子书免费下载
    《WPF应用开发项目教程》以项目化任务驱动式组织教学内容,循序渐进地介绍WPF的开发环境、XAML基础、Application类、窗体、布局控件、常用控件、路由事件、系统命令库、自定义命令、Binding、资源、样式、模板、触发器等,使读者不仅能够学习WPF的各种开发知识,而且能够培养分析问题、......
  • 关于指针
    学习参考文章链接http://t.csdn.cn/HhSR5前面几篇多次看到把地址作为scanf()的输入参数的用法。例如,函数调用scanf("%d",&n),把输入的值存储到变量n所在的内存单元里。其中&n表示变量n的内存地址或存储位置。这里&称为地址运算符,&是一元运算符,与其他一元运算符有同样的优先级。......
  • TreeSaver 使用教程整理——Step 4: Using a Title Figure
    请首先阅读前几篇教程,才能对本篇文章了解比较深入:TreeSaver使用教程整理——Step1:GettingStartedTreeSaver使用教程整理——Step2:AddingBasicUITreeSaver使用教程整理——Step3:CreatingGrids我们在第二步的基础上,copy到step4作为我们step4初始的基础。 Step4......
  • TreeSaver 使用教程整理——Step 3: Creating Grids
    请首先阅读前几篇教程,才能对本篇文章了解比较深入:TreeSaver使用教程整理——Step1:GettingStartedTreeSaver使用教程整理——Step2:AddingBasicUI我们在第二步的基础上,copy到step3作为我们step3初始的基础。 Step3:CreatingGrids模板文件resources.html的变化在......
  • TreeSaver 使用教程整理——Step 2: Adding Basic UI
    请首先阅读前一篇教程:TreeSaver使用教程整理——Step1:GettingStartedStep2:AddingBasicUI我们上一步实现的网页有了一个最最简单的功能,这一步我们将在上一步基础上添加切换分页的按钮以及显示当前页面信息。请Copy上一步的内容,并对下面文件做如下修改: 对资源文件(resource......
  • TreeSaver 使用教程整理——Step 1: Getting Started
    TreeSaver介绍Treesaver是一个开源的JavaScript框架,用来创建杂志风格的网页布局。 为何要整理这个系列的文章下面的教程整理自:https://github.com/Treesaver/treesaver/wiki/Walkthrough,也许是这个框架目前才刚刚起步,它的文档很成问题,文档中一些链接不能下载,源代码中欠缺一......