Производительность/стабильность файла с отображением памяти - Native или MappedByteBuffer - по сравнению с обычным ol 'FileOutputStream

Я поддерживаю устаревшее Java-приложение, которое использует плоские файлы (обычный текст) для сохранения. Из-за характера приложения размер этих файлов может достигать 100 МБ в день, и часто ограничивающим фактором в производительности приложения является файл IO. В настоящее время приложение использует простой ol 'java.io.FileOutputStream для записи данных на диск.

Недавно у нас было несколько разработчиков утверждать, что использование файлов с отображением памяти, реализованных в собственном коде (C/С++) и доступ через JNI, обеспечит большую производительность. Тем не менее, FileOutputStream уже использует собственные методы для своих основных методов (т.е. Write (byte [])), поэтому он представляет собой незначительное предположение без жестких данных или, по крайней мере, для некоторых доказательств.

У меня есть несколько вопросов по этому поводу:

  • Действительно ли это утверждение верно? Будут ли файлы с отображением памяти всегда обеспечить более быстрый IO по сравнению с Java FileOutputStream?

  • Является ли класс MappedByteBuffer доступ к файлам FileChannel та же функциональность, что и родная доступная библиотека с отображением памяти через JNI? Что такое MappedByteBuffer это может привести к использованию Решение JNI?

  • Каковы риски использования файлы с отображением памяти для дискового ввода-вывода в процессе производства выражение? То есть приложения которые имеют непрерывный минимальная перезагрузка (раз в месяц, макс.). Реальные анекдоты от производства приложений (Java или иначе) предпочтительным.

Вопрос №3 важен - я мог бы частично ответить на этот вопрос, написав приложение "игрушка", в котором перформанс IO использует различные варианты, описанные выше, но путем публикации в SO Я надеюсь на реальные анекдоты/данные пережевывать.

[EDIT] Уточнение - каждый день работы приложение создает несколько файлов размером от 100 МБ до 1 гигабайта. В общей сложности приложение может записывать несколько концертов в день.

Ответ 1

Возможно, вы сможете немного ускорить процесс, изучив, как ваши данные буферизуются во время записи. Это имеет тенденцию быть специфичным для приложения, поскольку вам понадобится идея ожидаемых шаблонов записи данных. Если важна согласованность данных, здесь будут компромиссы.

Если вы просто записываете новые данные на диск из своего приложения, то входы ввода/вывода с памятью, вероятно, не помогут. Я не вижу причин, по которым вы хотели бы инвестировать время в какое-то собственное закодированное собственное решение. Это просто кажется слишком сложным для вашего приложения, от того, что вы предоставили до сих пор.

Если вы уверены, что вам действительно нужна лучшая производительность ввода-вывода - или просто производительность O в вашем случае, я бы рассмотрел аппаратное решение, такое как настроенный дисковый массив. Бросать больше аппаратных средств в проблему часто бывает более экономически выгодным с точки зрения бизнеса, чем оптимизировать время для оптимизации программного обеспечения. Он также обычно быстрее реализуется и более надежен.

В общем, есть много подводных камней в оптимизации программного обеспечения. Вы вводите новые типы проблем в свое приложение. Вы можете столкнуться с проблемами памяти /GC, которые приведут к большему обслуживанию/настройке. Хуже всего то, что многие из этих проблем будут трудно протестировать, прежде чем вступать в производство.

Если бы это было мое приложение, я бы, вероятно, придерживался FileOutputStream с некоторой, возможно, настроенной буферизацией. После этого я воспользуюсь временем, затраченным на то, чтобы бросить в него больше оборудования.

Ответ 2

Входы/выходы, привязанные к карте памяти, не заставят ваши диски работать быстрее (!). Для линейного доступа это кажется немного бессмысленным.

NIO-сопоставленный буфер - это реальная вещь (обычная оговорка о любой разумной реализации).

Как и в случае с другими выделенными буферами NIO, буферы не являются нормальной памятью и обычно не имеют GCed. Если вы создаете многие из них, вы можете обнаружить, что у вас закончилось свободное место в памяти/адрес, не закончив кучу Java. Это, очевидно, проблема с долговременными процессами.

Ответ 3

Что касается пункта 3 - если машина выходит из строя и есть какие-либо страницы, которые не были сброшены на диск, они теряются. Другое дело, что трата адресного пространства - отображение файла в память потребляет адресное пространство (и требует смежной области), а на 32-битных машинах он немного ограничен. Но вы сказали о 100 МБ - так что это не должно быть проблемой. И еще одна вещь - расширение размера mmaped файла требует некоторой работы.

Кстати, это SO обсуждение также может дать вам некоторые идеи.

Ответ 4

По моему опыту, файлы с отображением памяти выполняют MUCH лучше, чем обычный доступ к файлам в случаях использования в реальном времени и настойчивости. Я работал в основном с С++ в Windows, но производительность Linux аналогична, и вы все равно планируете использовать JNI, поэтому я думаю, что это относится к вашей проблеме.

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

В другом проекте мне пришлось писать многоадресные сетевые приложения. Данные были отправлены в рандомизированном порядке, чтобы минимизировать влияние последовательной потери пакетов (в сочетании с FEC и схемами блокировки). Более того, данные могут значительно превышать адресное пространство (видеофайлы были больше 2 ГБ), поэтому распределение памяти не могло быть и речи. На стороне сервера разделы файлов были отображены по требованию, и сетевой уровень непосредственно отобразил данные из этих представлений; как следствие, использование памяти было очень низким. На стороне приемника не было способа предсказать порядок, в который были получены пакеты, поэтому он должен поддерживать ограниченное количество активных просмотров в целевом файле, и данные были скопированы непосредственно в эти представления. Когда пакет должен был быть помещен в неотображаемую область, самое старое представление было неотображено (и в конечном итоге очищено от файла системой) и заменено новым представлением в области назначения. Выступления были выдающимися, особенно потому, что система отлично справилась с выполнением данных в качестве фоновой задачи, а ограничения в режиме реального времени были легко выполнены.

С тех пор я убежден, что даже самая лучшая мелкомасштабная программная схема не может превзойти политику ввода-вывода по умолчанию с файлом с отображением памяти, потому что система знает больше, чем приложения для пользовательского пространства о том, когда и как должны быть данные написано. Кроме того, важно знать, что сопоставление памяти является обязательным при работе с большими данными, поскольку данные никогда не выделяются (следовательно, не потребляют память), а динамически отображаются в адресное пространство и управляются диспетчером виртуальной памяти системы, который всегда быстрее, чем куча. Таким образом, система всегда оптимально использует память и фиксирует данные всякий раз, когда это необходимо, позади приложения, не влияя на него.

Надеюсь, что это поможет.

Ответ 5

Я сделал исследование, где сравниваю производительность записи с исходным значением ByteBuffer и производительностью записи MappedByteBuffer. Файлы с отображением памяти поддерживаются ОС, и их задержки записи очень хороши, как вы можете видеть в моих контрольных номерах. Выполнение синхронной записи через FileChannel примерно в 20 раз медленнее, и поэтому люди постоянно выполняют асинхронную регистрацию. В моем исследовании я также приведу пример того, как реализовать асинхронное ведение журнала через свободную от блокировки и без мусора очередь для максимальной производительности, очень близкой к исходному ByteBuffer.

Ответ 6

Если вы пишете меньше байтов, это будет быстрее. Что, если вы отфильтровали его через gzipoutputstream или что, если вы написали свои данные в ZipFiles или JarFiles?

Ответ 7

Как упоминалось выше, используйте NIO (a.k.a. new IO). Там также появилось новое, новое IO.

Правильное использование жесткого диска RAID поможет вам, но это будет больно.

Мне очень нравится идея сжатия данных. Пойдите для чувака gzipoutputstream! Это удвоит вашу пропускную способность, если процессор сможет не отставать. Вероятно, вы можете воспользоваться преимуществами современных двухъядерных машин, а?

-Stosh