【问题标题】:Hashed/Sharded ActionBlocks散列/分片动作块
【发布时间】:2014-01-27 09:38:50
【问题描述】:

我需要并行处理某些项目,因此我使用的是TPL Dataflow。问题是共享相同键的项目(类似于字典)应该以先进先出的顺序处理,而不是彼此并行(它们可以与具有不同值的其他项目并行)。

正在完成的工作非常受 CPU 限制,并且异步锁最少,因此我的解决方案是创建一个大小为 Environment.ProcessorCountActionBlock<T>s 数组,没有并行性,并根据密钥的 GetHashCode 值发布给它们。

创作:

_actionBlocks = new ActionBlock<Item>[Environment.ProcessorCount];
for (int i = 0; i < _actionBlocks.Length; i++)
{
    _actionBlocks[i] = new ActionBlock<Item>(_ => ProcessItemAsync(_));
}

用法:

bool ProcessItem(Key key, Item item)
{
    var actionBlock = _actionBlocks[(uint)key.GetHashCode() % _actionBlocks.Length];
    return actionBlock.Post(item);
}

所以,我的问题是,这是解决我问题的最佳方法吗?我会损害性能/可扩展性吗?我错过了什么吗?

【问题讨论】:

  • 我喜欢它。我想不出另一种不需要存储的方法。我认为只要你确保你的哈希码被正确分配,这应该没问题。
  • 依赖GetHashCode的值对我来说听起来很奇怪,你为什么会有它?实际的需求是“相等的物品应该按照先进先出的顺序处理”吗?
  • @svick 更像是 具有相同键的项目应按 FIFO 顺序处理,类似于使用字典的方式(实际上不必是相同的项目类型)。我将更新问题以使其更清楚。
  • @I3arnon 你怎么知道所有线程至少有相当数量的工作要做? (uint)key.GetHashCode() % _actionBlocks.Length 有可能分布不均,某些内核不会做任何事情。
  • @MarcinJuraszek 这是真的。我已经确保哈希值尽可能均匀,并且通过测试我发现确实如此。但是……这是我把它放在这里的原因之一。

标签: c# .net task-parallel-library async-await tpl-dataflow


【解决方案1】:

我认为你的方法是合理的,假设你知道哈希码会很好地分布。

如果您想更好地防止不良分布,您可以使用更多数量的ActionBlocks,同时通过使用所有块共享的单个自定义TaskScheduler 来限制它们的总并发级别。你可以找到这样的调度器in ParallelExtensionsExtrason MSDN

【讨论】:

  • 如何解决糟糕的分布?如果我有一个比其他人使用得更多的“特殊”哈希,那么与使用% _actionBlocks.Length 相比,有多少相互阻塞的 ActionBlock 有什么不同?在您的情况下,“特殊”哈希将使其队列相对于其他队列更大......
  • 是的,它仍然会比其他的大,但它可能会比小块的小,因为与那个特殊哈希的碰撞次数会更少。例如,如果所有哈希值的一半为 0,其余均匀分布,那么有 2 个块,所有项目的 3/4 将进入块 0。但是有 4 个块,它只有 5/8,并且有无穷大块,它将是 1/2。
  • 但是你仍然只有 2 个线程。一个线程将处理 5/8 块和 1/8 块(6/8 = 3/4),另一个线程将处理剩下的 2 1/8 块(2/8 = 1/4)。我错过了什么吗?当您还增加线程数时,我会这样做,但是此代码非常受 CPU 限制,并且我建议每个内核使用 AFAIK 单线程。
  • 不,这不是我的意思,您将有 2 个线程,但它们将在所有 4 个块之间共享。这样,线程上的负载应该大部分均匀分布,即使块上的负载不是。
  • 甚至需要超额订阅才能充分利用 CPU,因为随机分布不是均等分布。一些区块将未被充分利用,一些被过度利用。
猜你喜欢
  • 2017-02-08
  • 2013-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多