2014-01-09 2 views
6

У меня есть постоянный поток определенных элементов, которые мне нужно обрабатывать параллельно, поэтому я использую TPL Dataflow. Уловка состоит в том, что элементы, которые имеют один и тот же ключ (аналогично словарю), должны обрабатываться в порядке FIFO и не быть параллельными друг другу (они могут быть параллельны другим элементам с разными значениями).Hashed/Sharded ActionBlocks

Работы делается очень ЦП с минимальными асинхронными замками так что мое решением было создать массив ActionBlock<T> сек размера Environment.ProcessorCount, без параллельности и после них в соответствии с 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); 
} 

Итак, мой вопрос, является ли это лучшим решением моей проблемы? Я ушибаю производительность/масштабируемость? Я что-то упускаю?

+1

Мне это нравится. Я не могу придумать другой метод, который не требует хранения. Я думаю, что до тех пор, пока вы убедитесь, что ваши хэш-коды правильно распределены, это должно быть хорошо. – spender

+0

Опираясь на значение 'GetHashCode', звучит очень странно, почему у вас это есть? Является ли фактическое требование «равными статьями» в порядке FIFO? – svick

+0

@svick больше похоже. Элементы с одним и тем же ключом должны обрабатываться в порядке FIFO, аналогично тому, как вы будете использовать словарь (на самом деле не обязательно быть одним и тем же типом элемента). Я уточню вопрос, чтобы сделать это более ясным. – i3arnon

ответ

3

Я думаю, что ваш подход разумный, если вы знаете, что хеш-коды будут хорошо распределены.

Если вы хотите получить лучшую защиту от плохих распределений, вы можете использовать большее количество ActionBlock с, ограничивая их общий уровень параллелизма, используя один настраиваемый TaskScheduler, используемый всеми блоками. Вы можете найти такой планировщик in ParallelExtensionsExtras или on MSDN.

+0

Как это решить плохие распределения? Если у меня есть «специальный» хэш, который используется больше, чем другие, то как иметь много ActionBlocks, которые блокируют друг друга, чем использование '% _actionBlocks.Length'? «Специальный» хэш в вашем случае сделает свою очередь больше по отношению к другим ... – i3arnon

+1

Да, она по-прежнему будет больше, чем остальные, но, скорее всего, она будет меньше, чем с небольшим количеством блоков, потому что будет меньше количество столкновений с этим специальным хэшем. Например, если половина всех хэшей равна 0, а остальная часть распределена равномерно, то с 2-мя блоками 3/4 всех элементов перейдут к блоку 0. Но с 4-мя блоками это всего лишь 5/8 и с блоками бездействия, это будет 1/2. – svick

+0

Но у вас все равно будет только 2 потока. можно было бы обработать блок 5/8 и 1/8 блока (6/8 = 3/4), а другой поток обработал бы 2 1/8 блоков влево (2/8 = 1/4). Я что-то упускаю? Я получаю это, когда вы также увеличиваете количество потоков, но этот код очень привязан к процессору, и рекомендуется использовать один поток AFAIK для каждого ядра. – i3arnon