经历了很多奇奇怪怪的bug,整理一下。先描述要做的事情以及怎么做:

在项目中,空间中有200w+的点,需要映射到一个grid_map的600*600的网格中,落入到同一个格子的点需要进行一些计算获得一个值。对于格子与格子之间是并行的,但格子之中的点需要设计为串行。所以在计算某个格子中的点时,需要将格子的值保护起来,只允许一个线程(点)计算并改变。

这里就用到了cuda的通用原子操作。也许有人会问,cuda提供了一些原子操作函数,能不能直接用呢?cuda提供的原子函数适用于简单的单一变量判断加减,而对于需要复杂的计算操作是力不从心的。但其实,我们要实现的通用原子操作也是基于cuda的原子函数,我们进行一些设计就可以得到想要的通用原子操作,比如锁。

方法1.原子锁

在《GPU高性能编程CUDA实战》一书中,提到了通用原子操作的锁的设计,贴上源码:

struct Lock {int *mutex;Lock(void) {int state = 0;cudaMalloc((void **) &mutex, sizeof(int));cudaMemcpy(mutex, &state, sizeof(int), cudaMemcpyHostToDevice);}~Lock(void) {cudaFree(mutex);}__device__ void lock(void) {while (atomicCAS(mutex, 0, 1) != 0);}__device__ void unlock(void) {atomicExch(mutex, 0);}
};
.......__global__ void theKernel(Lock myLock) {myLock.lock();Do_your_job();myLock.unlock();
}

这里通过atomicCASatomicExch两个函数进行设计,但一个线程lock之后,将mutex置为1,其他线程将在while处循环等待,直到该线程unlock,将mutex重新置于0,剩下的线程中再次争夺锁。

但是这个结构是存在问题的,我在测试时候发现调用theKernel<<<128, 1>>>(lock)可以正常运行,而theKernel<<<1, 128>>>(lock)出现了死锁,也就是在block中线程数大于1情况中,出现死锁。百思不得其解…后来查到了出现这种情况的原因:

cuda运行是以wrap为单位进行的,也就是说一个wrap中32个线程中的一个获得了锁,执行完了lock,按理说该线程要继续执行Do_your_job()unlock,而现实是线程都卡在了lock处。这就是因为wrap的同步执行规则(locked-step execution),换句话说,一个wrap的线程是同步执行一个函数,并同步退出一个函数。获得锁的线程在lock函数结束处苦苦等待其他31个线程兄弟一起进入Do_your_job(),而剩下的31个线程却等着它unlock释放锁,所以出现了死锁。而每个block中只有一个线程则不会出现死锁,是因为此时wrap中仅有一个线程。

显然,这个设计方法并不满足我的需求。

方法2.通用原子操作

考虑到同一个wrap的线程都是‘同进退共生死’,那么我们只能在那个获得锁的线程退出函数前,就释放了锁。看代码:

__global__ void kernel1(){int index = 0;int mSize = 1;bool blocked = true;while(blocked) {if(0 == atomicCAS(&mLock, 0, 1)) {index = mSize++;doCriticJob();atomicExch(&mLock, 0);blocked = false;}}
}
int main(){kernel1<<<4,128>>>();cudaDeviceSynchronize();
}

在程序中,获得锁的线程进入到if中,并在执行完if之前就释放了锁,这样就解决了同一个wrap出现死锁的情况。当然,这样的写法不怎么优美且不鲁棒…(但是能用)。另外,这个函数换成这样写法就不行了:

__global__ void kernel2(){int index = 0;int mSize = 1;while(true) {if(0 == atomicCAS(&mLock, 0, 1)) {index = mSize++;doCriticJob();atomicExch(&mLock, 0);break}}
}
int main(){kernel2<<<4,128>>>();cudaDeviceSynchronize();
}

这是因为break在不同的机器和编译器中,不能都保证是先释放了锁再break出来,可能被编译器优化成其他形式。可以看出这种cuda通用原子操作确实比较蛋疼。

不过我在项目中采取了这种方法,将mLock由int变为int数组,就可以实现多把锁并行,提高效率,贴上我运行ok的代码:

__device__ void doCriticJob(int thread_index, float* mProcess) {mProcess[thread_index] += 0.222;printf("thread is: %d \n", threadIdx.x);
}
__global__ void kernel2(int* mFlag, float* mProcess) {bool blocked = true;int thread_index = (threadIdx.x + blockDim.x*blockIdx.x) % 4;while (blocked) {if (0 == atomicCAS(&mFlag[thread_index], 0, 1)) {doCriticJob(thread_index, mProcess);atomicExch(&mFlag[thread_index], 0);blocked = false;}}
}int main() {cudaError_t cudaStatus;float h_Process[4] = {0};int h_Flag[4] = {0};float *dev_Process;int *dev_Flag;cudaStatus = cudaMalloc((void **)&dev_Process, 4*sizeof(float));if(cudaStatus != cudaSuccess){ fprintf(stderr,"malloc 1 failed\n");}cudaStatus = cudaMalloc((void **)&dev_Flag, 4*sizeof(int));if(cudaStatus != cudaSuccess){ fprintf(stderr,"malloc 2 failed\n");}cudaStatus = cudaMemcpy(dev_Process, h_Process, 4*sizeof(float), cudaMemcpyHostToDevice);if(cudaStatus != cudaSuccess){ fprintf(stderr,"malloc 3 failed\n");}cudaStatus = cudaMemcpy(dev_Flag, h_Flag, 4*sizeof(int), cudaMemcpyHostToDevice);if(cudaStatus != cudaSuccess){ fprintf(stderr,"malloc 4 failed\n");}kernel2<<<2, 3>>>(dev_Flag,dev_Process);cudaStatus = cudaDeviceSynchronize();if (cudaStatus != cudaSuccess) {fprintf(stderr, "ffffff is %d\n",cudaStatus);}float outProcess[4];cudaStatus = cudaMemcpy(outProcess, dev_Process, 4*sizeof(float), cudaMemcpyDeviceToHost);if (cudaStatus != cudaSuccess) {fprintf(stderr, "kkkkkk is %d\n",cudaStatus);}for (float mProces : outProcess) {std::cout << mProces << std::endl;}
}

可以实现对多个锁的控制,锁之间是并行的。

方法3.仲裁中介

这个方法的名字是我瞎取的。直接上代码:

__device__ volatile int sem = 0;__device__ void acquire_semaphore(volatile int *lock){while (atomicCAS((int *)lock, 0, 1) != 0);}__device__ void release_semaphore(volatile int *lock){*lock = 0;__threadfence();}..........__global__ void inKernel(){...__syncthreads();if (threadIdx.x == 0)acquire_semaphore(&sem);__syncthreads();//begin critical section// ... your critical section code goes here//end critical section__threadfence(); // not strictly necessary for the lock, //but to make any global updates in the critical //section visible to other threads in the grid__syncthreads();if (threadIdx.x == 0)release_semaphore(&sem);__syncthreads();...
}

为什么叫仲裁中介呢?因为这里采用了每个block的一个线程作为中介进行仲裁,在acquire_semaphore中争夺锁,一旦某个block的第一个线程获得了锁,那么剩下的block第一个线程将陷入while循环中,同时因为__syncthreads(),导致整个block停下。这就实现了以block为单位的串行。但是这其实也不完美,若要再进一步在block的线程中实现串行,则要继续加入条件判断。可以看StackOverflow:链接

总结:

cuda 中不可避免的遇到需要串行计算的情况,可以每个方案都不是完美的,需要根据情况进行取舍,我也继续学习,应该是存在更好的方案,日后遇到了再添加进来。

cuda 原子锁多线程操作通用原子操作相关推荐

  1. MFC不能多线程操作控件的原因

    对于大多数mfc对象,请不要在线程间传递它们,不管是栈上的还是堆上的!原因如下:   mfc的大多数类不是线程安全的,调用传入对象的成员函数可能不会报错,但是未必能达到程序预定的功能!   mfc与界 ...

  2. 多线程操作数据库时为了防止数据的增删改的混乱该在数据库层还是程序层面上进行同步?

    多线程操作数据库时为了防止数据的增删改的混乱该在数据库层还是程序层面上进行同步? [问题点数:60分,结帖人jiao_zg] 不显示删除回复 显示所有回复 显示星级回复 显示得分回复 只显示楼主 收藏 ...

  3. FMDatabaseQueue 数据库多线程操作、事务处理

    SQLite数据库多线程操作: 在上面一节中已经讲过FMDB的用法了,接下来讲讲sqlite在都线程中的用法.如果应用中使用了多线程操作数据库,那么就需要使用FMDatabaseQueue来保证线程安 ...

  4. 一行 Python 实现并行化 -- 日常多线程操作的新思路 - 左手键盘,右手书 - SegmentFault...

    一行 Python 实现并行化 -- 日常多线程操作的新思路 - 左手键盘,右手书 - SegmentFault

  5. Shell多线程操作及线程数控制实例

    来源:http://www.jb51.net/article/51720.htm 这篇文章主要介绍了Shell多线程操作及线程数控制实例,文中从单线程实现一个需求开始,不断加入代码实现多线程以及线程数 ...

  6. python多线程读取数据库数据_python多线程操作MySQL数据库pymysql

    python多线程操作MySQL数据库pymysql 项目中使用多线程操作数据库提示错误:pymysql.err.InterfaceError: (0, "),原因是pymysql的exec ...

  7. python多线程读取数据库数据_Python基于多线程操作数据库相关知识点详解

    Python基于多线程操作数据库相关问题分析 本文实例分析了Python多线程操作数据库相关问题.分享给大家供大家参考,具体如下: python多线程并发操作数据库,会存在链接数据库超时.数据库连接丢 ...

  8. Core Data 多线程操作实战篇

    最近在解决百度音乐iPhone客户端偶现数据库操作crash的问题,顺手整理了下CoreData的多线程原则,以及实际开发时应该如何遵守这些原则. Core Data多线程操作的基本原则 不允许跨线程 ...

  9. c#多线程操作界面控件的简单实现

    一个小功能,早有人实现了.自己在一个项目中用到,觉得有必要记录一下,写下来. 代码 从上面你可能已经看出如何多线程操作同一个控件的,就是通过一个委托,然后定义委托方法,判断控件的InvokeRequi ...

  10. C#多线程操作界面控件的解决方案

    C#中利用委托实现多线程跨线程操作 - 张小鱼 2010-10-22 08:38 在使用VS2005的时候,如果你从非创建这个控件的线程中访问这个控件或者操作这个控件的话就会抛出这个异常.这是微软为了 ...

最新文章

  1. Java多线程面试题通关手册!
  2. pytorch tensorboard
  3. java emailbuilder 样式_Java8通用Builder了解一下
  4. python全栈开发总结_python全栈开发 * 12 知识点汇总 * 180530
  5. 用 Redis 实现分布式锁(Java 版)
  6. java 中映射关系_java – 在Hibernate中映射一对多的关系?
  7. 源码安装的php如何启动脚本,PHP源码编译安装管理常用脚本
  8. 信息抽取 | 72篇论文梳理:涉及NER、复杂关系、小样本、文档级、多模态、开放域抽取...
  9. angular的组件通信
  10. Atitit 健康减肥与软件健康减肥的总结 attilax著 1. 几大最佳实践减肥行为 1 1.1. 控制饮食分量用小碗 小盘子 小餐具 1 1.2. 软件如何减肥,控制资源占有率,比如体积 打包
  11. 自动驾驶1-5: 感知要求Requirements for Perception
  12. Revit二次开发——依据两条平曲线创建一条三维曲线
  13. 小米手环4怎么使用_小米运动手环4使用说明
  14. 图像工作回顾之三:极线匹配
  15. Tixati——BT下载软件
  16. 【BUG】Ubuntu 3090显卡驱动掉了,重新安装
  17. C++11生成随机数(random库)
  18. 齐岳定制|Cyclopropene-PEG-Xylan|环丙烯-聚乙二醇-木聚糖
  19. VOT Toolkit工具使用说明(Python版)
  20. C++的静态成员变量和静态成员函数

热门文章

  1. 智齿科技携手无忧我房 VR+AI新品亮相GTC
  2. python高德地图poi点_python3爬虫-高德地图POI数据的爬取
  3. 手机上微信总是说无法连接服务器错误代码,微信登录失败怎么办|微信登录失败常见提示及处理方法...
  4. Linux 系 统 目 录 详 解
  5. 【MATLAB】clear和clc用法
  6. python——字符串练习:句子反转(小米笔试题)
  7. 双线性插值实现图像放大算法 matlab,FPGA/verilog实现双线性插值图像放大
  8. 深度学习大神都推荐入门必须读完这9篇论文
  9. 2022智源大会议程公开 | 预训练大模型论坛
  10. 视频教程-java淘宝优惠券系统二-Java