Я працюю над додатком Java для вирішення класу задач з числовою оптимізацією - більш точні проблеми лінійного програмування. Одну проблему можна розділити на менші підпрограми, які можна вирішити паралельно. Оскільки є більше підпроблем, ніж ядер CPU, я використовую ExecutorService і визначаю кожну підпроблему як Callable, який надсилається до ExecutorService. Розв’язання підпрограми вимагає виклику рідної бібліотеки - лінійки, що вирішує програмування, у цьому випадку.
Проблема
Я можу запускати додаток в Unix та в системах Windows з об'ємом до 44 фізичних ядер і до 256 г пам'яті, але час обчислень у Windows на порядок вище, ніж у Linux для великих проблем. Windows не тільки вимагає значно більше пам’яті, але використання процесора з часом падає з 25% на початку до 5% через кілька годин. Ось скріншот менеджера завдань у Windows:
Спостереження
- Час вирішення великих випадків загальної проблеми становить від години до дня і вимагає до 32 г пам'яті (на Unix). Час рішення підпроблеми знаходиться в діапазоні мс.
- Я не стикаюся з цим питанням щодо невеликих проблем, на вирішення яких потрібно лише кілька хвилин.
- Linux використовує обидва сокети поза коробкою, тоді як Windows вимагає від мене явно активувати переплетення пам'яті в BIOS, щоб програма використовувала обидва ядра. Не робити це я не впливає на погіршення загального використання процесора з часом.
- Коли я дивлюсь на потоки в VisualVM, всі потоки пулу запущені, жодна не очікує або ще.
- Згідно з VisualVM, 90% часу процесора витрачається на виклик нативної функції (вирішення невеликої лінійної програми)
- Збір сміття не є проблемою, оскільки додаток не створює та не відсилає посилання на багато об’єктів. Крім того, більшість пам’яті, здається, виділяється поза грою. 4 г купи достатньо для Linux і 8 г для Windows для найбільшого екземпляра.
Що я спробував
- всілякі аргументи JVM, високий XMS, високий метапростір, прапор UseNUMA, інші GC.
- різні JVM (Точка 8, 9, 10, 11).
- різні рідні бібліотеки різних рішень лінійного програмування (CLP, Xpress, Cplex, Gurobi).
Запитання
- Що визначає різницю продуктивності між Linux та Windows великого багатопотокового додатку Java, який широко використовує вбудовані дзвінки?
- Чи можу я змінити впровадження, що могло б допомогти Windows, наприклад, чи слід уникати використання ExecutorService, який отримує тисячі викликів, і робити що замість цього?
ForkJoinPool
це більш ефективно, ніж ручне планування.
ForkJoinPool
замістьExecutorService
? 25% використання процесора дуже низьке, якщо ваша проблема пов'язана з процесором.