2016-10-08 2 views
1

У меня есть 10 потоков и Vec длины 100.Могут ли разные потоки писать в разные разделы одного и того же Vec?

Могу ли я иметь нить 0 работу над элементами 0-9 (сортировать их, к примеру), в то время как поток 1 работает на элементах 10-19 и т.д.?

Или мне нужно использовать Vec<Vec<>> для этого? (Который я бы предпочел избежать, потому что элементы больше не будут смежными в памяти)

+0

Поиск в Интернете для «ржавчины vec thread» приводит к http://stackoverflow.com/q/28599334/155423; http://stackoverflow.com/q/31644152/155423; http://stackoverflow.com/q/33818141/155423; и многие другие. Обязательно сделайте свое [усилие и проявите это усилие при задании вопроса] (http://meta.stackoverflow.com/q/261592/155423). – Shepmaster

+0

@Shepmaster спасибо – MaxB

ответ

9

Да, вы можете. Вы спросили об изменчивом случае, но я предоставлю, сказав, что если только Vec (например, для уменьшения), вы можете безопасно отправлять неизменяемую ссылку на определенный фрагмент, который вы хотите в каждом потоке. Вы можете сделать это, просто используя что-то вроде &my_vec[idx1..idx2] в цикле.

Для изменчивого футляра это немного сложнее, поскольку трекер записи недостаточно изощрен, чтобы позволить неперекрывающимся заимствованиям Vec. Однако существует ряд методов, в частности split_at_mut, которые вы можете позвонить, чтобы получить эти объекты. Самым простым является итератор chunks_mut, зарегистрированный here. (Обратите внимание, что существует итератор соответствия chunks для неизменяемого случая, поэтому вам нужно лишь внести незначительные изменения при записи любого случая).

Имейте в виду, что chunks и chunks_mut функции принимают размер каждого куска, а не количество кусков. Однако вывод одного из другого достаточно прост.

Я хотел бы дать несколько слов предостережения с изменчивым случаем. Если вы разделите данные равномерно, вы можете получить ужасную производительность. Причина в том, что ЦП не работает на отдельных адресах, вместо этого он работает с блоками памяти, известными как строки кеша длиной 64 байта. Если несколько потоков работают в одной строке кеша, они должны писать и читать медленную память, чтобы обеспечить согласованность между потоками.

К сожалению, в надежной русте нет простого способа определить, где в строке кэша запускается буфер Vec (поскольку запуск буфера, возможно, был выделен в середине строки кэша ЦП), большинство методов I знать, чтобы обнаружить это, включают слияние с нижними байтами фактического адреса указателя. Самый простой способ справиться с этим - просто добавить 64-байтовый блок бессмысленных данных между каждым фрагментом, который вы хотите использовать. Так, например, если у вас есть Vec, содержащий 1000 32-битных поплавков и 10 потоков, вы просто добавляете 16 поплавков с фиктивным значением (поскольку 32-бит = 4 байта, 16 * 4 = 64 = 1 кэш-строка) между каждый 100 ваших «реальных» поплавков и игнорирует манекены во время вычислений.

Это известно как ложное разделение, и я призываю вас найти другие ссылки, чтобы узнать другие методы борьбы с этим.

Обратите внимание, что размер в 64 байта гарантирован на архитектуре x86. Если вы компилируете ARM, PowerPC, MIPS или что-то еще, это значение может и будет меняться.

Смежные вопросы