Процессы производительности Java против потоков
Я реализую рабочий пул в Java.
Это, по сути, целая нагрузка объектов, которые собирают куски данных, обрабатывают данные и затем сохраняют результат. Из-за латентности IO будет значительно больше рабочих, чем процессорные ядра.
Сервер предназначен для этой задачи, и я хочу выжать максимальную производительность из оборудования (но нет, я не хочу его реализовывать на С++).
Простейшей реализацией может быть один процесс Java, который создает и контролирует ряд рабочих потоков. Альтернативой может быть запуск Java-процесса для каждого работника.
Предполагая для аргументов ради четырехъядерного Linux-сервера, какие из этих решений вы ожидали бы быть более результативными и почему?
Вы можете предположить, что работникам никогда не нужно общаться друг с другом.
Ответы
Ответ 1
Один процесс, несколько потоков - по нескольким причинам.
При переключении контекста между заданиями дешевле на некоторых процессорах переключаться между потоками, чем между процессами. Это особенно важно для такого типа операций ввода-вывода с большим числом рабочих, чем ядра. Чем больше работы вы делаете между блокировкой ввода-вывода, тем менее важно это. Однако хорошая буферизация будет оплачивать потоки или процессы.
При переключении между потоками в одной JVM, по крайней мере, некоторые версии Linux (в частности, x86) не нуждаются в очистке кеша. Смотрите блог Tsuna. Кэширование загрязнений между потоками будет минимизировано, поскольку они могут совместно использовать кэш программы, выполняют одну и ту же задачу и используют одну и ту же копию кода. Мы говорим о сбережениях порядка 100 наносекунд до нескольких микросекунд за коммутатор. Если этот маленький картофель для вас, тогда читайте дальше...
В зависимости от дизайна путь данных ввода-вывода может быть короче для одного процесса.
Время запуска и прогрева для потока обычно намного короче. ОС не нужно запускать процесс, Java не должен запускать другую JVM, загрузка классов выполняется только один раз, JIT-компиляция выполняется только один раз, а оптимизация HotSpot выполняется один раз и раньше.
Ответ 2
Как правило, при обсуждении многопроцессорной обработки (/w по одному потоку на процесс) по сравнению с несколькими потоками в одном и том же процессе, в то время как теоретические накладные расходы в первом случае больше, чем в последнем (и, следовательно, многопроцессорная обработка теоретически медленнее, чем multi threading), в действительности на большинстве современных ОС это не такая большая проблема. Однако, обсуждая это в контексте Java, запуск нового процесса намного дороже, чем запуск нового потока. Запуск нового процесса означает запуск нового экземпляра JVM, который является очень дорогостоящим, особенно с точки зрения памяти. Я рекомендую вам запускать несколько потоков в одной JVM.
Кроме того, если вы говорите, что межпоточная связь не является проблемой, вы можете использовать Java Executor Service, чтобы получить фиксированный пул потоков размером 2x (количество доступных ЦП). Количество доступных CPU может быть автоматически определено во время выполнения через Java Runtime. Таким образом, вы получаете быструю простую многопоточность без кода котельной.
Ответ 3
На самом деле, если вы делаете это с помощью больших таков, используя несколько jvm-процессов, это быстрее, чем один jvm с многопоточными потоками. По крайней мере, у нас никогда не было одного jvm runnning с быстрым мультиплеевым jvms.
Мы выполняем некоторые вычисления, где каждая задача использует около 2-3 ГБ оперативной памяти и делает хруст тяжелого числа. Если мы создадим 30 jvm и выполним 30 заданий, они будут выполняться на 15-20% лучше, чем порождать 30 потоков в одном jvm. Мы попытались настроить gc и различные разделы памяти и никогда не подходили к первому варианту.
Мы выполнили это на разных машинах 14 задач на 16-ядерном сервере, 34 задания на 36-ядерном сервере и т.д. Многопоточность в Java всегда выполняла worde, чем несколько процессов jvm.
Это может не повлиять на простые задачи, но при тяжелых вычислениях кажется, что jvm плохо работает на потоках.