【发布时间】:2015-10-07 12:01:53
【问题描述】:
我已阅读此主题:C# Thread safe fast(est) counter 并已在我的并行代码中实现了此功能。据我所见,一切正常,但是它显着增加了处理时间,大约增加了 10%。
这一直困扰着我,我认为问题在于我正在对小数据片段执行大量相对便宜(Interlocked.Increment 中的 x86 LOCK 前缀将缓存线推入独占模式并强制其他内核上的缓存未命中并强制在每个并行通道上重新加载缓存只是为了增加这个计数器。由于缓存未命中的 100ns 延迟和我的工作量,它似乎加起来了。 (话说回来,我可能错了)
现在,我没有找到解决办法,但也许我遗漏了一些明显的东西。我什至在考虑使用 n 个计数器(对应于并行化程度),然后在特定内核上递增每个计数器,但这似乎不可行(检测我在哪个内核上可能会更昂贵,更不用说复杂的 if/then/else结构并弄乱执行管道)。关于如何打破这个野兽的任何想法? :)
【问题讨论】:
-
map-reduce 通常比基于互锁/缓存的方法更容易推理...如果您可以对数据进行分区而不是单独计算每个分区而不是组合结果...
-
您的意思是将批次拆分为 n 个流并在特定核心上以多个增量处理每个流?但是我没有强制亲和力的方法,不是吗?这意味着我的流现在将超过 1 个量子并且将遍布内核,再次迫使缓存未命中在它们自己的计数器上,不是吗?即使它没有互锁,它也会变脏并强制写入读取。每个计数器的未命中次数会减少 n 倍,但计数器会增加 n 倍。
-
Java 的Striped64 使用分区模型。快速路径(无竞争)很便宜,当检测到竞争时动态增长计数器很复杂。我对它进行了调整,以增加一组环形缓冲区,并且在实践中效果很好。
-
你能“批量更新”计数器,即处理几个项目并使用
Interlocked.Add而不是Interlocked.Increment吗? -
@mmix 只要您保证批次由一次只由一个线程处理,您不需要围绕批次特定的计数器进行任何锁定。请注意,要求是“一次只有一个”,甚至不是“一个且唯一”或“一个特定的核心”。事实上,你有更多的计数器,但如果你的批次足够大,那应该没问题。
标签: c# multithreading cpu-cache interlocked mesi