如何从主机端更新设备端类中的width_与height_变量?
如何从主机端更新设备端MyArray类的width_和height_变量
核心结论
不能直接赋值,必须通过cudaMemcpy或者CUDA核函数来修改。因为MakeInstance返回的d_array是设备内存指针,指向GPU显存中的对象实例,主机端代码无法直接访问GPU地址空间的内存,直接解引用赋值会触发非法内存访问错误。
原因分析
你的MakeInstance函数中,d_array是通过cudaMalloc在GPU显存中分配的对象内存,主机端拿到的只是一个设备地址的句柄。主机端的CPU无法直接读写GPU显存,所以像d_array->width_ = new_width这种直接赋值操作是完全无效的,甚至会导致程序崩溃。
两种可行实现方案
方案1:用cudaMemcpy单独更新成员变量
写主机端的setter函数,通过cudaMemcpy将新值从主机端拷贝到设备对象的成员地址中。注意:修改width/height后必须重新分配data_的显存,否则原有的显存分配尺寸和新的宽高不匹配,后续访问数据会出错。
示例代码:
__host__ void SetWidth(MyArray* d_array, size_t new_width) { // 先从设备端获取当前height,用于重新分配显存 size_t current_height; cudaCheckError(cudaMemcpy(¤t_height, &d_array->height_, sizeof(size_t), cudaMemcpyDeviceToHost)); // 释放旧的data_显存 cudaCheckError(cudaFree(d_array->data_)); // 分配新的显存(按新width和原height) size_t new_pitch; int* new_data; cudaCheckError(cudaMallocPitch(&new_data, &new_pitch, new_width * sizeof(int), current_height)); // 将新的width、data_、pitch拷贝到设备对象中 cudaCheckError(cudaMemcpy(&d_array->width_, &new_width, sizeof(size_t), cudaMemcpyHostToDevice)); cudaCheckError(cudaMemcpy(&d_array->data_, &new_data, sizeof(int*), cudaMemcpyHostToDevice)); cudaCheckError(cudaMemcpy(&d_array->pitch_, &new_pitch, sizeof(size_t), cudaMemcpyHostToDevice)); } __host__ void SetHeight(MyArray* d_array, size_t new_height) { // 类似SetWidth的逻辑,获取当前width,重新分配显存 size_t current_width; cudaCheckError(cudaMemcpy(¤t_width, &d_array->width_, sizeof(size_t), cudaMemcpyDeviceToHost)); cudaCheckError(cudaFree(d_array->data_)); size_t new_pitch; int* new_data; cudaCheckError(cudaMallocPitch(&new_data, &new_pitch, current_width * sizeof(int), new_height)); cudaCheckError(cudaMemcpy(&d_array->height_, &new_height, sizeof(size_t), cudaMemcpyHostToDevice)); cudaCheckError(cudaMemcpy(&d_array->data_, &new_data, sizeof(int*), cudaMemcpyHostToDevice)); cudaCheckError(cudaMemcpy(&d_array->pitch_, &new_pitch, sizeof(size_t), cudaMemcpyHostToDevice)); }
方案2:用CUDA核函数修改设备对象成员
如果需要同时修改多个成员,或者有更复杂的设备端逻辑,可以写核函数来直接操作设备对象。但要注意:核函数内不能调用cudaMallocPitch这类主机端API,所以显存重新分配还是要在主机端完成。
示例代码:
// 核函数:修改设备对象的width和height __global__ void UpdateArrayDimensions(MyArray* d_array, size_t new_width, size_t new_height) { d_array->width_ = new_width; d_array->height_ = new_height; } // 主机端setter函数 __host__ void SetDimensions(MyArray* d_array, size_t new_width, size_t new_height) { // 先检查尺寸是否变化,避免不必要的显存操作 size_t old_width, old_height; cudaCheckError(cudaMemcpy(&old_width, &d_array->width_, sizeof(size_t), cudaMemcpyDeviceToHost)); cudaCheckError(cudaMemcpy(&old_height, &d_array->height_, sizeof(size_t), cudaMemcpyDeviceToHost)); if (new_width != old_width || new_height != old_height) { // 释放旧显存,分配新显存 cudaCheckError(cudaFree(d_array->data_)); size_t new_pitch; int* new_data; cudaCheckError(cudaMallocPitch(&new_data, &new_pitch, new_width * sizeof(int), new_height)); // 更新设备对象的data_和pitch cudaCheckError(cudaMemcpy(&d_array->data_, &new_data, sizeof(int*), cudaMemcpyHostToDevice)); cudaCheckError(cudaMemcpy(&d_array->pitch_, &new_pitch, sizeof(size_t), cudaMemcpyHostToDevice)); } // 调用核函数更新width和height UpdateArrayDimensions<<<1, 1>>>(d_array, new_width, new_height); cudaCheckError(cudaGetLastError()); // 检查核函数启动错误 cudaCheckError(cudaDeviceSynchronize()); // 等待核函数执行完成 }
关键注意事项
- 无论用哪种方式,修改
width_或height_后,必须同步更新data_和pitch_,否则原显存分配的尺寸和新的宽高不匹配,会导致数据访问越界或错误。 - 所有CUDA操作后都要调用
cudaCheckError检查错误,方便调试问题。
内容的提问来源于stack exchange,提问作者Cecilia
相关产品推荐
相关产品推荐

