# Apache Mina -> await()



## Kr0e (31. Jan 2010)

Hi,

wieder mal eine "Grundsatzfrage":

Momentan warte ich bei session.write(obj) mit .await() darauf, dass ein Paket wirklich verschickt wurde.

Warum warte ich ? Weil ich zum Senden immer ein und denselben Buffer benutze. Wenn ich den Buffer bloß in die Warteschlange einreihen würde, könnte es passieren, dass in den Buffer innerhalb meines Programms erneut geschrieben wird, obwohl der Buffer noch garnicht verschickt wurde...

Problem: Mir ist aufgefallen, dass mein Programm ein gutes Stück langsamer läuft, wenn ich await() benutze. Die Konsequenz ist jetzt natürlich, dass ich meinen Buffer (Kurz bevor ich ihn verschicke), dupliziere und in einen neuen ByteBuffer kopiere, was meiner Meinung nach unschön ist. Außerdem bringt das ganze noch ein weiteres Problem:
Es kann passieren (Wenn mal Mina langsamer die Pakete verschickt, als das Programm neue in die Queue setzt), dass es OutOfHeapspace Exceptions gibt... Was natürlich auch verständlich ist, da ja immer neue Bufferinstanzen erstellt werden.

Wie macht ihr das ?

Gruß,
Chris


----------



## FArt (1. Feb 2010)

Entweder schneller abarbeiten oder eine Queue realisieren, deren Einträge persistent sind.


----------



## tuxedo (1. Feb 2010)

Also mit dem zweiten Paket warten bis das erste vollends verschickt ist, ist ja auf dem Layer irgendwie ein Schuss ins eigene Knie. Dass das langsam geht hätte dir doch vorher schon klar sein müssen 

Wie du ja schon weißt hab ich für jedes Paket in SIMON nen eigenen Buffer. Die Zeit die für's NIO Buffer anlegen benötigt wird kann man getrost vernachlässigen. Glaub mir, das Netzwerk ist langsamer wie das anlegen der Buffer. 

Das mit dem überlaufenden Heap ist in der Tat etwas problematisch. Aber wieso willst du schneller senden als du übertragen kannst? Die Session kannst du btw. prima fragen wieviel/wie schnell sie bisher überträgt. Dementsprechend könntest du das senden drosseln.

- Alex


----------



## Kr0e (1. Feb 2010)

Hi,

eine prima Drosselung wäre es meiner Meinung nach, wenn man die Größe der WriteRequestQueue irgendwie einstellen könnte.
Geht das ?

Wie einer Art LinkedBlockingQueue oder so..

Dann kann man sich sicher sein, dass z.b. bei einem Clienten nie mehr als z.B. 5MB in der Queue sind...

Gruß,
Chris


----------



## tuxedo (1. Feb 2010)

Nochmal die Frage:
Wozu mehr senden als die Leitung her gibt?

Außer bei Filetransfers oder krückig langsamen mobilen Verbindungen ala CSD fällt mir da nix ein.

Was hast du davon wenn du die Größe der Queue einstellst? Was passiert wenn du mehr einträgst? Ich hätte da jetzt eine Exception oder dergleichen erwartet. Aber was hast du dann davon? Schick einfach nicht mehr wie die Leitung aktuell her gibt und gut ist  Und im Falle von Files etc. stellst du eben einen Thread ab der das senden übernimmt und ggf. kurz schlafen geht wenn die Leitung offensichtlich überlastet ist.

Wenn du mehr zu den "statistischen Daten" deiner Session wissen willst ist die API Doc oder die Mailingliste genau das richtige.

Gruß
Alex


----------



## Kr0e (1. Feb 2010)

Naja, das Problem ist, dass die obere Schicht meines Programms nichts von Sachen wie "Übertragungsgeschwindigkeit" oder Ähnliches weiß. Sprich das Programm sendet fröhlich Daten bis der Arzt kommt... Jetzt dachte ich mir "Dann lass halt die writeMethod
ne Weile blockieren, falls die Leitung überlastet ist". Aber wie sehe ich denn, ob die Leitung überlastet ist ?! Ich hab das Gefühl, ich verstehe grad nciht wie du das meinst.

Hab derweil mal ne kleine Lösung gemacht:

Im IoFilter zähle ich nun die Bytes die durch "filterWrite" reinkommen. Bei messageSent subtrahiere ich die Zahl der geschrieben Bytes. Ich überprüfe nun bei session.write() ob in der Queue mehr als 5mb stehen. Wenn ja, warte ich solange bis mindest für mein Paket wieder Platz und kehre dann wieder zurück.

Hast du sowas in der Art gemeint ?!

Gruß,
Chris


----------



## tuxedo (1. Feb 2010)

Schau mal in das Session-Objekt: Da gibts methoden die dir unter anderem verrate wieviele bytes/sek Durchsatz da sind. Wenn ich richtig liege liefert dir diese Methode den Durchsatz den Mina beim "wegschreiben" erreicht.

Und mit der Info kannst du entsprechend Drosseln.

- Alex


----------



## Kr0e (1. Feb 2010)

Hi, kann sein, dass ich mich grad etwas doof anstelle, aber um mit der Info, wie schnell geschrieben wird, etwas zu drosseln, brauche ich dafür dann nicht die "theoretisch Geschwindigkeit" die mein Netzwerk erreichen kann ? Ich meine, wie sollte ich sonst vergleichen und entscheiden wanns zu viel ist bzw zu wenig....  Und wie stark drosseln ? Einfach eine ms warten ? Mich entsprechend aufwecken lassen ?

Sry, aber irgendwie blick in diesem Wirrwar nicht durch.

Mal auf dein SIMON bezogen... Angenommen ich erstelle damit einen RawDataChannel und sende nun fröhlich ByteBuffers... Ohne sowas wie darauf zu achten, ob Mina mit der Geschwindigkeit mit der die Pakete in die Warteschlange gesetzt werden, klar kommt oder aber auch nicht.... Würde es bei dir ebenfalls im Extremfall zu OutOfMemory kommen, oder hast du dafür schon was implementiert, was ich mir anschauen könnte... ?

Gruß,
Chris

PS: Danke für deine Geduld 

EDIT: Die Methode session.getWrittenBytesThroughput() klappt bei mir garnicht. Sie gibt 0 zurück. Muss man evt. erst irgednwas initialisieren ?


----------



## Kr0e (1. Feb 2010)

Ok, hab jetzt eine akzeptable Geschwindigkeit.
Das Konzept, dass sich gemerkt wird, wieviel Bytes gerade in der Liste sind, ist schließlich aufgegangen.

Erreiche nun 17000 Methodcalls mit jeweil 1024 bytes Daten über localhost.

Ich glaube genau da war auch der Grund begraben, warum Mina bei mir niee soooo wirklich schnell war.

Hauptsache es klappt nun...

Gruß,
Chris


----------



## tuxedo (2. Feb 2010)

Bzgl. deiner Frage wie ich das mit SIMON und den RawChannels regle:

Ein write() auf dem RawChannel blockiert solange bis die Daten auf der anderen Seite angekommen sind. D.h. wenn ich in einer Schleife einen Datenblock einer Datei lese und durch den Rawchannel schicke, dann kann ich damit nicht schneller schicken als empfangen wird. 

Um den Effekt wie bei dir zu erreichen müsste ich den RawChannel nochmal wrappen und eine Queue dazwischen setzen, die dann, wenn man zu schnell hinein schreibt und der RawChannel nicht hinterher kommt, überläuft.

Was den Durchsatz angeht:

Mein letzter großer Test belief sich auf rund 11.000 Pakete die gesendet werden, und deren Empfang intern im Protokoll bestätigt werden muss. Dazwischen saß noch hier und da ein wenig klassische Serialisierung. Paketgröße eines Methodenaufrufs war auch etwa knapp 1kiB. Allerdings hab ich von simulierten 50 Clients den Server (Quadcore) befeuert.

Ich denke im großen und ganzen ist das mit deinen Werten vergleichbar.

- Alex


----------



## Kr0e (2. Feb 2010)

Freut mich zu hören, dass ich wenigstens nichts völlig unbrauchbares gemacht hab jetzt.
Deswegen war ich auch etwas deprimiert am Anfang... Geb mir Mühe das Protokoll möglichst "simpel" zu halten, und
dann noch die Sache mit dem Buffer (Der möglichst nie "umkopiert" werden muss) und trotzdem solch langsame Übertragungsraten...
Mal kurz zu deinem RawDataChannel. Du wartest also auf eine Antwort, bis du das nächste Packet schicken lässt ?
Kann es dadurch nicht auch recht langsam bei großen Datenmengen werden ? (Datei oder ähnliches...) Oder war der dafür garnicht gedacht ?

Gruß,
Chris


----------



## tuxedo (2. Feb 2010)

Den Durchsatz des RawChannels hab ich glaub noch nicht gemessen. Aber in den Tests war das nicht wirklich langsam. Werd's mir aber mal auf meine ToDo Liste schreiben.
Wenn ich aber so darüber nachdenke:

Wenn ich 3MB verschicken will, und die Leitung auf beiden Seiten nicht schneller als 70kyte/sek senden kann, und ich in 1MB Paketen Daten schicke, dann ergibt sich daraus folgendes:

> Sende 1MB+ProtokollOverhead (13byte) über die Leitung
< empfange Empfangsbestätigung (10byte)
> Sende 1MB+ProtokollOverhead (13byte) über die Leitung
< empfange Empfangsbestätigung (10byte)
> Sende 1MB+ProtokollOverhead (13byte) über die Leitung
< empfange Empfangsbestätigung (10byte)

Beim Senden der Daten kann man die 13byte denke ich vernachlässigen. 
1MB / 70kbyte = 14.3sec
10byte / 70kbyte = nahezu 0sec

Die Zeit ich ich also für's übertragen der Empfangsbestätigung brauche ist im Vergleich zur Dauer des Sendens so verschwindend gering, dass das absolut nicht ins Gewicht fällt. Natürlich sieht die Sache ganz anders aus wenn ich nur 100 byte Pakete schicke. Aber dafür ist der RawChannel ja nicht gedacht. Man muss sich also überlegen wie man fragmentiert um eine akzeptable Geschwindigkeit zu erreichen. In den meisten Fällen wird das wohl im 8k - 512k Bereich liegen 

Der Grund für die implementierung des RawChannels war/ist, dass das übertragen von größeren Datenmengen über normale remote-Methodenaufrufe nicht performant ist. Intern passiert da ne ganze Menge. Der Overhead eiens Methodenaufrufs ist etwas größer, ich muss für jede Methode einen Hash generieren und den auf beiden Seiten vergleichen, ich muss per Reflection eine Methode auf der anderen Seite aufrufen und und und.

Daten über den RawChannel durchlaufen den gleichen Protokoll-Stack, aber die Daten werden auf beiden Seiten anders gehandhabt und laufen vorbei am dem Reflection-Kram. Somit hast du da schon den Geschwindigkeitsvorteil gegenüber der Übertraghung von Daten via Methodenaufrufe. 

- Alex


----------



## Kr0e (2. Feb 2010)

Jepp, dass das verschwindend gering ist, bei genügend großen Paketen, stimmt schon. Ich brauchte jetzt ne Lösung die quasi immer optimal sein sollte.

Ich wollte ja auch erst ne RMI Alternative schreiben, was mir auch gelungen ist, aber meine Art der Serialisierung war wohl alles andere als perfekt. Die Pakete wurden viel zu groß selbst für simple Datentypen...

Deswegen hab ich dann einen völlig neuen Ansatz gewählt. (Inspiriert durch deine RawDataChannels, allerdings doch etwas anders)
Ein Channel ist bei mir die abstrakte Verbindung zwischen 2 Endpunkten. (Ähnlich dem Channelkonzept von NIO)
Allerdings habe ich eine Kleinigkeit anders:

Jeder Channel muss bei mir über einen "Multiplexer" geöffnet werden. Ein Multiplexer ist bei mir eine abstrakte Klasse die quasi die Channels verwaltet (Mit IDs versorgt usw...) Die einzige Methode die eine Subclass von Multiplexer überschreiben muss, ist
"transfer(ByteBuffer buffer)"...

Diese Transportebene muss sich nur darum kümmern, dass dieser Buffer zu dem Remotemultiplexer kommt irgendwie. Der Remotemultiplexer weißt das dann wieder dem entsprechendem channel zu.

Also wenn ich Channel A öffne, öffnet such automatisch ein Channel des selben Klassentyps auf dem anderen PC.
Diese Channel sind nun eins. Sprich wenn ich eine Klasse von Channel ableite und öffne, dass öffnet sich exakt der selbe Typ auf der Gegenseite. Das erspart viel Arbeit.

Auf diesem System hab ich dann eine neue RMI Alternative implementiert.

Gruß,
Chris

PS: Ich glaube der Aufruf einer Methode via Reflection ist so fix, dass der Overhead fast als 0 angesehen werden. Hab jetzt zwar keine "vergleichende" Tests gemacht, aber eine Datenübertragung bei meinem System über die RMI-Variante dauert nicht länger als als ohne. Ich habe das so geregelt, dass auf Methoden die void zurück geben auch nicht gewartet wird. Das erspart die Antwortdauer...


----------



## tuxedo (2. Feb 2010)

Darf man fragen warum du auch eine RMI Variante baust? Was macht denn deine, Abstrakt gesehen, anders als meine?

Oder ist deine "etwas sehr speziell"? Oder war dir nur langweilig? Oder hattest du ein Lizenzproblem?

Zu deiner Multiplexer-Geschichte:

Naja, sowas in der Art ist in Simon auch enthalten. Ich hab eine einzige Verbindung zwischen Client und Server und meine Dispatcher-Klasse leitet empfangene Aufrufe an einen ThreadPool zur Verarbeitung weiter und schickt lokale Aufrufe an. Nur hab ich für die einzelnen Objekte nicht ne extra Channel-Schicht eingezogen, sondern arbeite hier streng Nachrichten-basiert. 

Ein Methodenaufruf erzeugt eine andere Nachricht wie ein Lookup,das öffnen einen RawChannels oder das Senden von RaqChannel Daten. Die Hin- und Rück-Pakete halte ich mit Sequenz-IDs zusammen. 

Am Client gibts noch den Spezialfall, dass mehrere Lookups auf ein und dieselbe Registry oder ein und dasselbe Objekt über ein und dieselbe Verbindung laufen statt für jeden Lookup eine weitere Socketverbindung zu öffnen. 

Deine Variante erinnert irgenwie an das nochmalige abstrahieren der Transportschicht. Auf der einen Seite hast du eine gemeinsame Grundlage (roh-übertragung der Daten) für Methodenaufrufe und für normale Datenübertragung. Auf der anderen Seite wird das Methoden übertragen komplexer.

MINA ist an sich schon eine saubere Abstraktion von der Transportschicht und mit entsprechenden Protocol-Codecs auch extrem flexibel was die Übertragung von unterschiedlichen Daten anbelangt. An und für sich ist die doppelte Abstraktion, so wie ich das bei dir Verstanden habe, unnötig (sofern du nicht über MINA-Features abstrahiert hast).

- Alex

P.S. Ach ja, zum Thema Reflection und Geschwindigkeit:

Bei nem einzelnen Methodenaufruf ist das nicht wirklich messbar. Aber wieviele Methodenaufrufe machst du als Client pro Sekunde?!
Wenn du allerdings Daten überträgst und kleine aber feine Puffer benutzt, dann hast du unter Umständen tausende "Aufrufe" pro Sekunde. Wenn das viele Clients an einem Server so machen, dann gerät der schon eher in Stress. EInfacher ist es da wenn die Daten einfach roh durchgereicht werden. Und genau das macht mein RawChannel.


----------



## Kr0e (2. Feb 2010)

"Etwas sehr speziell" trifft es. Aber auch die GPL hat mich etwas gestört. Ich kann noch nicht sagen was die Endlizenz sein soll, und wenn man dann auf GPL aufbaut und sichs hinterher evt. anders überlegt.. tja.. Dumm gelaufen.

Zum Thema "doppelt abstrakt"... Mein Channel hat absolut keine Einschränkung was Übertragung angeht. Es war auch gedacht das ganze lokal laufen lassen zu können und nur die Referenzen zu verschieben. Mina ist meiner Meinung nach shcon auf diese Netzwerkgeschichte ausgelegt... Klar es ist abstrakt und man kann damit sehr viel machen, nur leider bietet Mina mehr als ich dafür speziell brauche. Ich benutze Mina nur, weil es (Nachdem ich das await() weggenommen hab ) es NIO, sehr schnell und leicht zu benutzen ist. Insofern ist es nicht wirklich doppelt-gemoppelt. Ja, es sind 2 Abstraktionen, aber letztenendes notwendige für mein Vorhaben.

Ein Beispiel für die "doppelte Abstraktion":
Angenommen es soll ein Channel über mehrere Wege geöffnet werden... Also es hängen z.B. 10 PCs in der Transportschicht die als Proxies agieren. Dann ändert sich mein Code nicht, nur die "transfer(ByteBuffer)" Methode muss angepasst werden. Sprich der Channel muss lediglich über einen anderen Multiplexer geöffnet werden. Den Rest regelt dann eine Impl. mit Mina oder was auch immer. Man kanns auch ausdrucken und zu Fuß zum anderen Pc bringen. 

Also ich finde diese zweite Abstraktion schon sinnvoll.

Gruß,
Chris

PS:
Nochmal wegen Reflection. Ich glaub da gibt es auch bei seeehhr vielen Aufrufen keinen Engpass.
Hab ein lokales Proxyobjekt genommen und 1.000.000 eine Methode mit 128 byte Daten gefüllt.
Der Unterschied lag bei mir unter einer Millisekunde. (System.currentTime() Differenz war meistens 0 oder 1).
Ich glaube das kann man getrost vernachlässigen.


----------



## tuxedo (3. Feb 2010)

Kr0e hat gesagt.:


> "Etwas sehr speziell" trifft es. Aber auch die GPL hat mich etwas gestört. Ich kann noch nicht sagen was die Endlizenz sein soll, und wenn man dann auf GPL aufbaut und sichs hinterher evt. anders überlegt.. tja.. Dumm gelaufen.



Ist ja nicht so dass die propritäre Lizenz ein Vermögen kostet  Wer mit dem Programm hinterher Geld verdient (auch wenns nicht zum Lebven reicht) kann sich locker vom Hocker ne Lizenz leisten 
Aber gut. Jedem das seine. Auf der anderen Seite hast du beim selbst basteln ja auch noch was gelernt 



> PS:
> Nochmal wegen Reflection. Ich glaub da gibt es auch bei seeehhr vielen Aufrufen keinen Engpass.
> Hab ein lokales Proxyobjekt genommen und 1.000.000 eine Methode mit 128 byte Daten gefüllt.
> Der Unterschied lag bei mir unter einer Millisekunde. (System.currentTime() Differenz war meistens 0 oder 1).
> Ich glaube das kann man getrost vernachlässigen.



Hmm, da muss ich bei mir glaub nochmal nachmessen. Wäre ja der Hammer dass ich mich so krass geirrt habe

Gruß
Alex


----------



## Kr0e (3. Feb 2010)

Es sind 67 ms. Messfehler. Aber dennoch akzeptabel bei 1.000.000 Aufrufen. Da wird das Netzwerk sicher langsamer sein.
Hatte dummerweise garnicht das Proxyobjekt aufgerufen.

Das bedeutet:
0,000067 ms pro Aufruf mehr...

Habs nochmal auf einem etwas langsameren rechner getestet:
250 ms länger
sprich selbst auf einem alten Rechner:
0.00025 ms länger..
Ich denke, das kann also vernachlässigt werden.

Da is die Antwortzeit, die benötigt wird, damit der RawDataChannel wieder zurückkehrt, größer.

Gruß,
Chris

PS: Hier der Code:


```
public class AppStart implements ITest {

    @Override
    public void test(ByteBuffer buffer) {
    }
}

//ITest erklärt sich ja von selbst...

//Der restliche Code:

 public static void main(String[] args) {
        final AppStart as = new AppStart();

        Object proxy = ReflectHelper.Util.newProxy(AppStart.class, new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return method.invoke(as, args);
            }
        });

        ByteBuffer b = ByteBuffer.allocate(128);
        ITest t = (ITest) proxy;

        long d = System.currentTimeMillis();
        for(int i = 0; i < 1000000; i++) {
            t.test(b);
        }
        System.out.println(System.currentTimeMillis()-d);

        d = System.currentTimeMillis();
        for(int i = 0; i < 1000000; i++) {
            as.test(b);
        }
        System.out.println(System.currentTimeMillis()-d);
}
```


----------



## Kr0e (3. Feb 2010)

Noch was anderes...

Wie schnell SOLLTE eigentlich eine localhost Verbindung sein ? Ist das abhängig von der Ethernetkarte ? Ist also z.B. ne gigabit leitung schneller bei localhost paketen als eine 100 mbit ? Oder ist das nicht wichtig ?
Ich erreiche einen lokalen Durchsatz von 86 mb/s. Und ehrlich gesagt kann ich damit nichts anfangen. Ist das jetzt gut oder schlecht ?!  (Ich hab ne Gigabitkarte. läuft aber in einem 100mbit Netzwerk)

Gruß,
Chris


----------



## tuxedo (3. Feb 2010)

AFAIK wird bei Localhostverbindungen der Transfer mehr oder weniger direkt durch den RAM geleitet. Ob 100mbit oder gigabit Karte im System oder nicht spiel keine Rolle (localhost geht ja auch komplett ohne Netzwerkkarte).

Die 86MB/sek sind schon ziemlich gut würd' ich sagen. Selbst in einem Gigabit-Netzwerk wäre das ganz schön schnell.

Hier im Büro krieg ich im Gigabit-Netzwerk Filetransfers hin die sich am Limit der verwendeten Festplatten orientieren.

Was sagt denn deine CPU während dem Test?

- Alex


----------



## Kr0e (3. Feb 2010)

Die sagt 50 %... (Irgendwie aufgeteilt auf den 4Cores). Keine Ahnung ob das gut ist (bzw. gerechtfertigt...).
Hab ansich ne ziemlich gute Netzwerkkarte. (Intel Ethernet und Intel Mainboard) Auch wenn die Netzwerkarte da jetzt keine Rolle spielt.

Ist das bei dir ähnlich ? Ist der Overhead für's rumschieben normal ? Ich wüsste auch ehrlich gesagt nicht, woran das liegen könnte.
Der Profiler von Netbeans sagt, dass die meiste CPU-Zeit wohl für byte[] draufgeht. Ich benutze allerdings nur ByteBuffer. Ist für den Profiler anscheinend das Gleiche. Ich mach das jetzt so ähnlich wie du: Ich erstelle einen Buffer bescheibe ihn und verschicke ihn.

Sind die Werte i.O. ?

PS:
Im Netzwerk bei 100mbit scheint irgednwas nicht richtig zu sein. Bekomme 30 % Netzwerkauslastung hin. 3mb/s.
Hab grad noch ein wenig dazu gelesen, da die relativ hohe CPU Last auch beim 100mbit Lan auftritt. Allerdings nicht so extrem.
Bei mir sinds 25-35%. Undzwar hab ich gelesen, dass das wohl mit der Flowcontrol in Verbindung gebracht werden kann (Die hohe CPU Last udn die geringe Übertragung). Sprich wenn zu schnell geschrieben wird z.b. und das RcvWindow ständig überläuft. Dann muss die Gegenseite ja quasi immer benachrichtigt werden, dass das gerade etwas zu schnell war. Ich bin mir aber nicht sicher, wie man das ändern kann. Vlt einen extrem hohen Wert für den Empfangspuffer bei einem Socket ? (1024*1024 statt den üblichen 8192...) Damit dieser seltener überläuft... Werde wohl noch einige Tests machen müssen, um das perfekt zu regeln.

PSS:
Irgendwie hab ich das gefühl, ich werde von meinem PC nach Strich und Faden verarscht. Habe jetzt die ReceiveBufferSize vom Socket 8x so hoch gesetzt wie die sendbuffersize. 8192 <-> 1024*64
Erreiche nun lokal einen Durchsatz von 108 mb/s.
Im Lan hingegen 0.6mb/s. Ich muss zu geben, ich schicke nur ByteBuffer durch die Gegend mit dem selben Inhalt. Merkt Mina sowas und sendet dann irgendwie nur die Meldung dass das selbe nochmal kam ?! Ich begreife das nicht.


----------



## tuxedo (4. Feb 2010)

Also ich habe bei mit die Puffergröße auf dem Socket nach wie vor bei 8k stehen. Allerdings hab ich den Nagle-Algo abgeschaltet. Das erhöht den Durchsatz etwas (eigentlich nur minimal) und verringert die Latenz (bei mir im Schnitt um 100ms).

Im 100Mbit LAN krieg ich knapp 100Mbit bzw. knappe 10MByte hin. Dabei macht es keinen Unterschied ob gute Intel-Netzwerkkarte, oder einfache OnBoard Netzwerkkarte des Fujitsu-Siemens Boards.

Die CPU Last hab ich da noch nicht weiter beachtet. Grund: Meine Tests haben nicht immer den gleichen Datensatz verschickt, sondern ich hab jedesmal einen neuen, zufälligen generiert: Hintergrund: der testansatz stammt noch aus Zeiten wo ich mit Object*Stream und Blocking-IO gearbeitet hatte. Da der Object*Stream Daten quasi "cached" hab ich jedesmal einen neuen Datensatz gebraucht um den Cache zu verscheissern 

Receive und Send-Buffer unterschiedlich groß machen: Leuchtet mir nicht ein. Wenn ein Paket mit Größe X verschickt wird, dann muss es auf beiden Seiten durch den Socket-Puffer durch. Und wenn man den Puffer jetzt unterschiedlich groß macht, dann wirkt sich das auch unterschiedlich aus. Während die eine Seite vllt. von dem größeren Puffer profitiert, wird die andere Seite durch den zu kleinen Puffer langsamer... 

Also wenn, dann würde ich beide gleich groß machen. Aber geändert hab ich den Default-Wert von 8k selten.

- Alex


----------



## Kr0e (4. Feb 2010)

Stimmt nicht! Das RcvWindow gibt an, wieviel geschrieben werden, OHNE das ein ACK zurück geschickt werden muss.
Gleiche Puffergrößen sind hinderlich.  Unter "TCP tuning" hab ich das gefunden. Naja warum be mir nicht 10 mb die sekunde verschickt werden, muss ich noch herausfinden. Vlt. wieder irgendwas ganz doofes. Aber das ein größerer Empfangspuffer sehr viel Sinn macht, habe ich durch den localhost test herausgefunden. Gerade bei vielen kleinen Datenpaketen (àla 128 byte) Habe ich meine Werte drastisch gesteigert, bei großen Paketen von 1mb jeweils waren es statt den 86 ca. 101mb/s. Probiers doch einfach mal aus... Vlt. kannste damit ja uach noch was rausholen...

PS: Per default ist nur der Sendepuffer bei Mina 8192 !! Der Empfangspuffer is per default 1024...

PSS: Nachtrag:
So, habe jetzt einige Tests durchführen können. Mal eben zu meinem OS. Ich benutze Windows XP SP2 + keine neuen updates.
Habe gelesen, dass eine lame Ethernet Verbindung nichu nbedingt an der Karte liegen muss, bzw. an dem ausführendem Programm.
Windows selbst könnte wohl das Problem sein, da die Standardwerte bei den Sockets wohl teilweise völlig mies sein können.
Von daher kann es ja sein (Keine Ahnugn was du jetzt für ein OS hast), dass bei dir die "Standardwerte" vom OS so gut waren, dass du garnicht optimieren musstest.

Bin einem Aritkel von IBM (WAr ne Supportseite für eines ihrer Produkte...) stand die Faustformel für die Empfangspuffergrößen bei Sockets. Transferspeed (bytes/second) / 3 = TcpRecvWindoeSize

Sprich bei 100mbit lan wären es knapp 3 mb Socketpuffer. Habe diesen Wert genutzt und erreiche damit 11,2 mb/s in meinem Lan. Laut IBM ist der Sendepuffer wohl nicht wirklcih relevant, er sollte nur erheblich kleiner sein als der Empfangspuffer. Sprich 32*1024 wäre z.b. ein guter Wert.  Habe genau diese Werte genommen und zum Vergleich Minas Standardwerte genommen (Oder sind das die OS Standardwerte ?) und der Unterschied war groß. OS Standard überträgt teilweise mit 88% Auslastung und manchmal mit 20%. Das macht Sinn, da die Anzahl der ACK-Befehle um einiges höher ist. Mit den "IBM" Werten erreiche ich konstante 95%. Ab und zu fällt sie mal auf 50 % ab gannz kurz aber am Ende hat man eine Linie und nicht, wie bei den Standardwerten, eine Berg und Talfahrt. Das ist  (Zumindest für mich) der klare Beweiß.

Ich habe diesen aufwendigen Test gemacht, weil selbst dein SIMON genau die gleichen Werte bei mir erzeugte. Da es bei dir und bei vielen Anderen wohl super läuft, konnte es nicht das Programm selber sein. Aber gut zu wissen, hat ja nicht jeder ein Up2Date Os 

Gruß,
Chris


----------



## tuxedo (4. Feb 2010)

Also die "tcp tuning" empfehlung die du da gefunden hast beisst sich ja ungemein mit dem, was MINA per default liefert (ganz dunkel erinner ich mich da an die 8k und 1k werte ... ).

Muss da mal in der MINA Mailingliste fragen wieso das so ist.

Zu meinem verwendeten OS:

Die letzten Tests hatte ich mit WinXP SP3 (32bit) auf nem DualCore 2,13Ghz und OpenSuSE (64bit) auf nem Quadcore 2,4Ghz gemacht. Beide waren da sehr ähnlich im Verhalten.

11.2MByte/Sek in einem 100Mbit lan .. Das ist schon ziemlich extrem hoch. Theoretisch kannst du 11,92MByte/Sek übertragen. Aber du musst noch den TCP/IP Overhead der sich auf (wenn ich mich vertan hab) 66 Byte pro Paket beläuft.

Geht man von 675Byte Paketgröße aus (das ist z.B. die Durchscnittliche Paketgröße die mein Root-Server den Tag über verwendet), dann kommt man auf exakt 10,86MByte/Sek Nettoübertragungsrate. 

Aber gut, das ist jetzt Erbsenzählerei. Bleiben wir mal bei "rund 10MByte pro Sek".

Hab so alles in allem das Gefühl dass du "irgendwie seltsam" messen tust. Aber nichts desto trotz: Rund 10MByte im 100Mbit LAN sind optimal. Da gibts nix dran auszusetzen. 
Nur hatte ich die auch ohne so krasse Änderungen der Puffergröße. Warum kann ich dir nicht sagen. kein Plan.

- Alex


----------



## Kr0e (4. Feb 2010)

Nein, diese super Werte kommen ja erst nachdem ich die Puffergrößen angepasst hab.
Vorher war ich bei 6,7 oder so.. Also zu wenig.

Ich habe eine direkte Verbindung zwischen 2 moderen PCs.
Es waren in der Tat ca. 11,17 mb/s. Ich messe auf altmodische Art. Zeit nehmen, 256 MB senden, Zeit nehmen und Datenmenge durch Zeitdifferenz/1000, das sollte schon stimmen. Meinet wegen bleiben wir bei 10 mb/s (Kann ja zufall gewesen sein).
Aber wie gesagt, diese hab ich zuvor ja überhaupt nicht erreicht...

PS: Wie gesagt, ich habe mehere Internetseiten gefunden, wo Leute dasselbe erzählen. Viele konnten das durch Anpassungen in der Win Reg lösen. Ich passe jetzt das Programm an, klappen tuts auch.


----------



## tuxedo (5. Feb 2010)

Ich sag ja nicht dass du unrecht mit deiner optimierung hast.

Ich sag nur dass ich mich wundere dass MINA das genau andersrum macht und ich, ohne spezialtuning mit "auf beiden Seiten 8k" auf meinem Testsystem auch an die 100Mbit grenzen gestoßen bin. Wieso das bei mir geklappt hat weiß ich nicht. Aber ich werd's rausfinden 

- Alex


----------



## Kr0e (5. Feb 2010)

Ich erzähle hier auch wirklich keinen Mist, ich hab mich selbst darüber gewundert.
Könnte evt. (um das bisschen anschaulicher zu machen) ein Video bei YT hochladen, das das alles etwas besser erklärt.
Und ich glaube die Standardwerte von Mina sind keine "Minawerte" sondern die OS Werte. Ein normaler Java Socket hat den selben Wert bei mir. Vermutlich werden die nativen OS BUffergrößen per default gewählt. Aber warum du mit 8k auf beiden Seiten so gute Werte erhälst, ist mir auch ein Rätsel. Werde auch weitere Tests (Auch mal unter Ubuntu...) durchführen und hier weiterhin veröffentlichen.

Gruß,
Chris


----------



## Kr0e (23. Feb 2010)

Ok, wie versprochen folgen nun meine Ergebnisse:

Also zuerst Mal ist zu sagen, dass das anpassen der Puffergrößen nur bei Windowssystemen einen Geschwindigkeitsvorteil bei mir gebracht hat. Bei Ubuntu 8.10 hab ich ohne anpassen der Puffergrößen zufriedenstellende Zahlen erhalten.

Noch was anderes, was mir aufgefallen ist. Ich glaube, dass Mina oder NIO im Allgemeinen etwas länger braucht als die klassischen Sockets. Vlt. wundert das ja auch nur mich, aber ich glaube fast, dass Mina und NIO bei z.B. einem Client bald langsamer ist, als java.io. Ich führe das auf den erhöhten Overhead zurück, der durch NIO ensteht (Channel registrieren, warten, agieren, erneut fürs schreiben oder lesen beim Selector registrieren.. usw...) Der Unterschied fiel jetzt nicht beim 100mbit Lan auf. Bei Verbindungen zu localhost dagegen sehr stark! java.io Sockets schaffen gut und gerne 150 mb/s. Mina schafft 70-85 bei mir maximal. Ich hab jetzt mal den Gedanken auf GBIT fortgesetzt. (Damit hab ich überhaupt keine Erfahrung... (Hatte noch nie die Möglichkeit mit Gbit lan zu testen.)) Köntne das ggf. bedeuten, dass Mina schon rein durch den Overhead garnicht in der Lage wäre gbit Lan auszunutzen ?
Vorrausgesetzt wir haben eine gute Hardware die das mitmachen würde ... Hast (Andere?) Erfahrung ? Also mit Mina und gbit. Würde mich mal interessieren... Ich hab jetzt einfach den Schluss gezogen, dass ich standard Sockets verwende. Mein Programm soll 1. nicht 1000 Clients bedienen (Jeder soll jeden bedienen->Sharing) und 2. erzeugt Mina in meinem Fall offensichtlich mehr Probleme als Lösungen... Vlt. bin ich auch nur zu doof zum Testen, aber ich hab aus meiner Sicht alles richtig gemacht. Keine await()'s mehr oder so. Wobei 70-85 ja auch nciht grad schlecht ist. Übrigens die Wert bezog sich jetzt nur auf Rohdaten. Es wird nicht auf die FFestplatte dabei zugegriffen...

Gruß,
Chris


----------



## tuxedo (23. Feb 2010)

zu der 70-80MB <-> 150MB Sache:

Naja. Einfach die Zahlen in den Raum werfen reicht da nicht um einen Vergleich zu ziehen.
Du kannst auch nicht eine komplexe MINA-Protokollumgebung mit einer minimalisten Java-IO-Socket-testanwendung vergleichen.

MINA hat seine stärken in der Abstraktionsschicht. Sie macht es einem einfach Daten zu kommunizieren und verschiedene Protokoll einzusetzen, bzw. aufeinander aufsetzen zu lassen.

Wenn du da statt NIO mal IO drunter schnallst wirst du vielleicht feststellen, dass IO genauso "einbricht" wie NIO.

Es gibt Zungen die behaupten dass NIO gegenüber IO keinen Vorteil bringt. Aber diese Zungen haben auch ein extra angepasstes Linux benutzt.

IO und NIO unterscheiden sich nicht in der "übertragungsgeschwindigkeit", sondern eher in der Art mit IO-Ereignissen umzugehen. NIO ist eben non-blocking, und IO ist blocking. Fertig.

Die ganze BUffer-Geschichte: Da lässt sich mit ein wenig Aufwand sicherlich mehr rausholen als mit byte[] Möglich wäre. Aber das sind in der Regel sonderspezialfälle. Denn oft hat man aus anderen Quellen ein byte[] das man übertragen muss. Das Umwandeln in ByteBuffer bringt keinen wirklichen Vorteil (IMHO).

Zu Übertragungsgeschwindigkeit allgemein:

Im 1000Mbit Netz lassen sich theoretisch um die 100MByte/sek übertragen. Da meist die Daten von einer Platte kommen, oder wieder auf eine Platte drauf müssen, ist  hier nicht das Netzwerk das Nadelöhr, sondern die Platten. Mein kleiner Mini-ITX Debian basierter NAS Server bringt rund 75MByte/sek (trotz Software-Raid) via 1000Mbt Netz zustande. Geht man davon aus dass deine Testanwendung "das Maß der Dinge in Sachen MINA" ist, dann passt das 

Auf der anderen Seite: Die wenigsten Server im Netz oder im (Firmen-)LAN sind mit Gigabit angebunden oder bedienen einzelne Clients mit 100% der 1000MBit. Wenn dann sind viele Clients dran die sich allesamt die 1000MBit teilen. Deshalb fällt auch hier die Übertragungsgeschwindigkeit (Nadelöhr Festplatte hin oder her) nicht ins Gewicht.

Das einzige das ich als "Problem" ansehen könnte wäre eine "Point to Point" Verbindung zwischen zwei Knoten im Netzwerk die die 1000MBit für sich haben. Aber da ist dann gleich die Frage: Braucht man da die skalierbarkeit von NIO? 

Selbst wenn man das mit JA beantworten kann: Ich denke MINA packt auch das, sofern man korrekt (will nicht sagen dass du stuff zusammengecodet hast) umgeht. 

Gruß
Alex


----------



## Kr0e (23. Feb 2010)

Ok, ich denke damit wären all meine Fragen geklärt. Danke für die Geduld. Gerade das mit "NIO nicht schneller als IO (Transport)" 
hab ich nicht gewusst. Hatte iwie im Kopf dass NIO auf die Daten generell schneller (Aus irgendeinem Grund) überträgt. Das der letztendliche Geschwindigkeitsvorteil (Sofern es überhaupt irgendeinen gibt) durch die bessere Skalierbarkeit kommt wusste ich schon, aber wie genau das zusammenhängt erst jetzt.

Da lag auch die gewisse "unlogig" gegraben. Du hattest ja schon angemerkt, dass es nciht sehr sinnvoll wäre, auf einem abstrakten Modell wiederum ein abstraktes Modell zu setzen. Genau das hab ich ja letztenendes getan. Für meine Anwendung hab ich garnicht die Fähigkeit mehrer oder besondere Protokolle zu implementieren, gebraucht. DAs machte mein Ansatz schon. Naja, ist wohl alles ne recht komplexe Sache mit vielen Tücken (Netzwerk, NIO, Mina). Vlt. wäre ein etwas schlankeres NIO Framework das Richtige für mich. Mina ist ja.. ^^ Seeehr umfangreich. Kennst du vlt. gerade eine gute aus Erfahrung ?

Gruß,
Chris


----------



## tuxedo (24. Feb 2010)

In der Theorie ist NIO wohl schneller als IO (Transport). Aber in der Praxis fällt das absolut nicht ins Gewicht fallen. In der Praxis zählt hier Hauptsächlich die Skalierbarkeit: NIO braucht einfach nicht so viele Threads wie IO und kann deshalb mehr Clients bedienen.

In deinem Fall wäre es wohl das beste gewesen deine Abstraktionsebene wegzulassen und hierfür MINA als Abstraktions-Layer einzusetzen. 

MINA an sich ist, so finde ich, ziemlich schlank. Zumindest so schlank als dass der Code-Overhead nicht sehr ins Gewicht fällt. MINA wird in vielen großen Projekten eingesetzt und hat sich da auch bestens bewährt. Wenn man die MINA-Mailingliste mitlilest stößt man immer wieder auf richtig große Client-Server konstellationen (Doppel-Quad-Core System mit 16GB Ram und so ...) die mit gigantisch viele Messages im Teils eigens dafür gebastelten Protokoll zurecht kommt. Ich staune als nicht schlecht wenn mir sowas über den Weg läuft 

Die MINA Webseite sagt selbst:



> Apache MINA is a network application framework which helps users develop high performance and high scalability network applications easily. It provides an abstract · event-driven · asynchronous API over various transports such as TCP/IP and UDP/IP via Java NIO.



Bei MINA geht's also nicht direkt darum einzelne bytes zu übertragen. MINA stellt viel eher ein umfangreiches Framework dar das einem die Arbeit erleichtert.

Wenn du NIO auf unterer Ebene benutzen willst und dich nicht mit den Tücken von NIO (die Selector-Sache ist ja nicht ganz so trivial ...) rumschlagen willst: Schau dir xSocket  an:



> Issues like low level NIO selector programming, connection pool management, connection timeout detection are encapsulated by xSocket.


----------



## Kr0e (24. Feb 2010)

In meinem Fall macht die abstrakte Ebene durchaus Sinn. xSockets bietet neben dem Standardprojekt auch was an, namens "Multiplexer API" oder so ähnlich. Meine Abstraktionsebene ist ziemlich genau sowas. Ich bau über eine beliebige Verbindung eine neue virtuelle Verbindung auf (Genannt bei mir Channel). Es können mehrere Channels über einen Multiplexer (TCP-Verbindung) geöffnet werden. Ich sah irgendwie keinen sinnvollen Weg das irgendwie mehr mit Mina zu machen als die bloße Transportebene.
Naja, danke für den Namen "xSockets". Die werde ich mir mal anschauen.

Gruß,
Chris


----------

