cudaMalloc(

cudaMalloc(,第1张

一、前言

上一篇文章,我觉得还是有点不是很理解,因此这里继续学习记录一下:

我们参考这篇博客:【CUDA】分配内存使用void**_TwT520Ly的博客-CSDN博客

在CUDA分配内存的时候,都是使用void**进行内存的分配。

我这里自己写了一段代码进行malloc测试:


#include 
#include


void memory(int* p) {

    std::cout << "p1指向的地址为:  " << p << std::endl;
    std::cout << "p1自身的存储地址为: " << &p << std::endl;

    p = (int*)malloc(10);
    std::cout << "p2指向的地址为: "<< p << std::endl;
}

int main() {

    int i = 10;

    int* a = &i;

    std::cout << "a1指向的地址为: " << a << std::endl;
    std::cout << "a1自身的存储地址为: " << &a << std::endl;

    memory(a);

    std::cout << "a2指向的地址为: " << a << std::endl;
    return 0;
}

运行结果如下:

从上面的运行结果,再结合博客差不多就可以理解了:

首先我们可以看到指针变量a在进入memory函数之前的值(即指向的地址)是0000001E9659FC94,

接下来进入memory函数中,将指针变量传递给memory函数的形参p,这时候实际是产生了一个a的副本,因此虽然指针变量p指向的地址与a指向的地址是一样的,但是就指针a和指针p本身自己也是一个变量,既然是非临时变量其本身就有的所在的存储地址,因此a和p二者的自身存储地址肯定是完全不同的!(任何变量都有一个存储的地方,要么寄存器,要么内存),因此我们就看到了运行结果如下:

指针变量a在进入memory函数之前,a指向的地址为0000001E9659FC94,而a自身的存储地址为0000001E9659FCB8。

进入memory函数之后可以看到p指向的地址跟a是一样的,但是p其自身的存储地址为 0000001E9659FC70,

明显符合:指针a和p指向同一地址,但是由于指针传参后发生的是拷贝(注意这个申请新的内存空间(也有可能是寄存器?)用于存储指针变量a的副本p的深拷贝),因此这个拷贝出来的p其本身所在地址与a就不一样了!

然后在进行p = (int*)malloc(10);这句话明显malloc函数申请新的内存空间(一块新申请出的空间都会有其对应的地址),返回指向这个新的内存空间的地址。那么这个时候经过赋值 *** 作自然指针p所指向的地址就发生了变化!此时p指向的地址从0000001E9659FC94变为000001E7E0C945A0

最后我们再看看memory函数之后的指针a所指向的地址有无变化呢?发现其指向内存空间的地址为:

a2指向的地址为: 0000001E9659FC94

二、总结

总结上面:

1)指针a在memory函数前后其指向的地址并没有发生变化

这个会让人感到很疑惑,这个跟我们之前想的不一样,我们之前认为memory函数内部形参p的指向地址在malloc之后就发生了变化,那么按道理主函数中memory函数后面打印指针a,应该此时指针a指向的地址与变化后的指针指向的地址一样的啊,但是发现并不是!!!!

2)memory函数形参拷贝

实参传递给形参(指针方式传递)实际是一种深拷贝,即形参变量是在重新开辟的内存中存储的。

具备的特点是:指针传递时,形参与实参均指向相同的地址,但是 其本身变量所处的内存位置不同(因此自身地址不同)。

三、cudamalloc

经过上面的分析,我们可以明白正是由于一维指针(假设为int* ptr)作为形参传入函数中,其经过cudamalloc函数则指针ptr的指向地址并不会发生变化(正如上面a1和a2的指向地址均为0000001E9659FC94)。

而cudamalloc函数执行时正如上面的memory函数执行过程一样,原本p只是实参a的一个副本,由于是深拷贝机制,p和a是:指向相同的地址(假设为0x0000f),而自身变量的地址却不相同!gpu调用malloc函数分配的内存是给了p(此时p的指向也发生了变化,不再指向前面的地址0x0000f,而是一个新的地址假如为0x01222b)。

而使用void**方法就是传入的二维指针的实参(假设为int** ddptr),一层解引用*ddptr返回的是二级指针所指向的对象内容,这个对象内容是一个地址(二维指针就是这样,解一次引用获取的地址,就是一维指针所指向的地址)。

*ddptr = malloc(10);之后就是gpu分配的新的内存空间,这个新的内存空间所在的地址赋值给了一级指针*ddptr(如0x12312f),那么此时原来的二级指针**ddptr指向的对象内容就是这个地址0x12312f。由于二级指针ddptr的指向的地址是不变的,只是指向的地址所对应的内存空间中的内容发生了变化。

int* dptr = nullptr;
int** ddptr = &dptr;
 
*ddptr = malloc(10);

下面我给出测试代码,就可以很好地看二级指针的情况:


#include 
#include

/***
void memory(int* p) {

    std::cout << "p1指向的地址为:  " << p << std::endl;
    std::cout << "p1自身的存储地址为: " << &p << std::endl;

    p = (int*)malloc(10);
    std::cout << "p2指向的地址为: "<< p << std::endl;
}




int main() {

    int i = 10;

    int* a = &i;

    std::cout << "a1指向的地址为: " << a << std::endl;
    std::cout << "a1自身的存储地址为: " << &a << std::endl;

    memory(a);

    std::cout << "a2指向的地址为: " << a << std::endl;
    return 0;
}

***/


void memory(int** p) {

    std::cout << std::endl;
    std::cout << "形参二级指针p指向的地址为:  "  <

运行结果如下:

我们分析上面的代码及运行结果:

1)首先我们可以看到

 memory函数的形参是实参的一个拷贝副本,包括a和p的指向的地址,以及所指向地址的对象内容都是完全一样的,但是a和p本身的存储地址是不同的,这就是深拷贝的机制!

2)

我们看到形参p在malloc函数前后,所发生的变化只在于:二级指针形参p所指向的地址的内容(即一个一级指针值)发生了变化,只是由于malloc函数申请到了一块新的内存区域,并把该新区域的地址赋值给了一级指针值*p,因此才有了这种变化。

注:二级指针所指向的地址就是用于存储一级指针的内存空间的地方,所以我们一般二级指针(如int** m)解引用一次*m,获取的就是二级指针m所指向的对象(一个一级的指针值)。

3)

我们可以看懂memory函数执行后,实参二级指针a所指向地址以及该地址对应的对象内容 都是跟形参二级指针p是一样的,唯一的不同还是由于前面提到的形参实参传递的是”深拷贝“,重新申请了一块内存给了形参造成的!

总结:

这样也就理解了cudaMalloc((void** devPtr,size_t size),内存申请用void**的原因。实际还是为了获取 GPU分配内存的首地址(也就是二级指针解一次引用的结果)。

若使用void*的话,cudaMalloc((void* a,size_t size),这样的一级指针int* a,执行p= malloc(10);虽然形参p的指针值发生了变化,即所指向的地址变了;但是实参a的指针值并未变化仍然指向之前的地址!因此这样实参无法取得gpu分配内存的首地址,自然不能使用void*,而要使用void**!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/921246.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)