Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 11.07.16 13:45
Оценка:
Всем привет.

В настоящий момент я запускаю на выполнение 10000 параллельных задач посредством new Thread(...).Start().
Вначале они дерутся за процессорное время, но вскоре повисают на ожидании чего-либо — блокировок, ответа от сокета, просто в Thread.Sleep

Существуют ли какие-либо рекомендации по оптимизации подобных сценариев? Исходим из того, что реально одновременно выполняться может не больше 128 потоков, остальное время мы вынуждены гонять контекст между ними. Но из-за вышеупомянутых задержек, 10000 потоков с постоянным переключением контекста, работают быстрее, нежели 128, которые большую часть времени жизни спят.
threading многопоточность tpl потоки thread оптимизация
Re: Как оптимизировать выполнения 10000 параллельных задач?
От: b0r3d0m  
Дата: 11.07.16 13:52
Оценка:
LW>Вначале они дерутся за процессорное время, но вскоре повисают на ожидании чего-либо — блокировок, ответа от сокета, просто в Thread.Sleep
Писать неблокирующий код?
Re: Как оптимизировать выполнения 10000 параллельных задач?
От: Sinix  
Дата: 11.07.16 14:40
Оценка: 2 (1) +5
Здравствуйте, LWhisper, Вы писали:

LW>В настоящий момент я запускаю на выполнение 10000 параллельных задач посредством new Thread(...).Start().


Как всегда, нулевое правило: если упёрся в стену — не набивать шишки, а искать момент, в который свернул не туда.
Попробуйте сами найти корень проблемы, вопрос для разогрева — "а зачем мне вообще понадобились 10k потоков с вычислениями?".
Re: Как оптимизировать выполнения 10000 параллельных задач?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.07.16 14:56
Оценка: 1 (1) +4 -1
Здравствуйте, LWhisper, Вы писали:

LW>Существуют ли какие-либо рекомендации по оптимизации подобных сценариев?


Да. Не запускать столько потоков.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re: Как оптимизировать выполнения 10000 параллельных задач?
От: DeathKnight Беларусь  
Дата: 11.07.16 15:08
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>Всем привет.


LW>В настоящий момент я запускаю на выполнение 10000 параллельных задач посредством new Thread(...).Start().

LW>Вначале они дерутся за процессорное время, но вскоре повисают на ожидании чего-либо — блокировок, ответа от сокета, просто в Thread.Sleep

LW>Существуют ли какие-либо рекомендации по оптимизации подобных сценариев? Исходим из того, что реально одновременно выполняться может не больше 128 потоков, остальное время мы вынуждены гонять контекст

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

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

https://en.wikipedia.org/wiki/Work_stealing
Re: Как оптимизировать выполнения 10000 параллельных задач?
От: Shmj Ниоткуда  
Дата: 11.07.16 15:34
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>Существуют ли какие-либо рекомендации по оптимизации подобных сценариев? Исходим из того, что реально одновременно выполняться может не больше 128 потоков, остальное время мы вынуждены гонять контекст между ними. Но из-за вышеупомянутых задержек, 10000 потоков с постоянным переключением контекста, работают быстрее, нежели 128, которые большую часть времени жизни спят.


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

Во-вторых нужно смотреть где стопорится. Если это дедлок -- то проблема разработчика. Еще есть такая штука как лимит подключений по умолчанию -- он равен 2 одновременным подключениям (емнип). Исправляется одной строчкой в конфиг-файле. Кроме того, если вы обращаетесь к одному серверу -- то у него могут быть свои лимиты на 1 IP-адрес -- исправляется запросами через PROXY-сервера.
Re[2]: Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 11.07.16 15:47
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Во-вторых нужно смотреть где стопорится. Если это дедлок -- то проблема разработчика. Еще есть такая штука как лимит подключений по умолчанию -- он равен 2 одновременным подключениям (емнип). Исправляется одной строчкой в конфиг-файле. Кроме того, если вы обращаетесь к одному серверу -- то у него могут быть свои лимиты на 1 IP-адрес -- исправляется запросами через PROXY-сервера.


ThreadPool просто выжрет все доступные потоки и поставит остальные в очередь.
Нет проблемы с дедлоками. Есть проблема с тем, что крутится 10000 параллельно работающих потоков.
Re[2]: Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 11.07.16 15:51
Оценка:
Здравствуйте, DeathKnight, Вы писали:

DK>Использовать пул потоков, который будет пережевывать поступающие задачи. Задача может представлять собой некий объект с состоянием и от него плясать — идти на выполнение или ждать события.


DK>https://en.wikipedia.org/wiki/Work_stealing


Очевидное и очень хорошее решение. Проблема в том, что под него нужно перепроектировать всё приложение. Там где это можно было сделать без жертв, уже свернул 5000 потоков в 2.
Остались очень тяжеловесные места. Хочется обойтись минимальным количеством переделок.
Для того чтобы переписать все с поддержкой CancelableToken (ведь задача может зависнуть и тогда встанет вся очередь) понадобятся сотни человекочасов.
Re[2]: Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 11.07.16 15:55
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, LWhisper, Вы писали:


LW>>В настоящий момент я запускаю на выполнение 10000 параллельных задач посредством new Thread(...).Start().


S>Как всегда, нулевое правило: если упёрся в стену — не набивать шишки, а искать момент, в который свернул не туда.

S>Попробуйте сами найти корень проблемы, вопрос для разогрева — "а зачем мне вообще понадобились 10k потоков с вычислениями?".
Корень проблемы давно известен. Ошибка допущена 4 года назад. Легко и просто она не исправляется.
Просто перейти на очередь задач сейчас не получится, так как задачи имеют обыкновение виснуть в неожиданный момент и тогда встрянет весь пул задач.
Поэтому сейчас ищется волшебная кнопка "сделать хорошо". То, что можно переписать без больших рисков, я переписываю. К сожалению, это капля в море. Просто скейлиться начала та часть продукта, о которой 4 года назад никто не думал и считал её если и многопоточной, то в пределах 3-4 параллельных задач, но никак не 10000. Классика жанра, но с этим нужно что-то делать.
Re[2]: Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 11.07.16 15:56
Оценка:
Здравствуйте, b0r3d0m, Вы писали:

LW>>Вначале они дерутся за процессорное время, но вскоре повисают на ожидании чего-либо — блокировок, ответа от сокета, просто в Thread.Sleep

B>Писать неблокирующий код?
Поздно.
Re[3]: Как оптимизировать выполнения 10000 параллельных задач?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.07.16 16:24
Оценка: +1
Здравствуйте, LWhisper, Вы писали:

LW>Для того чтобы переписать все с поддержкой CancelableToken (ведь задача может зависнуть и тогда встанет вся очередь)


Встанет вся очередь только если все 10000 задач линейно зависят друг от друга. Но тогда вообще непонятно зачем тебе потоки.
Кроме того, таймаут в TPL никто не отменял.
Мне вот интересно другое — а сейчас ты чего с зависшей задачей делаешь? Abort зовешь?
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: Как оптимизировать выполнения 10000 параллельных задач?
От: Sinix  
Дата: 11.07.16 16:28
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>Корень проблемы давно известен. Ошибка допущена 4 года назад. Легко и просто она не исправляется.


А, ну это классика. Вам надо в любом случае перевести код на Task<T> и использовать cps (await) вместо блокирующих ожиданий, иначе вы так и будете замусоривать общий пул "зависшими" задачами.

Дальше проще. В принципе, можно даже попытать счастья с стандартным пулом, запуская проблемные места как LongRunning task. Не хватает — пишем свой scheduler c локальными очередями, общей очередью и отдельным подпулом для "долгих" задач. Прерывание не используете вообще, как я понимаю, поэтому по таймеру придётся переводить "зависшие" потоки из общего пула в пул для долгих задач (и наоборот).



Разумеется, все места, которые в явном виде зависят от Thread/TLS/используют блокировки придётся переписать. Если этого не сделать, то так и будете наступать себе на шнурки. Даже 100 активных потоков на одно ядро — это много.
Re[4]: Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 11.07.16 17:37
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Встанет вся очередь только если все 10000 задач линейно зависят друг от друга. Но тогда вообще непонятно зачем тебе потоки.

AVK>Кроме того, таймаут в TPL никто не отменял.
AVK>Мне вот интересно другое — а сейчас ты чего с зависшей задачей делаешь? Abort зовешь?

Ты как-то противоречишь сам себе. Если мы переходим на очередь из которой разгребаются задачи, то в ней не может быть долгих таймаутов. В противном случае мы ждём у моря погоды вместо того чтобы выполнять другие задачи. Сейчас я могу себе позволить подождать сутки, а после срубить поток через Thread.Abort. С пулом такой фокус не прокатит. Придётся прошерстить весь код и любое действие, которое может подвиснуть отбрасывать в очередь отдельной таской с коллбэком. Фактически, переписать всё решение. Нереально.
Re[4]: Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 11.07.16 17:43
Оценка:
Здравствуйте, Sinix, Вы писали:

S>А, ну это классика. Вам надо в любом случае перевести код на Task<T> и использовать cps (await) вместо блокирующих ожиданий, иначе вы так и будете замусоривать общий пул "зависшими" задачами.

Классика, кто спорит.
С Task<T> всё очень грустно. Было вкручено в одном единственном месте. Выстрелило моментально — 200 задач в ожидании запуска. В Mono LongRunning-таски запускаются просто в отдельном Thread'е — получаем нашу ситуацию. В C# какая-то хитрая логика в зависимости от шедуллера. С дефолтным не прокатило.
До awiat'ов ещё не доросли. Поэтому и используем Thread'ы вместо Task'ок из пула. Но, как видно, имеем палку о двух концах.

S>Дальше проще. В принципе, можно даже попытать счастья с стандартным пулом, запуская проблемные места как LongRunning task. Не хватает — пишем свой scheduler c локальными очередями, общей очередью и отдельным подпулом для "долгих" задач. Прерывание не используете вообще, как я понимаю, поэтому по таймеру придётся переводить "зависшие" потоки из общего пула в пул для долгих задач (и наоборот).

Увы, со стандартным пулом всё тухло. Насчёт LRT выше.
Можно подробнее про свой шедуллер? Есть полезные статьи на примете?

S>Разумеется, все места, которые в явном виде зависят от Thread/TLS/используют блокировки придётся переписать. Если этого не сделать, то так и будете наступать себе на шнурки. Даже 100 активных потоков на одно ядро — это много.

Очень медленно и печальном этим занимаемся.
Re[2]: Как оптимизировать выполнения 10000 параллельных задач?
От: Sharov Россия  
Дата: 11.07.16 17:58
Оценка: +2
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, LWhisper, Вы писали:


LW>>Существуют ли какие-либо рекомендации по оптимизации подобных сценариев? Исходим из того, что реально одновременно выполняться может не больше 128 потоков, остальное время мы вынуждены гонять контекст между ними. Но из-за вышеупомянутых задержек, 10000 потоков с постоянным переключением контекста, работают быстрее, нежели 128, которые большую часть времени жизни спят.


S>Во-первых есть ThreadPool, который по идее изначально подстраивается под систему. Обычно юзают его. Но все таки лучше вручную проверить.


А вот не надо так делать: ThreadPool могут использовать и другие библиотеки(хоть сам фреймворк) под свои нужды. И если все потоки у него отожрать, да еще и заблокировать их -- будет не очень здорово.
Кодом людям нужно помогать!
Re[5]: Как оптимизировать выполнения 10000 параллельных задач?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.07.16 17:59
Оценка:
Здравствуйте, LWhisper, Вы писали:

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


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

LW> В противном случае мы ждём у моря погоды вместо того чтобы выполнять другие задачи.


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

LW> Сейчас я могу себе позволить подождать сутки, а после срубить поток через Thread.Abort.


Не можешь. Нельзя Thread.Abort использовать от слова вообще.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[5]: Как оптимизировать выполнения 10000 параллельных задач?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.07.16 18:01
Оценка: +2
Здравствуйте, LWhisper, Вы писали:

LW>В Mono LongRunning-таски запускаются просто в отдельном Thread'е


В дотнете тоже.

LW> — получаем нашу ситуацию.


Вашу это какую? Пул в современном дотнете совсем не фиксирован и может расти до довольно больших величин.

LW>В C# какая-то хитрая логика в зависимости от шедуллера.


В случае с LR нет там ничего особо хитрого. Исходники есть — можешь посмотреть, там немного.
И при чем здесь C#?
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[6]: Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 11.07.16 18:56
Оценка:
AVK>Очередь это слишком примитивная модель того, что происходит внутри TPL. Стандартный алгоритм и сам умеет в какой то мере бороться с долгоиграющим кодом, и возможностей по настройке там масса.
В какой-то мере умеет. Этого недостаточно. Примеры?

AVK>Тебе вроде уже несколько человек написали, что твоя теория не соответствует действительности.

Вроде, нет.

AVK>Не можешь. Нельзя Thread.Abort использовать от слова вообще.

Вполне себе могу. Код корректно обрабатывает ThreadAbortException и использует CER согласно правилам, там где это необходимо.
Re[6]: Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 11.07.16 19:01
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>В дотнете тоже.

Да, верно. Итого 10000 LRT ничем не отличаются от 10000 тредов.
if ((task.Options & TaskCreationOptions.LongRunning) != TaskCreationOptions.None)
{
Thread thread = new Thread(ThreadPoolTaskScheduler.s_longRunningThreadWork);
thread.IsBackground = true;
Task task1 = task;
thread.Start((object) task1);
}

AVK>Вашу это какую? Пул в современном дотнете совсем не фиксирован и может расти до довольно больших величин.

С 10000 потоками.

AVK>В случае с LR нет там ничего особо хитрого. Исходники есть — можешь посмотреть, там немного.

AVK>И при чем здесь C#?
.NET имел ввиду. С LR нет, но, как мы уже выяснили, разницы между LRT и тредами нет.
Re[7]: Как оптимизировать выполнения 10000 параллельных задач?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.07.16 19:04
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>Да, верно. Итого 10000 LRT ничем не отличаются от 10000 тредов.


А зачем все 10К делать LR?

LW>.NET имел ввиду. С LR нет, но, как мы уже выяснили, разницы между LRT и тредами нет.


Разница есть, потому что остаются еще зависимости.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.