Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Проблема с многопоточностью в Windows  (Прочитано 24116 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
Sket4
Участник

ru
Offline Offline
Пол: Мужской

« : 22-08-2010 13:16 » 

Здравствуйте!
Написал программу для расчета числа пи методом Монте-карло. Чтобы получить достоверное значение, нужно выполнить некоторый алгоритм n-е число раз. Вкратце, алгоритм такой: генерируем случайное число заданном в диапазоне и проверяем его на вхождение в некоторую область, если число "входит", увеличиваем счетчик вхождений на 1. Эти операции в моей программе повторяются 250 млн. раз (чем больше, тем точнее результат). Решил "помочь" процессору, создав поток, выполняющий те же самые операции
Вот сам алгоритм:
Код:
// passes - кол-во просчетов алгоритма ( == 250 млн.)
// kv_count - число текущих просчетов
// kr_count - число "подходящих точек"
while(kv_count < passes)
{
// генерируем случайные координаты в пределах квадрата
x = rand() % radius;
y = rand() % radius;

// увеличиваем число обработанных точек
InterlockedExchangeAdd(&kv_count, 1);

// проверяем точку на вхождение в круг
if((x * x + y * y) <= radius * radius)
// если точка подходит, увеличиваем число вхождений на 1.
InterlockedExchangeAdd(&kr_count, 1);

}

Расчет алгоритма 250 млн. раз на моем компьютере занимает примерно 35 сек.
Теперь сама проблема: после просчета я обьявляю переменную r2 и присваиваю ей значение r2 = radius * radius, но в самом цикле не использую. Запускаю - теперь расчет занимает 50 секунд. Пробую закомментировать строку r2 = radius * radius, получаю снова 30 сек. Не понимаю, в чем проблема  А черт его знает... 99 % процессорного времени занимает прогон цикла, в котором перменная r2 даже не используется...
Кто знает,прошу помощи  Улыбаюсь
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 22-08-2010 13:21 » 

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

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 22-08-2010 13:29 » new

Я бы, кстати, вообще избавился от синхронизации каких-то счётчиков в работе алгоритма. Просто разделил бы число тестов на нужное количество потоков, считал бы в каждом потоке отдельно, а по итогам всей работы суммировал бы счётчики от каждого потока.
Кстати, не уверен, что rand потоконезависимый. Потоки создаются как, beginthreadex_ или CreateThread?
Записан
Sket4
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 22-08-2010 13:31 » 

В Debug- версии в обоих случаях время расчета одно. В релиз - версии без этой строки расчет выполняется гораздо быстрее. Пусть компилятор и уберет ее, мне не понятно, каким образом она влияет на время расчета, будучи ни разу не задействованная в ней. Кстати, процессор двухядерный.
Потоки создаются через _beginthreadex. В настройках проекта используется Многопоточная DLL
Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 22-08-2010 13:34 » 

Sket4, не совсем понял, где ты заводишь и как используешь r2. Можешь показать изменённый код?
Записан
Sket4
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 22-08-2010 13:39 » 

int r2;    // глобальная переменная, обьявляется до ф-ии main
В теле ф-ии main, до выполнения цикла пишу строку: r2 = radius * radius. Это все. Если недостаточно, вложил файл с проектом.
Есть такие мысли, может дело в выравнивании данных  А черт его знает...

* TEST_pi.rar (7.88 Кб - загружено 719 раз.)
Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 22-08-2010 14:20 » 

У меня 2008 студия, пришлось создать свой проект.
Твой код у меня работает в релизе за 21.0-21.5 секунду в обоих случаях (со строкой и без неё). Или это какой-то баг в компиляторе 2010, или что-то ещё. Ты запускаешь релиз отдельно от студии? (Ctrl+F5, например)
Записан
Sket4
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 22-08-2010 14:51 » 

Сейчас запустил программу на другом компьютере, однопроцессорном с гипертрейдингом, результат тот же, как в студии, так и отдельно от нее. Возможно и баг, а может какая то особенность компилятора 2010 студии...
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 22-08-2010 15:29 » 

По-моему алгоритм некорректный. Идея Монте-Карло состоит в том, чтобы соотнести площади внутри фигуры и полной площади. Это значит, что с заданной точностью будет получен результат при покрытии тестами всей полной площади (обычное сканирование по строкам и столбцам). При этом всегда есть множество ещё не рассмотренных и уже рассмотренных точек. Чтобы ускорить получение результата, точки выбираются равномерно случайным образом из множества нерассмотренных - равномерное случайное распределение при этом гарантирует, что число попавших в фигуру точек соотносится с общим числом рассмотренных точек так же, как соотносятся полные множества точек внутри фигуры и всех точек (т.е. плотность распределения одинакова на всей площади).

В приведённом решении нет контроля повторных попаданий в одну и ту же точку, а распределение остатков от деления не является равномерным. Это значит, что возможно получение плохого случая, когда плотность точек внутри фигуры может быть не равной плотности вне фигуры. В пределе стабильно попадаем в центр круга 250 млн раз и "узнаём", что площать круга равна площади квадрата Улыбаюсь
« Последнее редактирование: 22-08-2010 15:37 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Sket4
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 22-08-2010 16:30 » 

Про некорректность соглашусь, даже при расчетах в 250 млн. раз результат может быть равен 3.146 Улыбаюсь но меня это сейчас мало интересует, эту реализацию я наспех набросал Улыбаюсь проект был создан с целью протестировать выгоду многопоточности, столкнулся вот с полным непониманием принципаработы компилятора в 2010 студии Улыбаюсь
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 23-08-2010 09:32 » 

Sket4, а причём тут компилятор?

Прежде чем браться за потоки, хорошо было бы выполнить профилирование и узнать, какие инструкции являются самыми "тяжёлыми".

Второй момент: выгода от многопоточности будет лишь при эффективном распараллеливании выполнения инструкций (например, по ядрам процессора). В противном случае будет лишь торможение на переключении контекстов в Windows (если много потоков).

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

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

Лучше бы о предложении Вада подумал.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #11 : 23-08-2010 23:21 » 

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

С уважением Lapulya
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 24-08-2010 10:34 » 

Цитата: lapulya
У него в коде (тот, что отражается в ветке, прикрепленный файл не смотрел) нет переключения контекстов
Естественно, это делает диспетчер задач операционной системы, а не программист.
Цитата: lapulya
поскольку ни критических секций, ни тем более синхронизаций с использованием объектов ядра
У него есть:
Цитата: Sket4
InterlockedExchangeAdd(&kv_count, 1);
В моих представлениях это на много ядер не параллелится по определению - все потоки завязаны на общий участок памяти, операции с которым (инкремент) выполняются строго последовательно (хотя и гораздо быстрее, чем с использованием объектов синхронизации). Этот участок памяти может быть кэширован либо одним ядром, либо не может быть кэширован - со всеми вытекающими последствиями.

Эффективное использование многих ядер возможно лишь в таком решении, которое предложил Вад.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #13 : 24-08-2010 12:58 » 

Еще раз повторяю - нет у него переключений контекста. Interlocked вызовы не приводят к переключениям контекста.

Нет у него использований объектов ядра. Объект ядра это некоторый дескриптор, их в коде нет.

Кстати, нет никакой гарантии (в таком коде), что потоки будут выполняться на разных ядрах проца.
« Последнее редактирование: 24-08-2010 13:03 от lapulya » Записан

С уважением Lapulya
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 24-08-2010 14:14 » 

Цитата: laplulya
Еще раз повторяю - нет у него переключений контекста. Interlocked вызовы не приводят к переключениям контекста.

Нет у него использований объектов ядра. Объект ядра это некоторый дескриптор, их в коде нет.

Кстати, нет никакой гарантии (в таком коде), что потоки будут выполняться на разных ядрах проца.
И? Спор ради спора?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #15 : 25-08-2010 06:13 » 

да я и не спорил, я просто уточнить хотел.
« Последнее редактирование: 25-08-2010 06:35 от lapulya » Записан

С уважением Lapulya
Sket4
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 25-08-2010 18:16 » 

Так, как предложил Вад, уже сделал, время вычисления сократилось в два раза, после того, как убрал из кода Interlocked функции. Не спорю, что компилятор правильно исполняет свои обязанности, интереснее то, что результаты разительно отличаются в разных версиях компилятора, причем в версии поновее - в отрицательную сторону. А сама программа, пускай и написана "сходу", послужила хорошим полигоном для изучения плюсов многопоточности (я только - только начал втыкать в них Улыбаюсь. Плюсы были, кстати. Если кому интересно, при использовании 2х потоков вместо одного время вычисления на одноядерном проце с HT сократилось на 20 %, на двухядерном на 55 %. При трех потоках - уже минус. Ну может на трехядерном процессоре был бы прирост... За советы огромное спасибо, ценю  Улыбаюсь
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #17 : 25-08-2010 22:05 » 

там кстати какие-то статьи были про многопоточность... помоему...
Записан

С уважением Lapulya
Sket4
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #18 : 26-08-2010 11:08 » 

Там, это где? Улыбаюсь
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #19 : 26-08-2010 11:14 » 

Где то тут
https://club.shelek.ru/view.php

не... наврал Краснею там про объекты ядра есть, а не про многопоточность и синхронизацию

ну тогда тут
https://club.shelek.ru/download.php?id=239
« Последнее редактирование: 26-08-2010 11:20 от lapulya » Записан

С уважением Lapulya
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #20 : 26-08-2010 15:23 » 

Кстати, если помнить про разные фреймворки (как раз только что на глаза попался примерчик про Intel TBB: http://habrahabr.ru/blogs/development/102670/), данную задачу вообще практически не надо параллелить. Точнее, надо лишь организовать так, чтобы тесты были независимые. Думаю, это тоже полезно иметь в виду: если сколько-то там лет назад ничего такого ещё не было, то теперь есть openMP и тот же TBB, и если задача успешно решается готовыми средствами, не стоит городить велосипедов.

А вот, кстати, интересно, что будет с rand() в случае, если я опишу алгоритм и засуну в TBB. Но, думаю, об этом позаботились Улыбаюсь
Записан
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #21 : 26-08-2010 20:19 » 

можно мои пять копеек вставить? поскольку оптимизацию люблю...

1. ошибка использовать InterlockedExchangeAdd() если возвращаемое значение вам не нужно. вместо нее - необходима всего лишь InterlockedIncrement().

2. Interlocked-функции выполняются очень быстро(это единственная аппаратно реализуемая синхронизация) - однако завернуты в вызов call. чтобы избежать этого - по возможности использовать intrinsic-аналоги _InterlockedIncrement().

3. само-собой включить всю отпимизацию на максимум.

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

5. основные накладные места цикла - всего два:
    1. rand() - посмотреть реализацию, не уверен что она очень быстрая, и хорошо работает в нескольких нитях
    2. вызов InterlockedExchangeAdd. не сама функция а именно вызов!

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

PS kv_count и kr_count  должны быть объявлены как volatile. так правильнее)
PPS а профайлер в данном случае бесполезен... тут же совсем простой код - все видно невооруженным глазом. да и вообще, ИМХО, пользоваться профайлером - все равно что не понимать собственную программу)
PPPS а по собственно вопросу - надо асемблер смотреть. чего там компилятор наворотил в обоих случаях)
PPPPS а про общую память и interlocked - по-моему верно. при слишком интенсивном использовании interlocked,  фактически память будет заблокирована одним из процессов. точнее - будет заблокирована вся кэш-линейка L1 на 128 байт, или как ее там... кэш я плохо знаю... выход - разворачивать цикл, благо тут это вполне возможно.
« Последнее редактирование: 26-08-2010 20:41 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #22 : 27-08-2010 11:28 » 

Оф топ.
Размер кеша L1 это килобайты, а не байты
Записан

С уважением Lapulya
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #23 : 27-08-2010 14:00 » 

я говорил про размер кэш-линейки а не всего кеша) если не ошибкаюсь она была 128 байт....
PS или кэш-строка, если угодно...

PPS кстати вот имеет смысл почитать... http://rus-linux.net/lib.php?name=/MyLDP/hard/memory/memory-7-1(6.4).html мельком глянул - вроде по теме.
« Последнее редактирование: 27-08-2010 14:04 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #24 : 29-08-2010 12:09 » 

Много потоков для прироста скорости подобных действий это действительно несуразно. Параллелить нечего абсолютно. В этом случае меет смысл задать более высокий приоритет для потока.
Записан
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #25 : 29-08-2010 15:31 » 

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

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #26 : 29-08-2010 20:14 » 

ну значит я чего-то не так понял. Исходил из этого
Цитата: Sket4
Решил "помочь" процессору, создав поток, выполняющий те же самые операции

Как это еще можно понять, не знаю.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines