为什么用OpenMP并行计算会出现比单线程还要慢
1个回答
展开全部
openmp默认使用的schedule是取决于编译器实现的。gcc默认使用schedule(dynamic,1),也就是动态调度并且块大小是1。在你的程序里面,这种调度是及其低效的,看代码都能预期到,不太可能比单线程快。
动态调度的一种简单理解方式是,计算任务存在一个任务队列里面,你的for循环每一个i值对应一个计算任务。每个线程每次提取一批任务,然后计算。“一批”是多少呢?就是前面说的块大小,在你的程序里面是1。提取任务需要什么操作呢?因为这个任务队列是多线程共享的,提取任务前必须加锁,读取一批,从队列中移除,然后解锁。说到这里,你应该已经知道原因了。
你的线程一次只提取一次计算任务,这个任务还完成得很快。然后所有的16个线程排着队,逐个去加锁,抢任务,然后解锁让其它线程继续抢。然后马上发现这个任务很快,又要重新去排队等任务,始终处于饥饿状态。注意排队的时候可能也是要占cpu的,因为使用了busy wait,所以可能你看来十六核满负荷,但是其实啥也没干。
我的建议就是,使用static schedule,或者增加dynamic schedule的块大小,比如1024,取决于你循环多少次。一般如果你知道每次循环的执行时间基本都是一样,并且是专用服务器设置好affinity,无其它负荷无oversubscription无numa问题的话,static schedule会是个比较好的选择。这样每个线程做哪些任务只需要进行一次分配,最小化了openmp本身的消耗。
动态调度的一种简单理解方式是,计算任务存在一个任务队列里面,你的for循环每一个i值对应一个计算任务。每个线程每次提取一批任务,然后计算。“一批”是多少呢?就是前面说的块大小,在你的程序里面是1。提取任务需要什么操作呢?因为这个任务队列是多线程共享的,提取任务前必须加锁,读取一批,从队列中移除,然后解锁。说到这里,你应该已经知道原因了。
你的线程一次只提取一次计算任务,这个任务还完成得很快。然后所有的16个线程排着队,逐个去加锁,抢任务,然后解锁让其它线程继续抢。然后马上发现这个任务很快,又要重新去排队等任务,始终处于饥饿状态。注意排队的时候可能也是要占cpu的,因为使用了busy wait,所以可能你看来十六核满负荷,但是其实啥也没干。
我的建议就是,使用static schedule,或者增加dynamic schedule的块大小,比如1024,取决于你循环多少次。一般如果你知道每次循环的执行时间基本都是一样,并且是专用服务器设置好affinity,无其它负荷无oversubscription无numa问题的话,static schedule会是个比较好的选择。这样每个线程做哪些任务只需要进行一次分配,最小化了openmp本身的消耗。
本回答被网友采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询