Страницы

Поиск по вопросам

четверг, 19 декабря 2019 г.

Sleep'ы в C# делают код быстрее

#c_sharp #многопоточность


Всем добра!
Наткнулся на одну очень интересную фичу.
Немного предыстории, без которой никак:
Пишу на C# дипломную программу -- многопоточный эмулятор систем массового обслуживания.
Суть программы: задаём все параметры системы и количество потоков (Thread'ов). Во
всех потоках система одинаковая, и она моделируется до бесконечности (пока пользователь
не остановит).
Сделал -- всё отлично, но возникла проблема: операционная система (win7 x64) не может
их нормально синхронизировать. При большом числе потоков (большим, чем число ядер)
некоторые потоки по модельному времени очень сильно обгоняют\отстают от других (причём
изначально "тормознутый" поток со временем будет отставать от большинства всё сильнее,
а "быстрый" -- убегать всё дальше). А нужно, чтобы время было везде примерно одинаковым
-- для наглядности.
Не беда! Написал метод, который притормаживает самые быстрые потоки (считается среднее
модельное время по всем потокам -- и притормаживаются те потоки, которые на CONST это
среднее время опережают). Синхронизация заработала, всё хорошо.
А потом началось самое интересное:
Я решил снизить нагрузку на процессор, и в каждом потоке поставил SLEEP(1) [поспать
1 миллисекунду] раз в 100 итераций -- и сделал эту фичу устанавливаемой пользователем
(CheckBox на форме, включающий или выключающий эти SLEEP'ы).
Казалось бы, очевидно, что наличие SLEEP'ов в коде должно снижать скорость его выполнения.
И при малом числе потоков (сравнимым с числом ядер, которых в моём i5 4) это действительно
так. Но при увеличении числа потоков в 2-3 раза (до ~10) разница пропадает. А при дальнейшем
увеличении числа потоков (20, 50, 100) программа начинает работать в разы быстрее,
причём на загрузке процессора это никак не отражается. Наличие SLEEP'ов в коде при
большом числе потоков делает код гораздо быстрее!
Тестировал на ноутбуке (win8\x64\2 ядра) -- результат тот же.
Неужели Win7\Win8 настолько плохо синхронизирует потоки, что за счёт периодических
SLEEP'ов в коде они синхронизируются лучше, и программа в итоге выполняется быстрее?
Потоки-то все равноправные, ОС должна предоставлять всем равные кванты времени CPU.
В коде никаких ошибок нет, копаюсь в этом уже который месяц -- и безрезультатно.
Раньше я думал, что всё дело в синхронизации -- но теперь я отключил метод для синхронизации
потоков между собой -- и результат получился абсолютно таким же: при наличии SLEEP'ов
и большого числа потоков код всё равно работает в 2-5 раз быстрее!
И теперь я уже вообще ничего не понимаю. Может, вы знаете, в чём тут дело?
P.S. да, я чайник, если что.
Всем спасибо!    


Ответы

Ответ 1



sleep(1) - это особый вид sleep. Он говорит планировщику потоков - поток, который вызвал его, свой квант времени отработал. Дело в том, что для обеспечения мультизадачности, каждый поток получает свой квант времени на работу (обычно от 50мс на декстопе до 130 на серверных системах). Если потоки отрабатывают цикл за 10 мс, а потом ждут данные или делают активную синхронизацию (while (get_var(xxx)) {/*do nothing*/}, где get_var просто получает переменную, используя синхронизацию), то они конечно будут отставать по отношению к тем, которые более полно выбирают свой квант. Случайным образом расставленные sleep(1) работу не ускорят, а замедлят (из-за накладных расходов на переключение контекста - а оно не дешевое). Делайте выводы.

Комментариев нет:

Отправить комментарий