Отвечая на ваш вопрос: Default
диспетчер лучше всего подходит для задач, в которых нет функции блокировки, потому что нет выигрыша от превышения максимального параллелизма при одновременном выполнении таких рабочих нагрузок ( the-difference-between-concurrent-and-parallel- выполнение ).
https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/5_CPU_Scheduling.html
Ваш эксперимент ошибочен. Как уже упоминалось в комментариях, ваш blockingWork
не привязан к ЦП, а к вводу-выводу. Все дело в ожидании - периодах, когда ваша задача заблокирована, и ЦП не может выполнять свои последующие инструкции. Ваш blockingWork
, по сути, просто "подождите 1000 миллисекунд", и ожидание 1000 мс X раз параллельно будет быстрее, чем выполнение этого в последовательности. Вы выполняете некоторые вычисления (генерируете случайное число, которое, по сути, также может быть связано с вводом-выводом), но, как уже отмечалось, ваши рабочие генерируют большее или меньшее количество этих чисел, в зависимости от того, сколько времени базовые потоки были переведены в спящий режим.
Я провел несколько простых экспериментов с генерацией чисел Фибоначчи (часто используемых для моделирования рабочих нагрузок процессора). Однако после учета JIT в JVM я не мог легко получить какие-либо результаты, доказывающие, что диспетчер Default
работает лучше. Возможно, переключение контекста не так важно, как можно подумать. Возможно, диспетчер не создавал больше потоков с диспетчером ввода-вывода для моей рабочей нагрузки. Возможно, мой эксперимент тоже был ошибочным. Не могу быть уверенным - тестирование производительности на JVM само по себе непросто, и добавление сопрограмм (и их пулов потоков) в микс, конечно, не делает его проще.
Однако я думаю, что здесь есть кое-что более важное, а именно блокировка . Default
диспетчер более чувствителен к блокировке звонков. При меньшем количестве потоков в пуле более вероятно, что все они будут заблокированы и никакая другая сопрограмма не сможет выполняться в это время.
Ваша программа работает в потоках. Если все потоки заблокированы, ваша программа ничего не делает. Создание новых потоков обходится дорого (в основном с точки зрения памяти), поэтому для высоконагруженных систем блокировка функций становится ограничивающим фактором. Kotlin проделал потрясающую работу по введению функций "приостановки". Параллелизм вашей программы больше не ограничивается количеством имеющихся у вас потоков. Если одному потоку нужно подождать, он просто приостанавливается, а не блокирует поток. Однако «мир не идеален», и не все «приостанавливается» - все еще остаются «блокирующие» вызовы - насколько вы уверены, что используемая no библиотека выполняет такие вызовы под капотом? С большой властью приходит большая ответственность. При использовании сопрограмм нужно быть еще более осторожным в отношении взаимоблокировок, особенно при использовании диспетчера Default
. На самом деле, на мой взгляд, диспетчер IO
должен быть диспетчером по умолчанию.