Здравствуйте, AlexGin, Вы писали:
AG>Собираюсь делать новый проект на C++, при этом планирую активно AG>применять распарраллеливание обработки данных за счёт многопоточности.
AG>Какие могут быть соображения по выбору?
В данном случае выбор не важен. Важны алгоритмы. Если параллельный алгоритм часто ждет на мьютексе, то реализация мьютекса не очень важна, на самом то деле. Он будет медленно работать. Поэтому я бы задумался прежде всего об алгоритмах и о том как избежать синхронизации, насколько это возможно. Конкретный инструмент я бы выбирал в зависимости от задачи, если у вас data-parallel алгоритмы, то стоит использовать OpenMP, например, а не явную синхронизацию, ну а если это серверное приложение — то стоит использовать то что предоставляет ваш тулкит для построения серверов (strands в boost.asio например).
CK>Если параллельный алгоритм часто ждет на мьютексе, то реализация мьютекса не очень важна, на самом то деле
Зависит от задачи. Если время, которое будет затрачено на разделяемую задачу, сравнительно небольшое, то обращаться каждый раз к ядру, например, может быть слишком расточительно.
Здравствуйте, AlexGin, Вы писали:
AG>Собираюсь делать новый проект на C++, при этом планирую активно AG>применять распарраллеливание обработки данных за счёт многопоточности.
Многопоточность -- это инструмент, который используется в двух сильно разных направлениях:
1. Parallel computing. Т.е. использование распараллеливания вычислений на все доступные ядра для уменьшения общего времени решения задачи. При этом, вероятно, каждое ядро будет выполнять одну и ту же последовательность операций, но над разными порциями данных. В задачах подобного рода используются свои наборы инструментов. Например, MPI (совокупность однопоточных процессов, возможно, работающих на разных узлах), OpenMP (как расширения языка программирования и соответствующая поддержка со стороны компилятора), Threading Building Blocks, HPX и т.п.
2. Concurrent computing. Т.е. использование всех доступных ядер для параллельного выполнения различных (почти) независимых задач. При этом, вероятно, каждая задача будет выполнять свой собственный набор операций. Тут используется несколько другой набор инструментов (хотя тот же Threading Building Blocks может пригодиться и здесь).
Соответственно, для того, чтобы давать советы, нужно понимать, что именно вы понимаете под "распараллеливанием обработки данных". Может статься (причем это наверняка так), что для ваших задач уже давным давно придуманы инструменты, которыми нужно просто суметь воспользоваться. А не пытаться повторить их на коленке с помощью WinAPI или низкоуровневых оберток над системным API (вроде std::mutex-а).
Здравствуйте, b0r3d0m, Вы писали:
B>Зависит от задачи. Если время, которое будет затрачено на разделяемую задачу, сравнительно небольшое, то обращаться каждый раз к ядру, например, может быть слишком расточительно.
Нет, не зависит от задачи. Можно захватывать мьютекс относительно редко и ненадолго, но при этом система будет медленной (из-за cache line ping-pong-а и false sharing-а), а можно захватывать мютексы очень часто, на каждый чих вообще, но система будет работать быстро. Это вопрос дизайна алгоритмов и все. Никакая навороченная реализация примитивов синхронизации не спасет от их тупого использования.
CK>а можно захватывать мютексы очень часто, на каждый чих вообще, но система будет работать быстро
Если система будет на каждый чих обращаться к ядру и засыпать в ожидании пробуждения, то о каком быстродействии может идти речь?
Здравствуйте, b0r3d0m, Вы писали:
B>Если система будет на каждый чих обращаться к ядру и засыпать в ожидании пробуждения, то о каком быстродействии может идти речь?
А мьютексы на каждый чих обращаются к ядру? Если захватывается свободный мьютекс, никакого обращения к ядру не происходит. Системный вызов выполняется только тогда, когда поток нужно усыпить, ну а задача параллельного алгоритма — сделать так, чтобы поток практически никогда не нужно было усыплять.
Пример — у тебя есть некий реестр объектов и есть потоки, которые работают с объектами из этого реестра. Обычно каждый поток работает со своим уникальным набором объектов, поэтому ты создаешь по одному локальному реестру объектов на каждый поток. Когда поток хочет начать работать с неким объектом, он берет и переносит атомарно объект из глобального реестра в свой локальный, по прошествии таймаута объект возвращается обратно, ну то есть это по сути реализация lease схемы, много где такое используется.
У глобального реестра есть мьютекс, у локального реестра есть мьютекс, у самого объекта тоже может быть мьютекс. Если потоку нужно обратиться к объекту, который был уже захвачен другим потоком — он получает адрес локального реестра того потока, захватывает его мьютекс и обращается к объекту. Но fast path — это захват одного или двух uncontended мьютексов, локальных для потока, никаких обращений к ядру, никаких дорогих операций. Дорогие операции появляются в slow path — захват мьютекса, принадлежащего другому потоку, который использовался другим потоком и даже может быть в данный момент захвачен этим другим потоком. Но это редкая операция.
Вот такие алгоритмы я имел ввиду. Это многопоточное программирвоание, все что не учитывает разницу между contended и uncontended состояниями мьютексов и памяти — многопоточным программированием не является, это просто разнообразные thread-safe реализации, которые могут иногда даже работать быстрее на многоядерных и многопроцессорных машинах (а могут и медленнее. Рассуждения о синхронизации без вот этого вот контекста — просто пустая трата времени, которая показывает только то, что рассуждающий очень поверхностно понимает многопоточность.
Здравствуйте, b0r3d0m, Вы писали:
CK>>А мьютексы на каждый чих обращаются к ядру? B>Насколько я знаю, в Windows -- да.
Win API функции тоже могут делать что-нибудь в user-space. Я не знаю как это точно реализовано в win-api (не писал ничего под эту платформу лет эдак шесть), но например в linux никто никогда не делает системные вызовы напрямую — все вызовы завернуты в вызовы glibc и тот же pthread_mutex_lock много чего делает в userspace прежде чем делать системный вызов, там в принципе может и без системного вызова обойтись. Подозреваю в win32 api что-то похожее, ну либо у них там мьютекс это действительно голый объект ядра, а поддержку в userspace имеют только критические секции.
Здравствуйте, so5team, Вы писали:
S>Здравствуйте, AlexGin, Вы писали:
AG>>Собираюсь делать новый проект на C++, при этом планирую активно AG>>применять распарраллеливание обработки данных за счёт многопоточности.
S>Многопоточность -- это инструмент, который используется в двух сильно разных направлениях:
S>1. Parallel computing. Т.е. использование распараллеливания вычислений на все доступные ядра для уменьшения общего времени решения задачи. При этом, вероятно, каждое ядро будет выполнять одну и ту же последовательность операций, но над разными порциями данных. В задачах подобного рода используются свои наборы инструментов. Например, MPI (совокупность однопоточных процессов, возможно, работающих на разных узлах), OpenMP (как расширения языка программирования и соответствующая поддержка со стороны компилятора), Threading Building Blocks, HPX и т.п.
Да — именно об этом и идет речь в моём посте.
S>2. Concurrent computing. Т.е. использование всех доступных ядер для параллельного выполнения различных (почти) независимых задач. При этом, вероятно, каждая задача будет выполнять свой собственный набор операций. Тут используется несколько другой набор инструментов (хотя тот же Threading Building Blocks может пригодиться и здесь).
Это не для данного случая.
S>Соответственно, для того, чтобы давать советы, нужно понимать, что именно вы понимаете под "распараллеливанием обработки данных". Может статься (причем это наверняка так), что для ваших задач уже давным давно придуманы инструменты, которыми нужно просто суметь воспользоваться.
Вполне возможно.
S>А не пытаться повторить их на коленке с помощью WinAPI или низкоуровневых оберток над системным API (вроде std::mutex-а).
Дело в том, что в предшествующих разработках у меня наблюдалась работа над "вариантом 2".
Здравствуйте, b0r3d0m, Вы писали:
CK>>ну либо у них там мьютекс это действительно голый объект ядра, а поддержку в userspace имеют только критические секции. B>Да.
B>Помимо этого, в Windows они могут шариться между процессами.
The threads of a single process can use a critical section object for mutual-exclusion synchronization. The process is responsible for allocating the memory used by a critical section object, which it can do by declaring a variable of typeCRITICAL_SECTION. Before using a critical section, some thread of the process must call InitializeCriticalSection orInitializeCriticalSectionAndSpinCount to initialize the object.
Здравствуйте, AlexGin, Вы писали:
S>>Соответственно, для того, чтобы давать советы, нужно понимать, что именно вы понимаете под "распараллеливанием обработки данных". Может статься (причем это наверняка так), что для ваших задач уже давным давно придуманы инструменты, которыми нужно просто суметь воспользоваться. AG>Вполне возможно.
Ну так приоткройте тайну, может вам более конкретные вещи посоветуют.
Здравствуйте, so5team, Вы писали:
S>Здравствуйте, AlexGin, Вы писали:
S>>>Соответственно, для того, чтобы давать советы, нужно понимать, что именно вы понимаете под "распараллеливанием обработки данных". Может статься (причем это наверняка так), что для ваших задач уже давным давно придуманы инструменты, которыми нужно просто суметь воспользоваться. AG>>Вполне возможно.
S>Ну так приоткройте тайну, может вам более конкретные вещи посоветуют.
Обработка данных. Эта обработка должна быть распараллелена по всем ядрам CPU.
Как подсчитать количество ядер — дело ясное:
Ну а дальше — создать столько потоков (threads), сколько у нас ядер — и считать.
Все потоки считают по одинаковому алгоритму, складывают данные (результаты) в общие коллекции.
Вот собственно и все премудрости.
AG>Нет — критические секции в WinAPI не разделяются между процессами
Я про Mutex'ы.
AG>Именно поэтому у меня и возник данный вопрос: AG>можно ли в Qt, boost, STL использовать такие штуковины, как критические секции
Да что вы так WinAPI боитесь-то? Напишите свой враппер, протестируйте его и забудьте про детали реализации.
Алсо, у Microsoft есть заголовочный файл concrt.h, в котором, помимо всего прочего, есть обёртка над критическими секциями. Насколько он публичный, я не в курсе, надо гуглить.
Здравствуйте, AlexGin, Вы писали:
AG>В связи с этои возникает вопрос: AG>Оптимальная (прежде всего с точки зрения производительности) реализация многопоточности: AG>1) При помощи функций WinAPI — вероятно, это самый производительный вариант, однако недостаток — нет кроссплатформенности; AG>2) Средствами Qt — QThread кроссплатформенность есть, вопрос по производительности не очевиден, доп-бонус — отсутствие сложного кода, как в п.1; AG>3) Средствами STL — std::thread (примерно та же картина как и во втором пункте). AG>Дополнительная красота третьего пункта — если будет серверное приложение, без GUI, то не нужно "притягивать за уши" доплнительные библиотеки.
AG>Какие могут быть соображения по выбору?
int QThread::idealThreadCount () [static]
Возвращает идеальное количество потоков, которое может быть запущено в системе. Она делает запрос количества процессорных ядер, как реальных, так и логических. Эта функция возвращает -1, если количество процессорных ядер не может быть определено.
Ещё добавлю, что параллельное программирование в Qt состоит не из одного QThread.
Пространство имен QtConcurrent предоставляет высокоуровневые API, которые делают возможным написание многопоточных программ без использования низкоуровневых потоковых примитивов, таких как мьютексы, блокировки чтение-запись, условия ожидания или семафоры. Программы, написанные с помощью QtConcurrent, автоматически приводят количество используемых потоков в соответствие с доступным количеством процессорных ядер. Это означает, что приложения, написанные сегодня, будут продолжать масштабироваться при развертывании на многоядерных системах в будущем.
SISD (Single Instruction, Single Data) – системы, в которых существует одиночный поток команд и одиночный поток данных. К такому типу можно отнести обычные последовательные ЭВМ;
SIMD (Single Instruction, Multiple Data) – системы c одиночным потоком команд и множественным потоком данных. Подобный класс составляют многопроцессорные вычислительные системы, в которых в каждый момент времени может выполняться одна и та же команда для обработки нескольких информационных элементов; такой архитектурой обладают, например, многопроцессорные системы с единым устройством управления. Этот подход широко использовался в предшествующие годы (системы ILLIAC IV или CM-1 компании Thinking Machines), в последнее время его применение ограничено, в основном, созданием специализированных систем;
MISD (Multiple Instruction, Single Data) – системы, в которых существует множественный поток команд и одиночный поток данных. Относительно этого типа систем нет единого мнения: ряд специалистов считает, что примеров конкретных ЭВМ, соответствующих данному типу вычислительных систем, не существует и введение подобного класса предпринимается для полноты классификации; другие же относят к данному типу, например, систолические вычислительные системы (см. [51, 52]) или системы с конвейерной обработкой данных;
MIMD (Multiple Instruction, Multiple Data) – системы c множественным потоком команд и множественным потоком данных. К подобному классу относится большинство параллельных многопроцессорных вычислительных систем.
Причём за использование того же OpenMP, MPI или ещё чего-нибудь придётся заплатить свою цену. Если нужно обычное прикладное приложение, то проще использовать чистый Qt. Могу сказать и по другому, если используется Qt, то это явно прикладное приложение, а не какой-нибудь навороченный кластер на тысячи компьютеров, иначе это был бы boost или что-то в этом роде.
Здравствуйте, b0r3d0m, Вы писали:
_>>Хочешь рапараленлить вычисления MPI, OpenACC, OpenCL
B>Тогда уж и про OpenMP не забываем.
Дык OpenMPI реализация MPI также как и MPICH
Здравствуйте, AlexGin, Вы писали:
AG>Актуален обмен данными между потоками.
Нужно сделать так чтобы был не актуален. AG>Какие могут быть соображения по выбору?
QT AG>Огромное спасибо, за любые мысли!
Только QT.
Здравствуйте, b0r3d0m, Вы писали:
CK>>А мьютексы на каждый чих обращаются к ядру? B>Насколько я знаю, в Windows -- да.
Емнип, кажется у Рихтера давно читал, что при захвате CRITICAL_SECTION делается короткий спинлок чтобы сразу не уходить в ядро.
Вообще в Windows Vista кроме Aero темы появилось новое ядро с новыми примитивами: CONDITION_VARIABLE и SRWLOCK. Последний можно использовать в реализации std::mutex, но не знаю так ли это в Visual C++