3

我有一个关键代码路径,其中线程使用整数上的原子增量来计算全局发生的事件数。这是相当快的,但仍然需要保存整数的缓存行在内核之间反弹。在 NUMA 系统中,这会产生大量的 MESI 流量。

hot pat 的伪代码是所有线程都这样做:

const int CHECK_VALUE = 42;

int counterNew = counter++;
if (counterNew == CHECK_VALUE) {
  Do Final work
}

计数器是单调递增的,它必须达到的值是预先知道的。

至少一个线程必须断定全局计数器CHECK_VALUE在递增后已达到counter。多个线程得出该结论是可以接受的(我总是可以在那时同步它们 - 因为那不再是热门路径)。

counter如果我知道它是单调的并且最终值已知,是否有可能比使用原子增量来跟踪值更好?

4

2 回答 2

0

您可以使用原子 CAS 操作(比较和交换)来做到这一点。在 i386 架构上,这是指令 CMPXCHG。如果需要,您可以使用小型汇编函数,在您的平台上实现 CAS,或者在这里向我询问有关英特尔实现的信息。您的代码必须如下:

int local_cnt;
// Atomic increment counter 
do {
  local_cnt = counter;
} while(cas(&counter, local_cnt, local_cnt + 1) != local_cnt);

// check old counter value
if(local_cnt == CHECK_VALUE) { 
  // do something
}
于 2015-04-13T16:14:24.790 回答
0

如果没有同步,计数器可能会停留在 0。实际上它不会经常出现这种竞争条件,因此计数器将大致准确。我认为您可以证明在计数器序列中不会跳过任何值:如果之前不是 1,则无法将计数器更改为 2,这适用于计数器可能持有的每个值。因此,如果可以错过一些事件,则使用 ++ 而不是原子增量的全局计数器将起作用。然而,即使不同步,这仍然会导致一些您想要避免的内存问题(重新同步 CPU 之间的高速缓存行)。

另一种方法是轮询。每个线程都可以在自己的私有数据中计算它的事件。另一个线程可以每分钟轮询一次以查看事件数是否 > 阈值。

另一种方法是在线程数据中碰撞一个内部计数器,当它达到 10 时,碰撞全局计数器。这会将全局增量的数量减少 10。

另一种方法是在线程中碰撞一个内部计数器。每当单个线程达到 cEvents/threadcount 时进行同步。

另一种方法是在线程中碰撞一个内部计数器。每当单个线程达到某个限制时,检查其他线程计数以查看它们是否一起 > 线程计数。这与使用轮询线程大致相同,但不使用另一个线程。

有很多方法可以用私人柜台做这样的事情。这完全取决于您需要的准确性。

于 2015-07-23T21:02:38.887 回答