ByteBuffer.allocate () против ByteBuffer.allocateDirect () - PullRequest
133 голосов
/ 15 апреля 2011

К allocate() или allocateDirect(), вот в чем вопрос.

Уже несколько лет я просто придерживаюсь мысли, что, поскольку DirectByteBuffer s - это прямое отображение памяти на уровне ОСчто с вызовами get / put он будет выполняться быстрее, чем HeapByteBuffer s.Я никогда не интересовался точными подробностями ситуации до сих пор.Я хочу знать, какой из двух типов ByteBuffer быстрее и на каких условиях.

Ответы [ 4 ]

143 голосов
/ 15 апреля 2011

Рон Хитч в своей превосходной книге Java NIO , кажется, предлагает то, что я думал, может быть хорошим ответом на ваш вопрос:

Операционные системы выполняют ввод / вывод операции над областями памяти. Эти области памяти, насколько операционная система обеспокоена, смежны последовательности байтов. Это не удивительно тогда только байтовые буферы право на участие в I / O операции. Также напомним, что операционная система будет иметь прямой доступ адресное пространство процесса, в в этом случае процесс JVM, чтобы передать данные. Это означает, что области памяти которые являются объектами ввода / вывода, должны быть смежными последовательностями байтов. В JVM, массив байтов не может быть хранится непрерывно в памяти, или Сборщик мусора может переместить его в любой время. Массивы являются объектами в Java, и как данные хранятся внутри этого объект может отличаться от одной JVM реализация к другому.

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

Прямые байтовые буферы обычно лучший выбор для операций ввода-вывода. От дизайн, они поддерживают наиболее эффективный механизм ввода / вывода, доступный для JVM. Непрямые байтовые буферы могут быть передается на каналы, но при этом может понести наказание за производительность. Это обычно не возможно для непрямого буфер, чтобы быть целью нативного Операция ввода / вывода. Если вы передаете непрямой Объект ByteBuffer для канала для пишите, канал может неявно делать следующее на каждый звонок:

  1. Создать временный прямой ByteBuffer объект.
  2. Скопировать содержимое непрямого буфер во временный буфер.
  3. Выполнение операции ввода-вывода низкого уровня используя временный буфер.
  4. Объект временного буфера гаснет области и в конечном итоге мусор собраны.

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

Прямые буферы оптимальны для ввода / вывода, но они могут быть дороже создать непрямые байтовые буферы. Память, используемая прямыми буферами, распределяется путем вызова родной, специфичный для операционной системы код, минуя стандартную кучу JVM. Настройка и снос прямой буферы могут быть значительно больше дороже, чем кучи резидентных буферов, в зависимости от операционной системы хоста и реализация JVM. области памяти прямого буфера не подлежат сборке мусора потому что они вне стандарта Куча JVM.

Компромисс производительности при использовании прямые и непрямые буферы могут широко варьируются в зависимости от JVM, операционной системы, и дизайн кода. Распределяя память вне кучи, вы можете подвергнуть приложение к дополнительным силам который JVM не знает. когда принося дополнительные движущиеся части в играть, убедитесь, что вы достигаете желаемый эффект. Я рекомендую старое программное обеспечение: сначала сделай это работать, а затем сделать это быстро. Не беспокойся слишком много об оптимизации заранее; сосредоточиться в первую очередь на правильности. Реализация JVM может быть в состояниивыполнять кэширование в буфере или другие оптимизации, которые обеспечат необходимую вам производительность, без лишних усилий с вашей стороны.

25 голосов
/ 15 апреля 2011

Нет никаких оснований ожидать, что прямые буферы будут быстрее для доступа внутри jvm.Их преимущество появляется, когда вы передаете их нативному коду - например, коду за каналами всех видов.

17 голосов
/ 17 апреля 2011

, так как DirectByteBuffers являются прямыми отображение памяти на уровне ОС

Это не так. Они являются просто обычной памятью процесса приложения, но не подлежат перемещению во время Java GC, что значительно упрощает работу внутри уровня JNI. То, что вы описываете, относится к MappedByteBuffer.

чтобы он выполнял быстрее при вызовах get / put

Вывод не следует из предпосылки; предпосылка ложная; и заключение также ложно. Они быстрее, когда вы попадаете в слой JNI, и если вы читаете и пишете с одного и того же DirectByteBuffer, они на намного быстрее, потому что данные вообще не должны пересекать границу JNI.

17 голосов
/ 15 апреля 2011

Лучше всего делать свои собственные измерения.Быстрый ответ, по-видимому, заключается в том, что отправка из буфера allocateDirect() занимает на 25-75% меньше времени, чем вариант allocate() (тестируется как копирование файла в / dev / null), в зависимости от размера, но само выделение можетбыть значительно медленнее (даже в 100 раз).

Источники:

...