Effizientere Serialisierung und RMI für Java Seminar Verteilte Systeme WS 05/06 Prof. Dr.-Ing. Klaus-Peter Löhr Referent: Magnus Konze RMI - Wiederholung Aufrufer Aufgerufener Vertreter Treiber Referenzschicht Transportschicht Übersicht • • • • • Motivation Optimierung der Serialisierung KaRMI Benchmarks und Ergebnisse verwandte Arbeiten Motivation • wachsender Einsatz von Java bei Hochleistungsanwendungen • Java bietet brauchbare Mechanismen für internetweite Kommunikation • Javas RMI-Implementierung sehr langsam Vergleich Wo lässt sich RMI optimieren? Anforderungen schnelle Serialisierung Prozess-/Threadpool schneller Prozesswechsel aktives Warten verbindungsloses Protokoll spezielle Kommunikationshardware direkter Speicherzugriff angepasste Speicherbereinigung RMI nein nein(?) ? nein an TCP/IP gebunden umständlich nicht möglich nicht austauschbar Kostenanalyse Optimierung der Serialisierung Grundlagen • Serialisierung mittels dynamischer Typintrospektion (Reflektion) • Programmierer kann Methoden schreiben, die spezifischer arbeiten Bewertung • JDK-Serialisierung ist ein „nettes“ Feature • schneller geht es mit eigenen Methoden und der UKA-Serialisierung Kodierung von Typinformationen • JDK-Serialisierung fügt dem Bytefeld eine komplette Typbeschreibung hinzu • nicht notwenig in Rechnerverbund, da angenommen werden darf, dass alle auf den gleichen Bytecode Zugriff haben • UKA-Serialisierung sendet nur Klassenund Paketnamen Ersparnis Zwei Reset Arten • Zyklen im Objektgraphen werden mit Hilfe einer Hashtabelle gesucht • diese wird bei jedem Fernaufruf neu angelegt oder alternativ zurückgesetzt • UKA-Serialisierung bietet neue Resetmethode, die die Hashtabelle löscht, aber sich die Typinformationen merkt Ersparnis Bessere Pufferung I • JDK-Serialisierung enthält auf Empfängerseite keine eigene Puffer-Strategie • Wissen über Puffergröße kann nicht genutzt werden • UKA-Serialisierung hat eigenen Puffer, im Optimalfall kann er in einem Schritt gelesen Ersparnis Bessere Pufferung II • Programmierer kann bei JDK-Serialisierung nicht direkt auf den Puffer zugreifen • in UKA-Serialisierung möglich, da Puffer Teil der Serialisierung Ersparnis Reflektion erweitern • JNI sollte Methoden bereitstellen, mit denen primitive Instanzvariablen direkt in einen Puffer geschrieben oder aus einem Puffer gelesen werden können • diese könnten zum Beispiel über die Klasse Class zugänglich sein Floats und Doubles • Floats und Doubles treten häufig in wissenschaftlichen Anwendungen auf und sollten daher effizient serialisiert werden können • native Methoden wandeln diese vorher in maschinenunabhängige Byterepräsentationen um Floats und Doubles • weiterhin wird dieser Schritt bei Feldern für jedes Element einzeln gemacht • sollte mit JIT-Compiler inline gemacht werden Testergebnisse Testergebnisse Design • UKA-Serialisierung setzt selbstgeschriebene Serialisierungsmethoden voraus • für „herkömmliche“ Objekte muss die JDKSerialisierung verfügbar bleiben Ansatz 1 • JDK-Serialisierung kopieren • Kopie bearbeiten • per CLASSPATH Variable gewünschte Implementierung auswählen • Nachteil: aufwendige Änderungen bei jedem JDK Update Ansatz 2 • UKA-Serialisierung Unterklasse der JDKSerialisierung • Nachteil: bestehender Code muss geändert werden (speziell Unterklassen der JDK-S.) • Security Manager muss konfiguriert werden • Bemerkung: sogar Klassen, deren Quellcode nicht vorliegt, können angepasst werden Problem bei Ansatz 2 • JDK-Serialisierung kann nicht gleichzeitig mit neuer Serialisierung benutzt werden protected ObjectOutputStream() { [...] // Abfrage des Security Managers enableOverride=true; } public final void writeObject(Object obj) { if (enableOverride) writeObjectOverride(obj) [...] } protected void writeObjectOverride() {...} Ansatz 3 • Problem von Ansatz 2 mit Delegation lösen neue Probleme: • aufwändig • funktioniert nicht in allen Fällen Ansatz 4 Änderungen an der JDK-Implementierung: • ObjectOutputStream bekommt Initialisierungsmethode • Flag enableSubclassImplementation wird protected (vorher private) • writeObject nicht mehr final Ergebnisse KaRMI KaRMI • KaRMI ist eine neu entworfene Implementierung von RMI • Ziel war ein schlankeres und schnelleres Framework Saubere Schnittstellen • wie bei RMI drei Schichten (Vertreter/Treiber, Referenz, Transport) • Fernaufruf in KaRMI benötigt nur zwei Methodenaufrufe zwischen Schichten (>30 in RMI) • andere Referenz- und Transportimplementierungen können leicht genutzt werden Saubere Schnittstellen • Sockets in RMI auf Anwendungsebene • daher muss Socketsemantik auch auf Transportebene genutzt werden (also auch z.B. für nicht-TCP/IP Netzwerke) => in KaRMI Sockets vom RMI-Design getrennt KaRMI Entwurf Performanz Verbesserung • RMI nutzt Hashtabellen, obwohl Felder effizienter wären • viele temporäre Objekte in RMI • KaRMIs Referenzschicht erkennt, wenn ein Objekt lokal ist • RMI Code enthält Debugcode (kann umgangen werden => Flags müssen geprüft werden) Technologie Objekte • KaRMI kann über nicht-TCP/IP Netzwerke kommunizieren • dafür wurde ein Technologie-Objekt eingeführt • für jede verfügbare Technologie ein Objekt, das Beste wird genutzt austauschbare Speicherbereinigung • Verteilte Speicherbereinigung ist kompliziert (verlorene, doppelte, verspätete Nachrichten, Knotenabstürze...) • kein perfekter Algorithmus bekannt • in KaRMI kann eine der Umgebung angepasste Speicherbereinigung eingesetzt werden Einschränkungen um alten Code zu nutzen, muss man: • BOOTCLASSPATH-Variable anpassen • Vertreter neu erstellen • Treiber neu erstellen Einschränkungen • • • • KaRMI kann nicht genutzt werden, wenn: alter Code die Socketfactory benutzt alter Code undokumentierte RMI Klassen nutzt Inkompatibilitäten: Vertreter und Referenzobjekte erben nicht mehr von Remoteobject nur eine RMI-Registrierung pro JVM Benchmarks und Ergebnisse Parastation • Technologie, die mehrere Standardrechner zu einem Rechner verbindet • Performanzgewinn durch Umgehung des Betriebssystems Benchmarks • ping (2 Rechner) • konkurrierende Aufrufe (1 Server, mehrere Clients) • Anwendungen (Hamming Problem, Erzeugung von Paraffin-Strukturformeln, Lösen von Laplace Differentialgleichungen auf einem 2D-Gitter) Konfigurationen • • • • 4 Software Konfigurationen: normales RMI RMI mit UKA-Serialisierung KaRMI mit normaler Serialisierung KaRMI mit UKA-Serialisierung Konfigurationen 3 Hardware Konfigurationen • zwei 350 MHz Pentium 2, Windows NT 4.0, Ethernet, Java 1.2 • 8er Bündel von 500 MHz Alphas, FastEthernet • 8er Bündel von 500 MHz Alphas, Parastation Bandbreite Zeitersparnis Zeitersparnis bei Feldern Verwandte Arbeiten Verwandte Arbeiten • explizite Versendemethoden • Compilerprojekt „Manta“: kompiliert Teil des Javacode in nativen Code • Java/DSM nutzt verteilten Speicher statt Fernaufrufe • direkter Zugriff auf Hardware Verwandte Arbeiten • Cache für fernaufrufbare Objekte • alternative Technologie (Horb, Voyager) Quellen • • • • • • Philippsen, Haumacher, Nester: More Efficient Serialization and RMI for Java Christian Nester: Eine flexibles RMI Design für eine effiziente Cluster Computer Implementierung Java Object Serialization Specification Friedrich Esser: Java 2 www.jacorb.org www.wikipedia.de