Производительность ввода / вывода нескольких JVM (затронута Windows 7, работает Linux) - PullRequest
2 голосов
/ 23 сентября 2011

У меня есть программа, которая создает файл размером около 50 МБ.Во время процесса программа часто перезаписывает разделы файла и принудительно вносит изменения на диск (порядка 100 раз).Он использует FileChannel и прямые ByteBuffers через fc.read (...), fc.write (...) и fc.force (...).

Новый текст:

Теперь у меня есть лучшее представление о проблеме.Кажется, проблема в том, что я использую три разных JVM для изменения файла (один создает его, два других (запускаются с первого) записывают в него).Каждая JVM правильно закрывает файл перед запуском следующей JVM.Проблема заключается в том, что стоимость fc.write () для этого файла иногда поднимается вверх для третьей JVM (в 100 раз больше обычной стоимости).То есть все операции записи выполняются одинаково медленно, а не просто очень длинные.Интересно, что один из способов помочь этому - вставить задержки (2 секунды) между запуском JVM.Без задержки, запись всегда медленная, с задержкой, запись идет примерно каждый второй раз или около того.

Я также нашел это Stackoverflow: Как удалить файл из памяти, отображенной с помощью FileChannel в Java?, которая описывает проблему для сопоставленных файлов, которую я не использую.

То, что я подозреваю, может происходить: Java не полностью освобождает дескриптор файла, когда я вызываю close ().Когда запускается следующая JVM, Java (или Windows) распознает параллельный доступ к этому файлу и устанавливает для него дорогой обработчик параллелизма, что делает запись дорогой.Имеет ли это смысл?

Проблема возникает в Windows 7 (Java 6 и 7, протестирована на двух машинах), но не в Linux (SuSE 11.3 64).

Старый текст:

Проблема: Запуск программы из тестового набора JUnit из Eclipse или из консоли работает нормально, это занимает около 3 секунд.Запуск программы с помощью задачи ant (или с помощью JUnit, запускающего отдельную JVM с использованием ProcessBuilder) замедляет программу до 70-80 секунд для той же задачи (фактор 20-30).

Использование -Xprof показывает, что использование 'force0' и 'pwrite' проходит через крышу с 34,1% (76 + 20 тиков) до 97,3% (3587 + 2913 + 751 тиков): Быстрый запуск:

27.0%     0  +    76    sun.nio.ch.FileChannelImpl.force0
 7.1%     0  +    20    sun.nio.ch.FileDispatcher.pwrite0
[..]

Медленный прогон:

    Interpreted + native   Method                        
48.1%     0  +  3587    sun.nio.ch.FileDispatcher.pwrite0
39.1%     0  +  2913    sun.nio.ch.FileChannelImpl.force0
[..]
     Stub + native   Method                        
10.1%     0  +   751    sun.nio.ch.FileDispatcher.pwrite0
[..]

ГХ и компиляция пренебрежимо малы.

Больше фактов:

Никакие другие методы не показывают существенного изменения в-Xprof output.

  • Это либо быстро, либо очень медленно, ничего между ними не бывает.
  • Память не проблема, все тестовые машины имеют по крайней мере 8 ГБ, процесс использует <200MB </li>
  • перезагрузка компьютера не помогает
  • переключение антивирусных сканеров и подобных вещей не влияет
  • Когда процесс идет медленно, загрузка процессора практически не происходит
  • Это никогда медленно при запуске из обычной JVM
  • Это довольно стабильно медленно при запуске в JVM, которая была запущена из первой JVM (через ProcessBuilder или какant-task)
  • Все JVM одинаковы.Я вывожу System.getProperty ("java.home") и параметры JVM через RuntimeMXBean RuntimemxBean = ManagementFactory.getRuntimeMXBean ();Аргументы списка = RuntimemxBean.getInputArguments ();
  • Я тестировал его на двух машинах с Windows7 64-битной, Java 7u2, Java 6u26 и JRockit, аппаратное обеспечение машин отличается, но результаты очень похожи.
  • Я также тестировал его из-за пределов Eclipse (муравей командной строки), но без разницы.
  • Вся программа написана мной самостоятельно, все, что она делает, это чтение и запись в / из этого файла,другие библиотеки не используются, особенно нативные.-

И некоторые страшные факты, в которые я просто не верю, имеют смысл:

  • Удаление всех файлов классов и перестройка проекта иногда (редко) помогают.Программа (вложенная версия) запускается быстро один или два раза, а затем снова становится очень медленной.
  • Установка новой JVM всегда помогает (каждый раз!), Так что (вложенная) программа работает быстро хотя бы один раз!Установка JDK считается двумя, потому что JDK-jre и JRE-jre работают как минимум один раз.Переустановка JVM не помогает.Ни одна не делает перезагрузку.Я еще не пробовал удалять / перезагружать / переустанавливать ...
  • Это единственный из двух способов, которым мне удалось получить быстрое время выполнения программы для вложенной программы.
    • Что может вызвать падение производительности для вложенных JVM?
    • Что именно делают эти методы (pwrite0 / force0)?-

Ответы [ 2 ]

0 голосов
/ 05 октября 2011

Одна вещь, которая может помочь, это убедиться, что вы явно установили FileChannel на null. Затем позвоните System.runFinalization() и, возможно, System.gc() в конце программы. Вам может потребоваться более одного звонка.

System.runFinalizersOnExit(true) также может помочь, но это устарело, поэтому вам придется иметь дело с предупреждениями компилятора.

0 голосов
/ 29 сентября 2011

Используете ли вы локальные диски для всех испытаний (в отличие от общего сетевого ресурса)?

Можете ли вы настроить Windows с оперативной памятью для хранения данных? Когда JVM завершает работу, по умолчанию его файловые дескрипторы будут закрыты, но вы можете увидеть, как происходит сброс данных на диск. При перезаписи большого количества данных предыдущая версия данных отбрасывается и может не вызвать дисковый ввод-вывод. Закрытие файла может заставить ядро ​​Windows неявно сбрасывать данные на диск. Таким образом, использование оперативной памяти позволит вам подтвердить, что их время дискового ввода-вывода удалено из вашей статистики.

Найдите инструмент для окон, который позволяет заставить ядро ​​сбросить все буферы на диск, используйте его между запусками JVM, посмотрите, сколько времени это занимает в данный момент.

Но я полагаю, что вы столкнулись с некоторой итерацией требований процесса и требований ядра при попытке управления буферным кешем дискового блока. В Linux есть такой инструмент, как "/ sbin / blockdev --flushbufs", который может это сделать.

FWIW

«pwrite» - это API Linux / Unix, позволяющий выполнять параллельную запись в файловый дескриптор (который будет лучшим API-интерфейсом ядра ядра для использования в JVM, я думаю, что в Win32 API уже предусмотрены те же типы использования для совместного использования дескриптор файла между потоками в процессе, но, поскольку у Sun наследственные вещи Unix названы в честь способа Unix). Google "pwrite (2)" для получения дополнительной информации об этом API.

"force" Я бы предположил, что это синхронизация файловой системы, что означает, что процесс запрашивает ядро ​​для сброса неписанных данных (которые в данный момент находятся в буферном кеше дискового блока) в файл на диске (такой, который был бы необходим перед Вы выключили компьютер). Это действие будет происходить автоматически с течением времени, но транзакционным системам необходимо знать, когда ранее записанные данные (с помощью pwrite) действительно попали на физический диск и были сохранены. Потому что какой-то другой дисковый ввод-вывод зависит от знания этого, например, от контрольной точки транзакции.

...