Большое количество потоков в C ++ и Efficiency - PullRequest
6 голосов
/ 13 апреля 2011

В настоящее время я написал программу на C ++, которая иногда использует более 300 потоков.В моей программе у меня есть массив структур, а длина массива равна количеству потоков.Давайте предположим, что у меня 400 структур и, следовательно, 400 потоков.

За одну итерацию цикла for я применяю функцию к каждой из 400 структур, и эта функция выполняется в потоке.Поэтому у меня одновременно запущено 400 потоков.(Я использую библиотеку потоков повышения).

Я попытался дать представление о том, как выглядит мой код (это не фактический код):

struct my_struct{
  // Structure's members
};

std::vector<my_struct> my_vec;

void my_fun(my_struct* my_str){
// Operations on my_str
}

int main(){
  std::vector<boost::thread> thr(400);
  for (int k = 0; k < 300; k++){
    for (int i = 0; i < 400; i++){
      thr.at(i) = boost::thread(my_fun, &my_vec.at(i));
      }
    }

    for (int m = 0; m < M; m++){
      thr.at(m).join();
    }
  }
}

ФункцияЯ использую вычислительно интенсивно, и из кода выше, я использую 400 потоков, чтобы сделать вычисления, и это делается 300 раз.Есть ли более эффективный способ выполнения этой задачи?Я не уверен, может ли так много активных потоков одновременно влиять на производительность.Я слышал о библиотеке потоков пула, но не уверен, принесет ли она мне какую-то пользу.Любая помощь приветствуется.

Большое спасибо.

Ответы [ 6 ]

16 голосов
/ 13 апреля 2011

Совершенно бесполезно создавать 400 связанных с процессором потоков, если у вас на целевой машине 400+ процессорных ядер.

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

Как выстрел в темноте, судя по тому, что вы опубликовали, первым ударом будет использование потоков N (см.ниже), и разделите ваши 400 объектов между ними так, чтобы каждый поток отвечал за обработку приблизительно 400/N объектов.Каждый поток может выполнить цикл 300 раз, и на каждой итерации он может обрабатывать каждый из своих назначенных объектов.

N - произвольное число;на самом деле, я рекомендую попробовать разные значения и сравнить результаты производительности.Однако, если ваши потоки не выполняют ввод-вывод или другие операции, которые тратят время на блокировку не вычислительных операций, N не должно превышать число процессорных ядер на вашей машине (попробуйте и посмотрите, как падает производительность )быстро ).

Редактировать: в соответствии с продолжающимся обсуждением, было бы целесообразно использовать очередь ваших объектов, из которой каждый из ваших потоков N может просто вытолкнуть, поскольку они готовы к дополнительной работе.,Конечно, очередь должна быть поточно-ориентированной.Для оптимальной производительности должна быть реализована очередь без блокировки.Здесь есть хорошая бумага .Реализация должна быть упрощена тем фактом, что вы полностью заполняете очередь один раз и, следовательно, нуждаетесь только в поточно-ориентированном чтении.

4 голосов
/ 13 апреля 2011

Единственный способ выгодно иметь больше потоков, чем реально используемых механизмов исполнения (процессоры или ядра или что-то еще используется - я просто назову их процессорами здесь), это если некоторые потоки действительно ожидают ресурсы, кроме этих процессоров.

Если потоки связаны с процессором, идеальное число эквивалентно количеству процессоров, которые вам доступны. Если многие потоки ожидают файлового ввода-вывода или доступа к базе данных, сетевого трафика или событий ОС (и т. Д.), Тогда пара сотен может быть в порядке. Но в вашем случае это не так.

Пул потоков - это действительно способ избежать непрерывного создания и уничтожения потоков в ситуациях, когда это может быть относительно неэффективно. Например, если запуск потока занимает десять секунд, а каждая из них выполняет только одну секунду, тогда пул потоков будет идеальным.

Учитывая, что вы, вероятно, сократите число потоков до чего-то существенно менее четырехсот (скажем, около двух или четырех), что, в свою очередь, увеличит объем работы, выполняемой каждым, пул потоков может не понадобиться , Но опять же, это зависит от объема работы, выполняемой потоками, по сравнению со стоимостью их запуска.

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

Вы по-прежнему можете разделить свою работу на четыреста единиц, но лучшим подходом будет просто поставить их в очередь и заставить каждый из ваших потоков извлечь элемент из очереди, когда он будет готов обработать его. Таким образом, работа автоматически распределяется между процессорами. Если по какой-то странной причине процессор № 1 работает в два раза быстрее других, он автоматически получит вдвое большую нагрузку.

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

3 голосов
/ 13 апреля 2011

Сотни потоков звучат как проблема для вычислительно дорогих задач.Скорее всего, программа тратит больше времени на переключение контекста, чем на обработку.Попробуйте использовать N потоков (где N - количество ядер в вашей машине) и разделить работу на более крупные единицы.

2 голосов
/ 13 апреля 2011

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

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

Если все рабочие элементы независимы, вы просто делитесь на группы одинакового размера. Если существует зависимость между рабочими элементами (результат item1 необходим для item2), то вам нужно разделить на некоторые вещи, которые имеют смысл на основе зависимости.

1 голос
/ 14 апреля 2011

Во-первых, больше, чем максимальное число одновременных потоков - пустая трата. 1 ядро ​​с гиперпоточностью или SMT или, как производитель чипов хочет назвать, имеет 2 или более одновременных потоков. Вы должны выяснить, сколько одновременных потоков могут обрабатывать ваши ядра, и умножить это на количество ядер. Не нужно делать больше тем, чем это. У тебя было 400 тем. В одно время, вероятно, 396 из них спали.

Вместо того, чтобы беспокоиться о выравнивании строк кэша, вам нужно беспокоиться о "locality" . Когда вы прокручиваете данные, превышающие кэш L2, каждый доступ к памяти - это медленный доступ к памяти вплоть до RAM . Если вы просматриваете данные, которые меньше, чем кэш L2, то весь доступ к памяти осуществляется в кэш L2, что примерно в 100 раз быстрее . Кроме того, если все обращения к данным являются медленными, то все потоки выполнения на процессоре будут остановлены. SMT работает только потому, что чаще всего один поток останавливается в ожидании оперативной памяти, поэтому процессор может выполнить другой поток. Если вы делаете что-то неправильно и останавливаете все потоки, то вы в основном отключили SMT. Теперь у вас нет одновременных тем.

Итак ... если ваш набор данных больше, чем кэш L2, вам нужно "раздеть шахту" . Разбейте расчет на части, достаточно маленькие, чтобы поместиться в кэш второго уровня. Например, если у вас есть матрица, то разделите матрицу на n x m квадратов, которые могут уместиться в кэш L2, и позволить правильному количеству потоков работать с этим. Когда эта полоса закончена, переходите к следующей и так далее. Если вы сделаете это правильно, ваш код может стать в 100 раз быстрее.

Еще один способ увеличить местность - это сократить ваши данные. Сделайте данные как можно меньше. Чем меньше данные, тем больше они остаются в кеше L2.

1 голос
/ 13 апреля 2011

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

Обычно, если некоторые потоки ожидают некоторого периферийного оборудования, многопоточный подход может предложить некоторую гибкость вашему приложению.

В вашем случае - задачи с интенсивным использованием процессора - я сомневаюсь, что многопоточный подходпринесет производительность в ваше приложение.

...