我正在修改我很久以前编写的一些代码,并决定重写它以更好地利用线程(并且更好地使用编程......)。
它位于:https://github.com/buddhabrot/buddhabrot/blob/master/basic.c:
这是一个渲染buddhabrot分形的应用程序。由于超出了这个问题范围的原因,很难使用memoization来优化这一点,基本上如果你对此进行分析,超过99%的时间花费在最终的最内层循环中:
buddhabrot[col][row]++;
多个线程将执行此代码。由于递增不是线程安全的,因此我在内存的这一部分周围使用了特定的互斥锁。因此,buddhabrot内存中的每个可寻址位置都有一个单独的互斥锁。
现在,这比使用一个锁更有效(这肯定会使所有线程彼此等待),但它的内存效率较低;似乎互斥体也会获取一些数据。我也想知道pthreads实现中有数百万个互斥体的其他影响吗?
我现在还有两个需要考虑的策略:
为每个"区域使用一组密度较小的互斥锁。在地图上。因此,例如,[col / 16] [row / 16]的锁只会锁定一个线程,如果它访问与另一个16像素相同的区域。锁的密度可以动态调整。 但是当我对这个进行建模时,我想知道我是否没有解决可能由内核实现的现有问题,而且我也无法在不减慢速度的情况下找到一种方法来实现这一点。我还考虑了#t;互斥树",但这一切在这个循环中太慢了(为了给出一个指示,在优化了编译器后面的一些数学运算的顺序之后我可以挤出大约30%的处理器时间)。有没有这方面的主题,我如何寻找关于"互斥密度规划的信息?#?;
复制每个线程的内存,所以我甚至不必在它周围进行互斥。但这更无记忆效率。它可以解决在不知道其影响的情况下拥有数百万个互斥锁的问题。
那么,还有什么我能做的更好的事情吗?
答案 0 :(得分:4)
您可以在Windows平台上使用intrin.h中的InterlockedIncrement等原子增量函数。
#include <intrin.h>
#pragma intrinsic(_InterlockedExchangeAdd, _InterlockedIncrement, _InterlockedDecrement, _InterlockedCompareExchange, _InterlockedExchange)
#define InterlockedExchangeAdd _InterlockedExchangeAdd
#define InterlockedIncrement _InterlockedIncrement
#define InterlockedDecrement _InterlockedDecrement
#define InterlockedCompareExchange _InterlockedCompareExchange
#define InterlockedExchange _InterlockedExchange
#pragma intrinsic(abs, fabs, labs, memcmp, memcpy, memset, strcat, strcmp, strcpy, strlen)
#pragma intrinsic(acos, cosh, pow, tanh, asin, fmod, sinh)
#pragma intrinsic(atan, exp, log10, sqrt, atan2, log, sin, tan, cos)
这种增量是原子的,不需要在矩阵上有数百万个互斥锁或全局锁。
答案 1 :(得分:0)
我认为你应该能够对矩阵进行分区,这样每个线程只会更新1列。这样他们就不会互相帮助,你也不必锁定。
创建所有列的中央同步队列,让每个线程去那里获取一个列号,然后它只更新该列中的值,然后转到下一列的队列,直到完成所有列。
然后争论只会在中央队列上,与其他人相比应该是微不足道的。
另外我猜每列中都会有足够的行,所以你不会得到错误的共享,这会让你失望。
关心GJ
答案 2 :(得分:0)
出于你给出的理由,你的第二个设计是更好的选择。为了渲染佛像,你需要建立一个大的总和矩阵。如果让每个处理器计算自己的阵列,然后每分钟左右将其结果添加到主阵列,则可以避免内存争用。这是唯一需要内存锁定的部分,甚至可以通过让每个线程写入自己的文件来避免这种情况。你有多个处理器,对吧?如果没有,那么添加线程将不会带来任何好处。