我目前正在实施一个多线程体素游戏引擎。当我使用多线程时,我很快遇到了使用互斥锁的性能瓶颈。
为了澄清我的问题,我们来看一个2D案例:
+-+-+-+
|A|B|C|
+-+-+-+
|D|E|F|
+-+-+-+
|G|H|I|
+-+-+-+
所有这些细胞都是体素块(16x16体素)。
我使用多个线程以块为单位执行所有体素算法。我有一个由要处理的块组成的作业队列,每个工作线程只是不断从队列中挑选块并对其进行处理。
现在想象一个线程需要在块E中进行一些光照计算。因为在E的角落可能会有一个光源传播到相邻的块,它必须锁定所有九个相邻的块以避免潜在的数据竞争,使用互斥锁。
然而,正如我的实验,互斥体的性能开销并不好。目前我正在使用简单的for
循环来添加作业。因此,当游戏运行时,初始作业队列将变为:
A, B, C, D, E, F, G, H, I, ...
这非常糟糕,因为第一个作业A将锁定A,B,D,E并使所有后续八个作业等待互斥锁,从而导致性能下降。
目前我能想到的唯一缓解措施是尝试以分散的方式添加工作,希望我们可以避免大多数摊位。但我不喜欢这种方法,因为它看起来更像是一种解决方法,如果锁定模式发生变化,则不是很灵活。
我还想过使用“异步互斥体”。但我不太清楚该怎么做。
编辑: 只是为了澄清,照明作业是在运行时添加的,而不是按固定顺序添加的。例如,假设玩家移出当前处理的块,然后只应将外部块添加到队列中,队列可能处于不规则的边界。
所以我认为单独使用一个不错的调度程序不足以解决这个问题。
答案 0 :(得分:1)
这太长了,不适合评论部分。
如何使用原子bool查看当前是否正在处理块?这样,您将获得更高的线程利用率,而不是等待进行处理的线程。此外,如果您在相同分散的点开始每个线程,您将获得更少的冲突。然后,当线程需要在相同的块上工作时,此算法才解决问题。
答案 1 :(得分:0)
如何调度线程之间的一些偏移,如下所示:
您有4或8个neigbours,例如4个工作线程
为清楚起见,单元格0102表示行= 01列-02(均从0开始计算)
thread 0000 00001 0002 0003
-------------------------
cell cell cell cell
-------------------------
0000 0003 0006 0009
0001 0004 0007 0010
0002 0005 0008 0011
0012 0015 0018 0021
0013 0016 0019 0022
0014 0017 0020 0023
0024 0027 0030 0033
0025 0028 0030 0034
0026 0029 0030 0035
... till end of row
0100 0103 0106 0109
0101 0104 0107 0110
0102 0105 0108 0111
0112 0115 0118 0121
0113 0116 0119 0122
0114 0117 0120 0123
0124 0127 0130 0133
0125 0128 0130 0134
0126 0129 0130 0135
... till end of row
... till end map
线程之间的差距越大越好
此方法可以创建数据传播工件!!!