Самое простое решение, вероятно, состоит в том, чтобы использовать атомный счетчик для назначения индексов в меньшем массиве.
Порядок, в котором элементы будут появляться в массиве, не будет предсказуемым, поэтому вам необходимо будет также сохранить идентифицирующую информацию (например, оригинал id
), а затем, возможно, отсортировать ее в зависимости от того, что вам нужно делать с этот вывод следующий.
Однако, действительно ли это эффективно , зависит от ряда факторов; что еще хуже, эти факторы работают друг против друга.
Прежде всего, звучит так, что вероятность того, что элемент из этого второго выходного массива будет меньше, составляет менее 0,1%. Небольшое число, подобное этому, полезно для атомных элементов: чем больше рабочих элементов хотят увеличить значение этого счетчика, тем выше вероятность того, что они попытаются сделать это одновременно и заблокировать друг друга и сериализовать. Кроме того, распределение имеет значение. Любая кластеризация будет работать против вас. Если ваши 0,1% в основном являются смежными, они также будут смежными в рабочей группе, а рабочие элементы в рабочей группе обычно выполняются в режиме блокировки на графическом процессоре. Поэтому им всем нужно подождать, пока друг друга завершат приращение, прежде чем любой из них сможет продолжить.
С другой стороны, если another_func()
является достаточно дорогостоящим в вычислительном отношении (что предполагает ваш вопрос), то равномерное распределение равно плохо , поскольку большинство рабочих элементов в группе будут работать вхолостую, в то время как один элемент работает another_func()
и занимает весь вычислительный блок.
Возможные варианты простого подхода для устранения различных недостатков:
- Кластеризация элементов, принимающих ветвь, или высокая вероятность выбора ветки. В этом случае вы можете выделять диапазоны выходного массива для всей рабочей группы за раз, поэтому только один рабочий элемент в группе увеличивается счетчик. Вам нужно будет либо выделить количество выходных слотов для всей рабочей группы, даже если некоторые из них не нужны, либо запустить алгоритм сокращения по всей группе, чтобы определить, сколько необходимо , и назначить смещения индекса.
- Некластеризованные рабочие элементы с дорогими условными вычислениями. В этом случае вы можете выводить только индексы элементов, которые нуждаются в дальнейшем вычислении, в массив с использованием атомарного метода, а затем вызывать
another_func()
во втором ядре, где каждый рабочий элемент работает с одним из элементов, требующих дальнейшего вычисления, поэтому все рабочие элементы запускаются another_func()
, что означает, что ядра полностью заняты.
Эти два варианта, конечно, могут быть объединены при необходимости, и, вероятно, вы можете внести дополнительные усовершенствования, особенно если вы можете использовать профилировщик вашей платформы для выявления узких мест.