Frage ByteBuffer.allocate () und ByteBuffer.allocateDirect ()


Zu allocate() oder zu allocateDirect(), das ist hier die Frage.

Seit einigen Jahren bleibe ich bei diesem Gedanken DirectByteBuffers sind eine direkte Speicherzuordnung auf Betriebssystemebene, die bei get / put-Aufrufen schneller ausgeführt wird als HeapByteBuffers. Ich war nie wirklich daran interessiert, die genauen Details der Situation bis jetzt herauszufinden. Ich möchte wissen, welche der beiden Arten von ByteBuffers sind schneller und unter welchen Bedingungen.


119
2018-04-14 23:28


Ursprung


Antworten:


Ron Hitches in seinem ausgezeichneten Buch Java NIO scheint zu bieten, was ich dachte, könnte eine gute Antwort auf Ihre Frage sein:

Betriebssysteme führen E / A durch   Operationen auf Speicherbereichen. Diese   Speicherbereiche, soweit es die Bedienung betrifft   System betrifft, sind zusammenhängend   Sequenzen von Bytes. Es ist keine Überraschung   dann, dass nur Byte-Puffer sind   berechtigt zur Teilnahme an I / O   Operationen. Auch daran erinnern, dass die   Betriebssystem wird direkt zugreifen   der Adressraum des Prozesses, in   In diesem Fall ist der JVM-Prozess zu übertragen   die Daten. Dies bedeutet, dass Speicherbereiche   Das sind Ziele von I / O-Operationen   zusammenhängende Sequenzen von Bytes sein. Im   die JVM, ein Array von Bytes möglicherweise nicht   zusammenhängend im Speicher gespeichert, oder die   Garbage Collector könnte es bei jedem verschieben   Zeit. Arrays sind Objekte in Java und   die Art, wie Daten darin gespeichert werden   Objekt könnte von einer JVM abweichen   Umsetzung zu einem anderen.

Aus diesem Grund wird der Begriff a   direkter Puffer wurde eingeführt. Direkte   Puffer sind für die Interaktion vorgesehen   mit Kanälen und nativen I / O-Routinen.   Sie bemühen sich am besten, das zu speichern   Byte-Elemente in einem Speicherbereich, der a   Kanal kann für direkte oder rohe verwenden,   Zugriff über nativen Code zum Ermitteln   das Betriebssystem zu entleeren oder zu füllen   der Speicherbereich direkt.

Direkte Byte-Puffer sind normalerweise die   beste Wahl für E / A-Operationen. Durch   Design, sie unterstützen die meisten   effizienter E / A-Mechanismus verfügbar für   die JVM. Nichtdirekte Byte-Puffer können sein   an Kanäle übergeben, aber dies tun kann   eine Leistungsstrafe verursachen. Es ist   normalerweise nicht möglich für einen Nichtdirekten   Puffer als Ziel eines Natives   E / A-Betrieb. Wenn Sie einen Nichtdirekten passieren   ByteBuffer-Objekt an einen Kanal für   schreiben, der Kanal kann implizit tun   Folgendes bei jedem Anruf:

  1. Erstellen Sie einen temporären direkten ByteBuffer   Objekt.
  2. Kopiere den Inhalt des Nichtdirekten   Puffer zum temporären Puffer.
  3. Führen Sie den Low-Level-E / A-Vorgang aus   Verwenden des temporären Puffers.
  4. Das temporäre Pufferobjekt erlischt   von Umfang und ist schließlich Müll   gesammelt.

Dies kann möglicherweise zu einem Puffer führen   Kopieren und Objektwechsel auf jeder I / O,   Das sind genau die Arten von Dingen   Wir möchten es vermeiden. Allerdings abhängig   auf die Implementierung können die Dinge nicht   sei so schlecht. Die Laufzeit wird wahrscheinlich   Direktpuffer zwischenspeichern und wiederverwenden oder   führe andere schlaue Tricks aus, um zu verstärken   Durchsatz. Wenn Sie einfach erstellen   ein Puffer für den einmaligen Gebrauch, der   Unterschied ist nicht signifikant. Auf der   Andererseits, wenn Sie die   puffern wiederholt in a   High-Performance-Szenario, du bist   besser, direkte Puffer zuzuteilen   und sie wiederverwenden.

Direkte Puffer sind optimal für I / O,   aber sie können teurer sein   Erstellen Sie als nondirect Byte-Puffer.   Der von direkten Puffern verwendete Speicher ist   Zuweisung durch Aufruf von bis   nativ, betriebssystemspezifisch   unter Umgehung des Standard-JVM-Heaps.   Auf- und Abbau direkt   Puffer könnten wesentlich mehr sein   teurer als haufenresidente Puffer,   abhängig vom Betriebssystem des Hosts   und JVM-Implementierung. Das   Speicherbereiche von direkten Puffern   unterliegen nicht der Müllabfuhr   weil sie außerhalb des Standards sind   JVM-Heap.

Die Leistungseinbußen bei der Verwendung   direkte gegenüber nichtdirekten Puffern können   variieren weit nach JVM, Betriebssystem,   und Code-Design. Durch Zuweisen von Speicher   Außerhalb des Heap können Sie Ihre   Anwendung auf zusätzliche Kräfte von   was der JVM nicht bekannt ist. Wann   bringen Sie zusätzliche bewegliche Teile hinein   Spielen Sie, stellen Sie sicher, dass Sie erreichen   der gewünschte Effekt. Ich empfehle die   alte Software Maxime: Mach es erstmal   arbeite, dann mach es schnell. Mach dir keine Sorgen   zu viel über die Optimierung im Vorfeld;   Konzentriere dich zuerst auf die Richtigkeit. Das   JVM-Implementierung möglicherweise in der Lage sein   Pufferpuffer oder anderes durchführen   Optimierungen, die Ihnen die   Leistung, die Sie ohne viel brauchen   unnötige Anstrengung von Ihrer Seite.


129
2018-04-15 02:48



Es gibt keinen Grund zu erwarten, dass direkte Puffer schneller für den Zugriff sind Innerhalb der jvm. Ihr Vorteil kommt, wenn Sie sie an nativen Code übergeben - wie der Code hinter Kanälen aller Art.


24
2018-04-15 01:20



Am besten machen Sie Ihre eigenen Messungen. Schnelle Antwort scheint zu sein, dass das Senden von einem allocateDirect() Puffer benötigt 25% bis 75% weniger Zeit als der allocate() variant (getestet wie das Kopieren einer Datei nach / dev / null), abhängig von der Größe, aber das die Zuordnung selbst sein kann bedeutend langsamer (sogar um den Faktor 100).

Quellen:


17
2018-04-15 00:18



da DirectByteBuffers eine direkte sind   Speicherzuordnung auf Betriebssystemebene

Sie sind nicht. Sie sind nur ein normaler Speicher für den Anwendungsvorgang, unterliegen aber während Java GC keiner Verlagerung, wodurch die Dinge in der JNI-Ebene erheblich vereinfacht werden. Was Sie beschreiben, gilt für MappedByteBuffer.

dass es mit get / put Calls schneller funktioniert

Die Schlussfolgerung folgt nicht aus der Prämisse; die Prämisse ist falsch; und die Schlussfolgerung ist auch falsch. Sie sind schneller, wenn Sie in die JNI-Ebene gelangen und wenn Sie gleichzeitig lesen und schreiben DirectByteBuffer Sie sind viel schneller, weil die Daten niemals die JNI-Grenze überschreiten müssen.


17
2018-04-17 01:56