Russian Qt Forum
Ноябрь 23, 2024, 06:18 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3 4 5   Вниз
  Печать  
Автор Тема: Нагрузка на поток  (Прочитано 35292 раз)
kramer3d
Гость
« Ответ #15 : Февраль 24, 2010, 19:21 »

Красиво но сложно  Улыбающийся Почему не простенько: задачи принимаются и складываются в одну общую очередь. Каждая нитка берет следующую задачу из этой очереди как только закончит предыдущую. Число ниток просто фиксировано. Вероятно задачи могут быть зависимыми, напр задача "B" может требовать результатов задачи "A". Мне кажется не стоит решать это созданием новых ниток (напр. нитка не может принять "C" потому что она ждет "B", давайте создадим еще нитку). Лучше сохранять результаты/контекст а потом восстанавливать.
Проблема в том, что задача асинхронная. Т.е. объекты в нитке управляются событиями, приходящими извне. Можно, конечно, выделить очередь событий в отдельную нитку, с которой рабочие потоки будут разбирать задания строго по одному (тут появляется оверхед с сериализацией доступа: получить мьютекс - это довольно дорогая операция в контексте мелких задач на полста умножений). Но в этом случае опять же встает проблема масштабируемости для потока, управляющего очередью заданий - что делать, если очередь событий в нем забилась окончательно, и задание в разы дольше дожидается "постановки на учет", чем собственно обрабатывается? Понятно, надо создать еще одну нитку с параллельной очередью заданий, и добавлять задания в ту, в которой их меньше. Вопрос в том, как узнать, что очередь событий потока заполнилась, и добавлять туда новые неэффективно? Понятно, я ищу сферического коня в вакууме, этакую идеальную парадигму масштабирования, но ведь Генри Форд нам завещал всегда желать большего. Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #16 : Февраль 24, 2010, 20:16 »

Проблема в том, что задача асинхронная. Т.е. объекты в нитке управляются событиями, приходящими извне. Можно, конечно, выделить очередь событий в отдельную нитку, с которой рабочие потоки будут разбирать задания строго по одному (тут появляется оверхед с сериализацией доступа: получить мьютекс - это довольно дорогая операция в контексте мелких задач на полста умножений). Но в этом случае опять же встает проблема масштабируемости для потока, управляющего очередью заданий - что делать, если очередь событий в нем забилась окончательно, и задание в разы дольше дожидается "постановки на учет", чем собственно обрабатывается? Понятно, надо создать еще одну нитку с параллельной очередью заданий, и добавлять задания в ту, в которой их меньше. Вопрос в том, как узнать, что очередь событий потока заполнилась, и добавлять туда новые неэффективно? Понятно, я ищу сферического коня в вакууме, этакую идеальную парадигму масштабирования, но ведь Генри Форд нам завещал всегда желать большего. Улыбающийся
Про Форда ничего не слышал, но Ваш поиск коня мне нравится  Улыбающийся Я свое мнение не навязываю. но по-моему Вы преувеличиваете проблему, вероятно ее здесь вообще нет. Для простоты положим что на сервере аж один (!) процессор - а запросы поступают интенсивно. Что толку что Вы (допустим) распихали все по ниткам? Один процессор все равно будет тянуть как он сможет, больше задач - больше накладных расходов на синхронизацию. Разумно играть на приоритетах запросов/событий, напр. "сейчас мало задач в обработке, приемщик новых запросов имеет имеет высший приоритет". И наоборот "работы валом, пусть следующие запросы ждут" - это неизбежная ситуация когда "сервер перегружен запросами" - и стесняться этого нечего. Динамическое создание новых ниток эту проблему не решит.

BTW: "kramer3d" - это просто так или действительно "3d"?
Записан
kramer3d
Гость
« Ответ #17 : Февраль 25, 2010, 05:51 »

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

А 3D - это не просто так, я действительно 3D, т.е. кроме прочего занимаюсь разработкой прикладного ПО для нужд трехмерной графики. Улыбающийся
Записан
ритт
Гость
« Ответ #18 : Февраль 25, 2010, 08:20 »

Я свое мнение не навязываю. но по-моему Вы преувеличиваете проблему, вероятно ее здесь вообще нет.
ты очень неправ. как уже было замечено выше, простая "очередь задач" даже при небольшой нагрузке уткнётся в значительные накладные расходы на синхронизацию рабочих потоков. и чем больше будет потоков, обращающихся к этой очереди задач, тем больше будет мат.ожидаемое время простоя каждого из них - это нормально для смешных серверов типа "тридедфортунесервер", но недопустимо для серьёзных серверов с задачами масштабирования/распределения нагрузки.
и  kramer3d абсолютно прав в том, что пытается заранее предусмотреть возможности масштабирования, которые не потребуют модификаций кода.
эдаких "сейчас же всё работает? а прижмёт - подумаем" я уже насмотрелся - когда "прижмёт", он сначала будет возмущаться что это, мол, не его вина, потом начнёт переписывать каждое узкое место заново, снова тратить на это время и снова получать зарплату...
Записан
BRE
Гость
« Ответ #19 : Февраль 25, 2010, 08:46 »

Спасибо! Вопрос безотносительно серверной архитектуры пока так и остается открытым. Т.е. как быть, если есть поток, производящий вычисления исходя из поступающих извне данных? Как этому потоку понять, что он загружен по самое не могу и больше данных принимать не может?
Факт занятости потока по самое не могу это все таки величина относительная от загрузки других потоков.
Я бы немного изменил твой вариант, примерно так:
Если QTimer запустить с интервалом равным 0, то такой таймер будет срабатывать на каждой итерации цикла обработки событий. В его обработчике можно смотреть сколько времени прошло с момента предыдущего входа в этот слот. И зная сколько времени выполняется цикл для каждого потока, принимать решение о загруженности.
Записан
ритт
Гость
« Ответ #20 : Февраль 25, 2010, 08:56 »

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

итак, идея следующая: запросы (в вашей терминологии "задачи"?) складываются в простую очередь (QQueue) в порядке их получения (полагаю, это довольно честно, если учесть, что обычно клиенты не стараются заддосить сервер, а это - отдельная тема), а каждый рабочий поток сигнализирует о завершении обработки вверенного ему запроса - кто раньше отметился, что свободен, тот и получает на обработку следующий запрос из очереди (первый, если учесть, что у нас схема FIFO), если таковой имеется. при получении запроса от клиента создаём рабочие потоки по принципу стандартного пула (нужен новый и общее кол-во меньше максимального? - создаём; иначе выходим и первый освободившийся поток в дальнейшем получит данный запрос на обработку).
мысли/комментарии?
Записан
kramer3d
Гость
« Ответ #21 : Февраль 25, 2010, 09:12 »

Факт занятости потока по самое не могу это все таки величина относительная от загрузки других потоков.
Я бы немного изменил твой вариант, примерно так:
Если QTimer запустить с интервалом равным 0, то такой таймер будет срабатывать на каждой итерации цикла обработки событий. В его обработчике можно смотреть сколько времени прошло с момента предыдущего входа в этот слот. И зная сколько времени выполняется цикл для каждого потока, принимать решение о загруженности.
Спасибо за идею. Улыбающийся По-моему, это то же самое, что и было, но с одним минусом - такой подход создаст немаленький оверхед для пустых потоков или потоков с очень низкой активностью. А поскольку афинностью потоков я напрямую не управляю - этим занимается ОС, может оказаться, что два потока будут крутиться на одном ядре, и более слабо нагруженный будет отжирать ресурс у более сильно нагруженного просто на обработку событий таймера.
В моем случае объект таймера - это член класса потока (наследника QThread), т.е. он живет в родительском потоке и не создает дополнительной нагрузки на рабочий поток. Он всего лишь постит сообщения некоему объекту-обработчику, ссылка на который также хранится в классе, но который был создан динамически в теле run(), т.е. живет в рабочем потоке. Обработчик предельно легкий - померял время, фильтранул по Гауссу, например, записал куда надо и вышел.
 
Записан
kramer3d
Гость
« Ответ #22 : Февраль 25, 2010, 09:44 »

если я правильно понял описание, главный поток сервера - самый ненагруженный и можно его озадачить ещё чем-то полезным...опять же, если правильно понял, он также занимается определением того, какому рабочему потоку следует  отдать задачу, основываясь на некоторой циферке, указывающей на загруженность потока (кстати, описанный ранее принцип определения рабочей нагрузки меня настораживает - если интервал опроса недостаточно маленький, поток, только что завершивший "тяжёлую" задачу и теперь простаивающий, мог ещё не успеть ответить на пинг - как будто он всё ещё нагружен, задача достанется другому (более отзывчивому) потоку и будет в очереди на исполнение ещё некоторое время, тогда как в действительности имеем один простаивающий поток; если интервал опроса минимален (или 0), на очередь событий потока, в котором крутится таймер, постоянно будет создаваться дополнительная нагрузка).
Да, согласен, такая проблема имеет место быть. Возможно, пересматривать время простоя в очереди нужно не по таймеру через равные интервалы времени, а по каким-то событиям, связанным с выполнением задачи. Т.е. ответственность за измерение нагрузки на поток можно возложить не на сам поток, а на объекты, которые в нем живут. Т.е. закончил тяжелую операцию - будь добр, запости событие в очередь, что нужно обновить данные о нагрузке. Тогда эти данные будут обновляться на каждой следующей итерации цикла событий. Т.е. закончилось исполнение, объект поставил сообщение об этом в очередь, обработчик сообщений запостил событие с таймстемпом, и уже на следующей итерации цикла событий информация о нагрузке обновилась. Это, по-моему, вполне приемлемо, с учетом того, что свежедобавленный объект будет обработан не в текущей итерации, а только в следующей, если я правильно понимаю логику работы Event Loop.
итак, идея следующая: запросы (в вашей терминологии "задачи"?) складываются в простую очередь (QQueue) в порядке их получения (полагаю, это довольно честно, если учесть, что обычно клиенты не стараются заддосить сервер, а это - отдельная тема), а каждый рабочий поток сигнализирует о завершении обработки вверенного ему запроса - кто раньше отметился, что свободен, тот и получает на обработку следующий запрос из очереди (первый, если учесть, что у нас схема FIFO), если таковой имеется. при получении запроса от клиента создаём рабочие потоки по принципу стандартного пула (нужен новый и общее кол-во меньше максимального? - создаём; иначе выходим и первый освободившийся поток в дальнейшем получит данный запрос на обработку).
мысли/комментарии?
Это, в принципе, уже было предложено Igors. см. мой ответ http://www.prog.org.ru/index.php?topic=12552.msg79842#msg79842
Но это так, чисто академически. А вообще, основная засада тут в том, что рабочие потоки - асинхронны, т.е. принимают не задачи в виде данных, которые можно обработать и забыть, а в виде QObject-ов, которые крутятся в их цикле событий. Причем нагрузка от этих объектов принципиально непредсказуема - у одного может быть единственный обработчик сообщений, а у другого - полста сигналов и слотов на QueuedConnection. Поэтому поток не может принимать решение о том, что пора взять еще один объект на обработку, основываясь на количестве обслуживаемых объектов. Вот я и пытаюсь выделить критерий, чтобы понять, насколько загружен Event Loop потока. В любом случае, большое спасибо за интерес, мысли и поддержку, так или иначе мне это очень помогает. Улыбающийся
Записан
BRE
Гость
« Ответ #23 : Февраль 25, 2010, 09:58 »

Спасибо за идею. Улыбающийся По-моему, это то же самое, что и было, но с одним минусом - такой подход создаст немаленький оверхед для пустых потоков или потоков с очень низкой активностью. А поскольку афинностью потоков я напрямую не управляю - этим занимается ОС, может оказаться, что два потока будут крутиться на одном ядре, и более слабо нагруженный будет отжирать ресурс у более сильно нагруженного просто на обработку событий таймера.
IMHO, оверхед несомненно будет, но итерация цикла обработки событий операция тоже очень не дешевая, даже если поток простаивает. На его фоне прямой вызов одного слота, в котором, грубо говоря, выполняется операция вычитание, сильно ресурсы не отожрет. Это на фоне самой итерации цикла.

Записан
kramer3d
Гость
« Ответ #24 : Февраль 25, 2010, 10:06 »

IMHO, оверхед несомненно будет, но итерация цикла обработки событий операция тоже очень не дешевая, даже если поток простаивает. На его фоне прямой вызов одного слота, в котором, грубо говоря, выполняется операция вычитание, сильно ресурсы не отожрет. Это на фоне самой итерации цикла.
Трудно сказать вот так, навскидку. Но ведь можно потестить. Улыбающийся Сейчас займусь. Надо бы профайлер поискать.
Записан
BRE
Гость
« Ответ #25 : Февраль 25, 2010, 10:13 »

Трудно сказать вот так, навскидку. Но ведь можно потестить. Улыбающийся Сейчас займусь. Надо бы профайлер поискать.
Для интереса, посмотри в исходниках, сколько разных действий выполняется при одной итерации цикла. А ведь он так крутиться постоянно...  Улыбающийся
Записан
BRE
Гость
« Ответ #26 : Февраль 25, 2010, 10:26 »

Еще мысли...  Улыбающийся
Для организации рабочих потоков я бы использовал QThreadPool/QRunnable или написал бы аналогичный механизм, при котором простаивающие нити запирались бы на мьютексе, и не занимали бы ресурсы даже на кручение eventloop.
Записан
kramer3d
Гость
« Ответ #27 : Февраль 25, 2010, 10:39 »

Еще мысли...  Улыбающийся
Для организации рабочих потоков я бы использовал QThreadPool/QRunnable или написал бы аналогичный механизм, при котором простаивающие нити запирались бы на мьютексе, и не занимали бы ресурсы даже на кручение eventloop.
А тут опять та же проблема - QRunnable не подразумевает потоков с event loop'ом, т.е. такую функциональность мне придется реализовывать самому, и опять же из тред-пула нужно будет определять, насколько тот или иной event loop загружен, чтобы понять, можно ли туда добавлять объекты.
Я вот думаю основательно разобраться с QtConcurrent, но что-то мне подсказывает, что и он мне не поможет.
Записан
BRE
Гость
« Ответ #28 : Февраль 25, 2010, 10:42 »

А тут опять та же проблема - QRunnable не подразумевает потоков с event loop'ом, т.е. такую функциональность мне придется реализовывать самому, и опять же из тред-пула нужно будет определять, насколько тот или иной event loop загружен, чтобы понять, можно ли туда добавлять объекты.
Под рабочим потоком подразумевается задача, которая получает входные данные, занимается их обработкой никого не трогая и при завершении предоставляет результат. Для подобных задач eventloop не нужен.
Записан
kramer3d
Гость
« Ответ #29 : Февраль 25, 2010, 10:50 »

Под рабочим потоком подразумевается задача, которая получает входные данные, занимается их обработкой никого не трогая и при завершении предоставляет результат. Для подобных задач eventloop не нужен.
Для рабочих потоков без event loop нечего и огород городить - очередь заданий и вперед. Тут уже подсказывали.
Меня же интересует рабочий поток (ну, назовем его обслуживающий тогда, чтоб не было путаницы) с event loop'ом, который обслуживает QObject-ы. Ведь объектов может быть много и наверняка эффективнее обрабатывать их сигналы-слоты и события многопоточно. В целом ведь Qt это позволяет, но вот с нагрузкой на event loop - засада.
Записан
Страниц: 1 [2] 3 4 5   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.06 секунд. Запросов: 23.