Название: parallel transform (параллелим алгоритмы) Отправлено: m_ax от Ноябрь 05, 2013, 21:39 Приобщаясь к параллелизму написал (в рамках С++11) один пользующейся популярностью (во всяком случае у меня) алгоритм transform - аналог std::transform, но выполняющийся параллельно.
Возможно это велосипед.. Знаю про существование Intel TBB, VC++ PPL, OpenMP но.. просто было интересно) Код
тест: Код
Критика приветствуется) Название: Re: parallel transform (параллелим алгоритмы) Отправлено: b-s-a от Ноябрь 05, 2013, 23:49 1. вместо struct лучше использовать namespace
2. порядок записи результатов в выходной итератор неопределен. в первую очередь это касается ostream_iterator и back_insert_iterator. Поэтому тебе необходимо использовать ForwardIterator и сделать проверку на его использование. Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Igors от Ноябрь 06, 2013, 01:38 2. порядок записи результатов в выходной итератор неопределен. в первую очередь это касается ostream_iterator и back_insert_iterator. Поэтому тебе необходимо использовать ForwardIterator и сделать проверку на его использование. Не понял почему он не определен если каждая нитка пишет в свой участок?- если size не кратен nthreads, то последние эт-ты не обрабатываются - запуск нитки - удовольствие дорогое, поэтому заводят все нитки сразу, а в нужный момент "спускают с цепи" - пока считается - все заморожено (запускающий стоит на join) Название: Re: parallel transform (параллелим алгоритмы) Отправлено: m_ax от Ноябрь 06, 2013, 10:04 1. вместо struct лучше использовать namespace 2. порядок записи результатов в выходной итератор неопределен. в первую очередь это касается ostream_iterator и back_insert_iterator. Поэтому тебе необходимо использовать ForwardIterator и сделать проверку на его использование. 1) Да, возможно) Тож думал над этим) 2) У меня аналогичный вопрос, что и у igors Цитировать Не понял почему он не определен если каждая нитка пишет в свой участок? - если size не кратен nthreads, то последние эт-ты не обрабатываются - запуск нитки - удовольствие дорогое, поэтому заводят все нитки сразу, а в нужный момент "спускают с цепи" - пока считается - все заморожено (запускающий стоит на join) 1) Неправда, обрабатываются в любом случае.. (как раз последним std::transform из главной нитки) 2) А в чём разница между заводят и запускают?) 3) Именно так.. А для таких алгоритмов это кажется не логичным? Немного исправил: (сейчас последний трансформ из запускающей нитки запускается до join пораждённых потоков.) Код
Спасибо) Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Igors от Ноябрь 06, 2013, 14:13 1) Неправда, обрабатываются в любом случае.. (как раз последним std::transform из главной нитки) Давайте смотретьКод
2) А в чём разница между заводят и запускают?) Не "запускают" а "спускают", т.е. снимают с мутекслв. Причем мутексы комбинированные честный/нечестный. Напр после выполнения расчетов нитки усыпляются не сразу, а какое-то время работают вхолостую (а вдруг сейчас еще расчет). Длительность этой прокрутки значительна, по умолчанию 0.3 секунды(!) в OpenMP. А запуск - это тонна работы для ОС3) Именно так.. А для таких алгоритмов это кажется не логичным? Для любых. Никто не против "lite" веши, пусть есть многочисленные аналоги. Но выходит "ни богу свечка ни черту кочерга". Для маленьких рачсетов - запуск сожрет все, а если расчет идет секунды - ни отменить, ни обновить UI. Название: Re: parallel transform (параллелим алгоритмы) Отправлено: m_ax от Ноябрь 06, 2013, 14:38 Цитировать Давайте смотреть Запускается 3 нитки: в каждую передаются итераторы:Пусть size = 7, nthreads = 4. Запускаете 3 нитки (почему если есть 4 ядра?). group = 1, цикл выполняется 3 раза, обработано 3 (из 7), или как? [0 1] [1 2] [3 4] 4-ая нитка та, в которой и был вызвана parallel::transform В неё передаются итераторы: [4 7]. Этот хвост всегда будет отработан) В итоге алгоритм распределяется между всеми ядрами. Цитировать Для любых. Никто не против "lite" веши, пусть есть многочисленные аналоги. Но выходит "ни богу свечка ни черту кочерга". Для маленьких рачсетов - запуск сожрет все, а если расчет идет секунды - ни отменить, ни обновить UI. И всё-таки для таких алгоритмов, как простой transform требовать нечто большее (вплоть до возможности прерывания и т.д.) как то неоправданно.. Согласен с тем, что для маленьких расчётов (то, что делает передаваемая функция) особого выйгрыша не будет, если не наоборот.. Но если функция выполняется в среднем от 0.1 - 0.01 сек, то для меня результат уже ощутим, даже если размер контейнера порядка 100.. Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Igors от Ноябрь 06, 2013, 16:37 Запускается 3 нитки: в каждую передаются итераторы: [4 8]. А чего же всем по одной а главной аж 4 задачи? :) Это легко исправить[0 1] [1 2] [3 4] 4-ая нитка та, в которой и был вызвана parallel::transform В неё передаются итераторы: [4 7]. Этот хвост всегда будет отработан) В итоге алгоритм распределяется между всеми ядрами. Код
Атомарная переменная (отслеживающая число выполненных задач) здесь была бы к месту. Также лучше добавить флажок m_master (true для запускающей). Тогда "деревья не кажутся столь уж большими", напр Код Нагрузку (result =) лучше вынести "хвунктором" :) Тут правда возникает мелкая проблема - главная закончила свои дела и ушла на join - заморозка. Ну это тоже решаемо Название: Re: parallel transform (параллелим алгоритмы) Отправлено: m_ax от Ноябрь 06, 2013, 17:43 [4 8]. А чего же всем по одной а главной аж 4 задачи? :) Это легко исправить Согласен, но думаю, можно проще:Код
Код
Цитировать Атомарная переменная (отслеживающая число выполненных задач) здесь была бы к месту. Также лучше добавить флажок m_master (true для запускающей). Тогда "деревья не кажутся столь уж большими", напр Ну это уже больше детали прикручивания алгоритма к конкретным задачам/ситуациям.. Конечно, в более сложных случаях может понадобиться и функтор с разделяемыми ресурсами и т.д.. ) Название: Re: parallel transform (параллелим алгоритмы) Отправлено: b-s-a от Ноябрь 06, 2013, 18:18 2. порядок записи результатов в выходной итератор неопределен. в первую очередь это касается ostream_iterator и back_insert_iterator. Поэтому тебе необходимо использовать ForwardIterator и сделать проверку на его использование. Не понял почему он не определен если каждая нитка пишет в свой участок?Я подскажу, попробуйте код, в котором результат пишется в изначально пустой вектор с помощью std::back_inserter. Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Igors от Ноябрь 06, 2013, 18:24 Согласен, но думаю, можно проще: Что бы ни выдала std::lround - это константв, а здесь это не устраивает. Не тянитесь к std::рюмочке, это Вам только мешает :'( Код
Господа, вы вообще знаете чем отличается OutputIterator от ForwardIterator? Я лично не имею ни малейшего понятия :) Если нетрудно, поясните. СпасибоЯ подскажу, попробуйте код, в котором результат пишется в изначально пустой вектор с помощью std::back_inserter. Название: Re: parallel transform (параллелим алгоритмы) Отправлено: m_ax от Ноябрь 06, 2013, 18:35 Цитировать Господа, вы вообще знаете чем отличается OutputIterator от ForwardIterator? Я подскажу, попробуйте код, в котором результат пишется в изначально пустой вектор с помощью std::back_inserter. Понятно) Да, согласен - надо запрещать такие ситуации) Исправлю) Спасибо) Цитировать Что бы ни выдала std::lround - это константв, а здесь это не устраивает. Подумаю.. Цитировать Я лично не имею ни малейшего понятия b-s-a имеет в виду, что вот такая ситуация:Код может привести к непредсказуемым последствиям) Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Igors от Ноябрь 06, 2013, 19:28 b-s-a имеет в виду, что вот такая ситуация: То ясно, но так же и для любого тулза. Вообще нужно ли следовать подходу transform, может гибче и лучше дать возможность функтору самому решать куда писать результатКод может привести к непредсказуемым последствиям) Код
Название: Re: parallel transform (параллелим алгоритмы) Отправлено: b-s-a от Ноябрь 06, 2013, 22:18 Igros, а если тебе надо тупо вывести в поток через std::ostream_iterator? Как ты это сделаешь? Через дополнительный массив?
Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Igors от Ноябрь 07, 2013, 10:38 Igros, а если тебе надо тупо вывести в поток через std::ostream_iterator? Как ты это сделаешь? Через дополнительный массив? Да, придется использовать доп контейнер если "элементы вывода" считаются параллельно. Иначе порядлк вывода невоспроизводим Название: Re: parallel transform (параллелим алгоритмы) Отправлено: m_ax от Ноябрь 07, 2013, 11:54 2. порядок записи результатов в выходной итератор неопределен. в первую очередь это касается ostream_iterator и back_insert_iterator. Поэтому тебе необходимо использовать ForwardIterator и сделать проверку на его использование. Исправил. Поставил статик асерт с проверкой на принадлежность к forward iterator type: Код
Цитировать Что бы ни выдала std::lround - это константв, а здесь это не устраивает. Не тянитесь к std::рюмочке, это Вам только мешает Согласен, что вариант с lround - неправильный. Вот только стандартная библиотека здесь ни при чём..Всё равно можно сделать это проще: Полное число элементов всегда можно представить в виде: size = m * (size/nthreads + 1) + (nthreads - m) * (size/nthreads) m - это число нитей которые принимают интервал длиной size/nthreads + 1 (nthreads - m) - число нитей принимающих интервал длиной size/nthreads Далее логика такая: сначала раздаём m потокам длинные интервалы, а всем оставшимся достаются нормальные size/nthreads интервалы. Выглядит сейчас это так: Код
Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Igors от Ноябрь 07, 2013, 13:39 Всё равно можно сделать это проще: Ну проще это никак не выглядит :) И при этом запускающая остается не у дел (а задействовать ее было хорошей идеей). Кроме того Ваш метод не общий, предполагается что size > nthread.Часто встречается такая задача: есть N треугольников, их площади известны. Распределить по ним заданное число точек, ну напр 100. Название: Re: parallel transform (параллелим алгоритмы) Отправлено: m_ax от Ноябрь 07, 2013, 13:45 Всё равно можно сделать это проще: Ну проще это никак не выглядит :) И при этом запускающая остается не у дел (а задействовать ее было хорошей идеей). Кроме того Ваш метод не общий, предполагается что size > nthread.1) Да нет, проще же) 2) Запускающая нить не остаётся не у дел) Она всегда отрабатывает, причём в неё заряжается интервал, размером size/nthreads 3) Предложенный метод общий - он работает и в случае size == nthreads :) пояснение: Если size кратен или равен nthreads, то m == 0. Это означает, что все нитки получат интервалы одинаковой длины size/nthreads. В противном случае будет выполнятся условие 0 < m < nthreads. При этом запускающая нитка всегда получает интервал size/nthreads Цитировать Часто встречается такая задача: есть N треугольников, их площади известны. Распределить по ним заданное число точек, ну напр 100. В смысле есть N площадей и m точек и их нужно разбить на N групп так, чтобы размер каждой был пропорционален соответствующей площади? Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Igors от Ноябрь 07, 2013, 18:30 В смысле есть N площадей и m точек и их нужно разбить на N групп так, чтобы размер каждой был пропорционален соответствующей площади? Да. По сути это та же задача что и разбросать нагрузку по ниткам, только в более общем виде. Напр при N > 100 какие-то треугольники не будут иметь ни одной точки, но при этом другие могут иметь больше однойНазвание: Re: parallel transform (параллелим алгоритмы) Отправлено: m_ax от Ноябрь 07, 2013, 21:36 Добавил алгоритм for_each
заменил struct на namespace Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Alex Custov от Ноябрь 07, 2013, 22:09 OpenMP это общий случай, в Qt есть QtConcurrent, классная штука.
Название: Re: parallel transform (параллелим алгоритмы) Отправлено: m_ax от Ноябрь 07, 2013, 22:45 OpenMP это общий случай, в Qt есть QtConcurrent, классная штука. Да, я знаю о QtConcurrent.. Но Qt я редко использую, для моих скромных целей вполне хватает стандартной библиотеки и буста) Всё же OpenMP - это не общий случай.. Это всего лишь стандарт (открытый) для распараллеливания со своими директивами, процедурами и переменными окружения.. Возможности, предоставляемые для поддержки многопоточности в C++11 выглядят более богаче) Я ничего не имею против OpenMP, напротив, в некоторых ситуациях openmp более предпочтительнее.. а в каких то не очень) Название: Re: parallel transform (параллелим алгоритмы) Отправлено: Igors от Ноябрь 08, 2013, 09:07 Всё же OpenMP - это не общий случай.. Это всего лишь стандарт (открытый) для распараллеливания со своими директивами, процедурами и переменными окружения.. Ну сказали тоже :) Ладно, чтобы не скатываться во флуд - предлагаю проверить на тестах 3 подходаВозможности, предоставляемые для поддержки многопоточности в C++11 выглядят более богаче) - С++ 11 (m_ax) - OpenMP (беру на себя) - QtConcurrent (есть смелые ?) |