2009-02-11 6 views
11

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

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

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

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

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

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

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

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

ответ

4

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

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

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

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

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

+0

Набрал этот ответ, чтобы развернуть точки. Кроме того, фраза «или просто производительность O в вашем случае» действительно застряла со мной. – noahlz

+1

Yup, все о O. – Gary

1

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

Кстати, this SO discussion также может дать вам некоторые идеи.

+0

На самом деле 100 с МБ - так до Гига в файл. И некоторые развертывания приложения имеют несколько таких файлов! Я отредактирую, чтобы быть более четким. – noahlz

2

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

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

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

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

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

5

Накопитель с памятью не будет работать быстрее (!). Для линейного доступа это кажется немного бессмысленным.

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

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

+0

Приложение записывает на диск * далеко * чаще (> 99%), чем читает. Не могли бы вы рассказать о том, что вы подразумеваете под «Для линейного доступа это кажется немного бессмысленным» - применимо ли оно к добавлению opperations? – noahlz

+0

Операции добавления будут линейными (файловая система может фрагментировать ваш файл, но это должна быть небольшая проблема). –

0

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

+0

Затем вы торгуете служебными данными операций ввода-вывода для накладных расходов на кодирование/декодирование данных. Было бы немного экспериментов, чтобы убедиться, что это было жизнеспособно. – noahlz

0

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

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

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

-Stosh

1

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

+0

Я обнаружил, что мне приходилось периодически называть 'force()', чтобы мои изменения попали в файл. Следовательно, это было медленнее. Конечно, это было несколько лет назад ... – noahlz

+0

Вам не нужно вызывать force(), если вы не хотите защитить себя от сбоя ОС. – TraderJoeChicago