具有边界的原子,可扩展,单调计数器

时间:2015-04-13 15:51:10

标签: algorithm atomic lock-free vector-clock

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

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

const int CHECK_VALUE = 42;

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

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

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

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

2 个答案:

答案 0 :(得分:0)

您可以使用原子CAS操作(比较ans swap)。在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
}

答案 1 :(得分:0)

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

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

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

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

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

有很多方法可以用私人计数器来做这样的事情。这一切都取决于你需要的准确性。