# RMI vs. SIMON



## tuxedo (11. Jan 2008)

Hallo zusammen,

nachdem es mich schwer angekotzt hat dass RMI Probleme macht wenn man den Client hinter einem Router hat und unbedingt Callbacks vom Server aus benutzen will (RMI versucht da immer eine Socketverbindung zurück zum Client aufzubauen...), hab ich mich drangesetzt und eine Alternative gebastelt.
Ich hab sie mal "SIMON" getauft. 

*S*imple
*I*nvocation of
*M*ethods
*O*ver
*N*etwork

Ich hab viel Diskussionen über die Geschwindigkeit von RMI gelesen. RMI wird meist als langsam bezeichnet. Aber je nach Anwendungszweck ist das mehr oder weniger anders zu betrachten.

Nun, meine erste Alpha von SIMON steht und ich habs mal verglichen mit RMI. Dazu hab ich einen Mini-Test geschrieben.

Sowohl RMI als auch SIMON haben Serverseitig nur eine einzige Methode:


```
public byte[] benchmark(byte[] b)
```

Der Test sieht wie folgt aus:


```
Erstelle X-Byte großes byte[]
befülle das byte[] mit Zufallswerten
Stoppe Zeit (
für 1000mal:
   rufe die Methode benchmark auf und übergebe das byte[]
)
```

Ich habe Tests von 16 Byte Array-Größe bis 65535 Byte gemacht. Client und Server liefen auf der gleichen Maschine. Verbunden wurde über Localhost. 

Testsetup:
Intel Core2 6420 @ 2,13Ghz
2GB RAM
JAVA 6 Update 3
Tests gestartet aus Eclipse 3.3 heraus

Das Ergebnis hab ich mal grafisch aufgereitet: 




Mein SIMON ist ziemlich Konstant was die Ausführungzeit der Test-Methode angeht. 
RMI variiert da ein wenig und kitzelt stellenweise mehr Speed raus. 

Ein wenig Hintergrundwissen zu SIMON (RMI wird intern auch nicht viel anders funktionieren):

Der Client holt sich vom Server via serialisierung das Interface des Serverobjets. Damit wird lokal ein Proxy gebastelt. Die Methodenaufrufe des Proxys werden abgefangen und an den Server übermittelt (Name des RemoteObjekts, Name des Remot-Methode (jeweils String), Parametertypen und Argumente (jeweils serialisierte Objekte)). Der Server nimmt die Daten entgegen, führt die besagte Methode mit Argumenten aus und liefert das Ergebnis wieder zurück an den Client (ebenfalls wieder serialisiertes Objekt)). Weitere "interna" wurde übrigens hier diskutiert.

So, jetzt meine Fragen an euch:

1) Hattet ihr mit RMI schon mal performanceschwierigkeiten? 
2) Für wie aussagekräftig haltet ihr meine Testergebnisse?
3) Hat einer nen Plan warum RMI das ganze unterschiedlich schnell ausführt? Die Methodenaufrufe sind immer wieder gleich und die übertragenen Daten sind immer zufällig. Dennoch schwanken die Anzahl der Methodenaufrufe ziemlich stark (von etwa 900 bis über 9000), wohingegen SIMON da immer konstant bei rund 3000 pro Sekunde bleibt.

Any Ideas?

- Alex


----------



## tuxedo (11. Jan 2008)

Hmm, kleine negative Ergänzung:

Über's echte Netzwerk ist SIMON momentan sch**sse langsam. Ein Methodenaufruf dauert rund 300ms ... RMI ist da fast ungebremst schnell.

Irgendwo _muss_ also noch der Wurm drin sein. *suchengeh*

Die 3 Fragen interessieren mich jedoch weiterhin.

- Alex

[update] Hab den Fehler vorerst mal behoben. Muss die beiden nun mal im lokalen Netzwerk vergleichen ...


----------



## tuxedo (11. Jan 2008)

So, neues Messergebnis. RMI ist nur noch einen Funken schneller als SIMON ...





Mein Mini-Test hatte einen Designfehler:

Die JVM hat scheint mitbekommen dass ich 1000mal immer das gleiche Objekt verschicke ... Nachdem ich jetzt vor jedem Senden das byte[] zufällig neu füllen lasse, liefert SIMON verlässliche Werte. Vorher konnte ich 65kbyte*1000, also rund 65mbyte in unter 2 Sekunden transferieren... Das konnte nicht stimmen. 

Jetzt sieht's besser aus. Für den File-Transfer sind jedoch beide nicht optimal geeignet. Mehr als knapp über 5MByte waren im Test mit 65kbyte/Aufruf nicht drin. Und rund 10Mbyte/s würden sich in dem Netzwerk gut übertragen lassen ...

Bin dennoch überrascht wie gut die erste Alpha schon funktioniert.

Meine Frage 3 hat sich mittlerweile erübrigt. Frage 1 und 2 lass ich mal noch offen.

- Alex


----------



## Guest (11. Jan 2008)

alex0801 hat gesagt.:
			
		

> ...nachdem es mich schwer angekotzt hat dass RMI Probleme macht wenn man den Client hinter einem Router hat und unbedingt Callbacks vom Server aus benutzen will (RMI versucht da immer eine Socketverbindung zurück zum Client aufzubauen...), ...


Nur aus Neugier, wie löst du das Callback-Problem ohne eine Verbindung zum Client?


----------



## HoaX (11. Jan 2008)

ich tippe mal entweder mit a) udp hole punching oder b) durch eine verbindung die vom client aus aufbebaut wird


----------



## tuxedo (12. Jan 2008)

Naja, ist doch ganz einfach:

Der Client verbindet sich mit dem Server... Damit haben wir ja EINE Verbindung. Warum RMI diese eine Verbindung nicht nutzt um die Callbacks abzuwickeln ist mir schleierhaft. Meine Implementierung verwendet jedenfalls diese eine Verbindung. 

Auch müssen meine Remote-Objekte nicht von irgendwelchen Klassen erben (z.B. UnicastRemoteObject). Es reicht ein einfaches Interface zu implementieren (welches übrigens keine Methoden mit bringt, dient nur der "erkennung" DASS es ein RemoteObjekt ist).

Ganz fehlerfrei ist die Sache noch nicht. Will noch "erkunden" wie RMI das macht mit der kurzen Latenzzeit, bzw. ob RMI ebenfalls den Nagle-Algo des TCP-Sockets abschaltet. Desweiteren brauch ich noch ein geschickteres Exception-Handling. 

- Alex


----------



## tuxedo (14. Jan 2008)

So, RMI-Sourcecode organisiert und mal reingeschaut...

In der Tat. Sun schaltet auch den Nagle-Algo ab. Bsp. TCPTransport-Klasse, Sourcecode (JDK5) ab Zeile 572:


```
// set socket to disable Nagle's algorithm (always send
	    // immediately)
	    // TBD: should this be left up to socket factory instead?
	    try {
		socket.setTcpNoDelay(true);
	    } catch (Exception e) {
		// if we fail to set this, ignore and proceed anyway
	    }
```

Was mich wundert ist, dass Sun den BUfferedInputStream und den BufferedOutputStream verwenden. Da sie beenfalls "flush()" benutzen, wundert's mich warum sie noch Puffern?! Oder hab ich da einen unschlagbaren Vorteil von Buffered*Stream nicht verstanden/gesehen?

- Alex


----------

