Forum: PC-Programmierung Cache hits der CPU optimieren


von Klaus H (Gast)


Lesenswert?

Hallo,
auf der Arbeit entwickele ich gerade ein System, das Produktregeln und 
Verfügbarkeiten checkt. Ich stehe noch am Anfang und probiere 
verschiedene Ansätze. Ziel ist es, 20000 Anfragen pro Sekunde von einem 
Server durchrechnen (plus Daten holen aus Redis) zu lassen, und das 
Ganze soll durch das Hinzufügen von Servern skalierbar sein. Ein Server 
hat 32 Kerne und 128 GB Arbeitsspeicher.

Programmiersprache ist Java, ich habe einen ersten Versuch (mit einem 
realistischen Beispielprojekt) mit dem vert.x Framework unternommen, da 
komme ich auf ~8000 Anfragen pro Sekunde. Problem bei vert.x ist, dass 
die einzelnen Verticles über den Eventbus kommunizieren, das bedeutet 
jedesmal Serialisierung und Deserialisierung. Also kann ich die 
Arbeitspakete nicht so feingranular machen, dass alle CPU Kerne benutzt 
werden.

Mein nächster Ansatz ist jetzt der Disruptor von lmax. Da fällt die 
Serialisierung weg, sollte also schneller sein. Nun liest man im 
Internet, dass das vor allem durch die vielen Cache Hits performant sein 
soll. Ich will also bei der Dimensionierung der RingBuffers prüfen, ob 
ich viele Cache-Hits habe.
Es gibt dafür ja verschiedene Tools (perf, valgrind, perfmon unter 
Windows, ...).

Nun meine Frage:
Wer von euch hat denn schon mal eine Anwendung bezüglich ihrer 
Cache-Hits analysiert? Mit welchem Tool? Welche Cache-Hit Ratio konntet 
ihr erreichen (ist natürlich von der Problemstellung abhängig, aber mich 
interessieren trotzdem die Werte aus der Praxis)? Und falls jemand ein 
Java-Programm analysiert hat: Gibt es da Fallstricke? Muss man den 
Just-In-Time Compiler ausschalten?
Postet doch mal bitte eure Erfahrungen!

Viele Grüße! Klaus

von (prx) A. K. (prx)


Lesenswert?

Klaus H schrieb:
> Wer von euch hat denn schon mal eine Anwendung bezüglich ihrer
> Cache-Hits analysiert? Mit welchem Tool?

Die Performance-Counter der CPU können bei der Analyse helfen. Zumal es 
bei Optimierung auf dieser Ebene noch andere Fallstricke gibt, als nur 
das Caching-Verhalten. So ist bei 32 Kernen und damit 64 parallelen 
Threads die Parallelisierung der Aufgabe von grosser Bedeutung. Aber da 
gibt es ein paar Fallen, die genau dies behindern können. Die 
Optimization Guides der CPU-Hersteller helfen etwas beim Verständnis, 
aber nicht alle Effekte sind dort klar beschrieben.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Klaus H schrieb:
> Muss man den Just-In-Time Compiler ausschalten?

Um auf ein völlig anderes Laufzeitverhalten zu optimieren?

von Klaus H (Gast)


Lesenswert?

Betreffend JIT-Compiler: Manche Tools bieten dir Rückschlüsse auf Teile 
des Quelltexts, die schlechte Lokalität haben. Das funktioniert dann 
angeblich nicht mehr, wenn sie die Instruktionen zur Laufzeit ändern.

Die von mir verwendeten Frameworks sehen einen Thread pro Kern des OS 
(also auch virtuelle Kerne) vor, es sollen also keine Threadwechsel 
stattfinden. Gegen Vert.x spricht, dass ich die Arbeit nicht so klein 
stückeln kann, dass alls Kerne etwas zu tun bekommen (denn dann ist der 
Serialisierungsaufwand größer als der Zeitgewinn durch 
Parallelisierung).

"Perf" arbeitet ja mit den Performance Countern. Ich denke mal, ein 
Server erreicht da andere Zahlen als z.B. ein Shell-Script. Deswegen 
interessieren mich da eure Werte, sonst würde ich einfach ein paar Shell 
Scripte analysieren.

von (prx) A. K. (prx)


Lesenswert?

Klaus H schrieb:
> Betreffend JIT-Compiler: Manche Tools bieten dir Rückschlüsse auf Teile
> des Quelltexts, die schlechte Lokalität haben. Das funktioniert dann
> angeblich nicht mehr, wenn sie die Instruktionen zur Laufzeit ändern.

JIT ja m.W. heisst nicht, dass der ursprüngliche Bytecode inplace 
gepatcht wird, sondern irgendwo im Heap neuer nativer Binärcode erzeugt 
wird. Der kann dann logischerweise ein recht anderes Verhalten haben.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Klaus H schrieb:
> Die von mir verwendeten Frameworks sehen einen Thread pro Kern des OS
> (also auch virtuelle Kerne) vor, es sollen also keine Threadwechsel
> stattfinden.

Konflikte zwischen Threads können nicht nur zwischen den Threads eines 
Cores auftreten, sondern auch zwischen verschiedenen Cores. Nur andere, 
d.h. dabei können systematische Cache-Konflikte auftreten.

Verticles/Eventbus/Disruptor/... kenne ich nicht. Entscheidend für 
solche Konflikte ist die Frage, wie die Threads auf gemeinsamen Speicher 
zugreifen. Da kann es schon vorkommen, dass gemeinsamer Speicher mehr 
kostet als das Threading bringt und eine Optimierung über TSX kann 
sinnvoll werden (wenn die CPU das unterstützt).

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Das könnte ein ähnliches Tool sein, wie ich es vor einiger Zeit mal bei 
AMD verwendet habe. Damit liessen sich Problemstellen aller Art bis auf 
einzelne Befehle runterbrechen:
https://software.intel.com/en-us/articles/intel-software-development-emulator

: Bearbeitet durch User
von Klaus H (Gast)


Lesenswert?

OK, danke für die Antworten, verstehe ich dich dann richtig, dass 
Optimierungsversuche ohne Analyse der Performance Counter sinnlos sind? 
Oder gibt es noch weitere Kennzahlen, auf die ich achten muss?

Zur Info: Der lmax Disruptor besteht aus einem Ringpuffer, und die 
einzelnen Threads arbeiten auf unterschiedlichen Positionen des Rings. 
Da die Daten an einem Index im Ring dann nacheinander von jedem Thread 
mal bearbeitet werden, soll laut Theorie ein gutes Cache-Verhalten 
erzielt werden.
Den Ring-Puffer kann man auf beliebige 2er-Potenzen dimensionieren. Je 
nachdem, wie groß die Daten im Puffer sind, wird das gut mit dem Cache 
harmonieren oder nicht (denke ich). Also im Idealfall passen die Daten 
in eine Cache-Line und der Ringpuffer kann groß dimensioniert werden (8 
MB L3-Cache / 32 Byte/CacheLine = 256 k Ringpuffer als Maximum). Wenn 
die Daten aber relativ groß sind, dann muss der Ringpuffer verkleinert 
werden.

Ich werde es wohl einfach ausprobieren müssen :-)

von Klaus H (Gast)


Lesenswert?

Sorry, hatte in Zwischenzeit deinen Post mit dem Link zu Intels Toos 
nicht gesehen. Danke, werde ich mal anschauen!

von (prx) A. K. (prx)


Lesenswert?

Klaus H schrieb:
> OK, danke für die Antworten, verstehe ich dich dann richtig, dass
> Optimierungsversuche ohne Analyse der Performance Counter sinnlos sind?

Es heisst, dass ich zu deiner Umgebung mit soundsoviel mir unbekannten 
Werkzeugen keinen zielgerichteten Kommentar abgeben kann. Wenn das Tools 
für parallele Programmierung sein sollten, dann wären spezielle Foren 
dafür der einzig sinnvolle Platz. So arg viele Mikrocontroller mit 32 
Threads gibt es noch nicht, und die einzigen dieser Art haben keine 
Caches.

> Zur Info: Der lmax Disruptor besteht aus einem Ringpuffer, und die
> einzelnen Threads arbeiten auf unterschiedlichen Positionen des Rings.
> Da die Daten an einem Index im Ring dann nacheinander von jedem Thread
> mal bearbeitet werden, soll laut Theorie ein gutes Cache-Verhalten
> erzielt werden.

Dummerweise haben solche Datenstrukturen mitunter einen Engpass: die 
Metadaten davon (Puffer-Index etc) und die entsprechende Absicherung per 
Mutex/Spinlock. Bei zeitlich dichtem Zugriff darauf kann der Vorteil der 
Parallelität im Cache-Pingpong verloren gehen. Genau das führte 
letztlich zur Entwicklung von TSX.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Die andere Frage ist ja, ob es da eine so gute Idee ist, eine die 
Hardware völlig abstrahierende Highlevelsprache wie Java zu nehmen. Wenn 
man sich über Dinge Gedanken macht, die nicht nur plattformspezifisch 
sind, sondern auch auf jeder CPU wieder anders laufen, dann widerspricht 
das doch schon der Grundidee von Java: write once, run everywhere. 
High-Performance-Hacks gehen damit nicht besonders gut, dazu ist Java 
auch nicht gedacht.

Da müßte man schon C nehmen, da hat man die völlige Controlle über das 
Speicherlayout. Allerdings wird bei beliebigem Multithreading auf 
skalierbar drölfzig Kernen die Anwendung dann niemals das 
Entwicklungsstadium verlassen.

Ich würd mir da weniger Gedanken um lowlevel-Optimierungen wie 
Cache-Hits machen, die sich sowieso mit jeder anderen CPU wieder anders 
benehmen, sondern um Optimierung auf Architektur-Ebene. Dafür ist Java 
doch da, da man sich nicht um den Lowlevelkram kümmert. Das bedeutet im 
Grundsatz schonmal das Benutzen möglichst weniger gemeinsamer 
Ressourcen. Algorithmische Optimierung schlägt Lowlevel-Optimierung 
ohnehin meistens.

von Klaus H (Gast)


Lesenswert?

@Nop:
Früher hätte ich dir da Recht gegeben, aber heute sehe ich das etwas 
anders. Natürlich ist es Unsinn, eine Java Businessanwendung genau auf 
eine Hardware zuzuschneiden und es ist durch die Sprache auch nicht 
möglich. Aber der Java-Code kann bestimmte Eigenschaften haben, die ihn 
auf heutiger Server-Hardware performant macht (Benutzung von möglichst 
vielen Kernen, wenige Locks, wenige Threadwechsel,...).

von Klaus H (Gast)


Angehängte Dateien:

Lesenswert?

Hier noch etwas Feedback zum Tool "Intel SDE":
Es gibt keine Schwierigkeiten, das zu Installieren. Laufen lässt man 
dann das Programm mit dem Befehl
sde -omix out.mix -ofootprint out.foot -- d:\dev\jdk8\jre\bin\java -jar 
d:\disruptor-1.0-SNAPSHOT.jar

Wichtig ist, den Pfad zu java absolut anzugeben, das SDE ignoriert den 
PATH und findet dann evtl. ein JRE in der falschen Version.

Die Anwendung muss terminieren, damit das tool seine Statistiken 
schreibt. Also beendet sich mein Projekt nach 20 Requests selbst mit 
System.exit(0).

Das ganze ist sehr sehr sehr langsam, erstens wegen der Instrumentierung 
und zweitens kann SDE auch Instuktionen der CPU simulieren, man kann dem 
Programm damit vorgaukeln, es würde auf einer moderneren CPU laufen.

Der Footprint enthält Informationen über Caches, aber da ist das 
Linux-Tool perf schon etwas gesprächiger. Ich hänge mal einen footprint 
an diesen Beitrag an, falls es jemanden interessiert.

Für die Analyse von Java-Applikationen hat Intel noch eine interessante 
Homepage:
https://software.intel.com/en-us/articles/java-applications-require-multi-level-analysis

von Stefan F. (Gast)


Lesenswert?

Besorge Dir mal das Buch "Java Performance: The Definitive Guide" von 
Scott Oaks. Es erklärt viele Fallstricke, Dinge die man eventuell besser 
machen kann und alle Tuning Parameter der JVM. Es erklärt auch, wie man 
die Peformance von Java Programmen mit Java, Profiler und Betriebsystem 
Mitteln analysiert.

Und besorge Dir das Buch "Effective Java" von Joshua Bloch. Auch dieses 
Buch erklärt einige Dinge, die sich performance verbessernd auswirken.

von Markus L. (rollerblade)


Lesenswert?

A. K. schrieb:
> Wenn das Tools
> für parallele Programmierung sein sollten, dann wären spezielle Foren
> dafür der einzig sinnvolle Platz. So arg viele Mikrocontroller mit 32
> Threads gibt es noch nicht, und die einzigen dieser Art haben keine
> Caches.
Du bist im falschen Forum: "Programmierung auf PCs, Algorithmen, 
allgemeine Programmierfragen ohne direkten Mikrocontroller-Bezug."

von (prx) A. K. (prx)


Lesenswert?

Markus L. schrieb:
> Du bist im falschen Forum: "Programmierung auf PCs, Algorithmen,
> allgemeine Programmierfragen ohne direkten Mikrocontroller-Bezug."

Ist mir klar. Dennoch gehe ich davon aus, dass solche Fragen besser dort 
aufgehoben sind, wo man Werkzeuge und Programmiertechnik für hohen 
Parallelisierungsgrad zum Thema hat.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Klaus H schrieb:
> Aber der Java-Code kann bestimmte Eigenschaften haben, die ihn
> auf heutiger Server-Hardware performant macht (Benutzung von möglichst
> vielen Kernen, wenige Locks, wenige Threadwechsel,...).

Ja, das meinte ich mit meinem dritten Absatz, mit Optimierung auf 
Architekturebene. Da stimme ich Dir auch zu z.B. mit der Minimierung von 
Locks, weswegen ich ja anregte, möglichst wenige gemeinsame Ressourcen 
zu nutzen. Diese machen ja Locks überhaupt erst nötig.

von Klaus H (Gast)


Lesenswert?

Und Architektur ist mal wieder nicht genau definiert. Für mich ist es 
schon eine Optimierung auf Architekturebene, wenn ich anderen 
Entwicklern sagen kann:
"Unsere Logik teilen wir in Arbeitspakete auf, die jeweils max. eine I/O 
Operation machen. Diese Arbeitspakete werden dann in einer Reihenfolge 
(oder bei parallelen Paketen auch Reihenfolgenagnostisch) als Consumer 
an den Ringpuffer gehängt. Und es gibt nur einen Producer".

Auf jeden Fall vielen Dank für alle bisherigen Antworten, das hilft mir 
schon beim Durchdenken weiter. Wenn ich die Analyse mit "perf" mal 
gemacht habe gebe ich hier nochmal Feedback.

von --- (Gast)


Lesenswert?

Das einfachste wäre natürlich eine CPU mit mehr Cache zu nehmen.
SPARC M7 hat 32 Cores und 256 Threads.
Und nicht dieses lächerliche Intel-Gehampel.

von Klaus H (Gast)


Lesenswert?

Vielleicht ist hier jemand an Feedback interessiert:

Ich habe als Ausgangsbasis Benchmarks mit JMH (openJDK) erstellt. 
Zunächst sollte man den "comp" JustInTimeCompiler-Profiler aktivieren, 
und die Warmup-Interationen solange erhöhen, bis nach dem Warmup kein 
Code mehr kompiliert wird.

In meinem Fall gab es dann noch deutliche Schwankungen des Durchsatzes 
der einzelnen Iterationen. Deshalb habe ich den Speicher für die JVM 
begrenzt (von 1 GB runter auf 320 MB). Dann läuft die GarbageCollection 
häufiger, aber schneller. Auf dafür bringt JMH einen Profiler mit.

Am Ende hat man dann ein Artefakt, das Vergleichsmessungen ermöglicht. 
An dem Punkt stehe ich jetzt, jetzt muss ich die Kollegen vom Build 
überreden, dass sie die JMH-Tests über ein Jenkins-Plugin ausführen. Und 
ich werde mit dem linux-Perf die CacheHits untersuchen.

Siehe auch http://www.brendangregg.com/linuxperf.html

von TestX (Gast)


Lesenswert?

@Klaus

Dein optimierungsansatz ist zwar löblich, allerdings wird man in der 
Praxis einfach zwei server nehmen um den Durchsatz zu erreichen. Viel 
wichtiger als die Geschwindigkeit ist die Stabilität bei solchen 
geschäftskritischen Anwendungen.
Überleg dir daher ob es sich überhaupt lohnt an der Stelle 
weiterzumachen oder ob man eher an der globalen Architektur arbeiten 
sollte um es effizienter zu bekommen..

von L1-Cache (Gast)


Lesenswert?

Ist das nicht so ein Thema mit Java und Cache Misses? Da man in Java 
prinzipiell alles mit Referenzen zu pflastert (gibt ja keine Möglichkeit 
eigene Wertetypen zu definieren), liegen die Speicherbereiche weiter 
auseinander.

von Sheeva P. (sheevaplug)


Lesenswert?

Klaus H schrieb:
> Programmiersprache ist Java, ich habe einen ersten Versuch (mit einem
> realistischen Beispielprojekt) mit dem vert.x Framework unternommen, da
> komme ich auf ~8000 Anfragen pro Sekunde. Problem bei vert.x ist, dass
> die einzelnen Verticles über den Eventbus kommunizieren, das bedeutet
> jedesmal Serialisierung und Deserialisierung. Also kann ich die
> Arbeitspakete nicht so feingranular machen, dass alle CPU Kerne benutzt
> werden.

Was sagen denn die klassischen Systemwerkzeuge {io,vm,mp}stat, top und 
SAR?

von (prx) A. K. (prx)


Lesenswert?

L1-Cache schrieb:
> Ist das nicht so ein Thema mit Java und Cache Misses? Da man in Java
> prinzipiell alles mit Referenzen zu pflastert (gibt ja keine Möglichkeit
> eigene Wertetypen zu definieren), liegen die Speicherbereiche weiter
> auseinander.

Ist nur ein Thema, wenn die referenzierten Daten wesentlich kleiner als 
eine Cache-Line sind (64 Bytes), und deshalb eine Art Fragmentierung 
auftritt. Bei Cache-Lines selbst ist unerheblich, ob die nebeneinander 
liegen, oder weit entfernt.

von Gerd E. (robberknight)


Lesenswert?

Klaus H schrieb:
> auf der Arbeit entwickele ich gerade ein System, das Produktregeln und
> Verfügbarkeiten checkt. Ich stehe noch am Anfang und probiere
> verschiedene Ansätze. Ziel ist es, 20000 Anfragen pro Sekunde von einem
> Server durchrechnen (plus Daten holen aus Redis) zu lassen

wäre es nicht auch ein Ansatz, diese Produktregeln und Verfügbarkeiten 
zumindest teilweise vorzuberechnen und in einer Datenbank abzulegen? 
Gerade wenn sich die Daten nur seltener ändern, dafür aber oft abgefragt 
werden, ist sowas oft eine effiziente Möglichkeit die Abfragen zu 
beschleunigen.

von Klaus H. (Gast)


Lesenswert?

Toll, dass hier noch ein paar Beiträge kommen! Die Vorberechnung von 
Produktregeln ist keine Option, die Daten müssen mindestens tagesaktuell 
sein. Da sich durch Kombinatorik so viele Möglichkeiten ergeben, müßte 
die Vorberechnung so schnell sein, dass das keinen Vorteil gegenüber 
einer Live-Berechnung bietet.
Zum Thema Cache-Misses und Java: Durch die Referenzen liegen die Objekte 
verstreut im Speicher. Für die Cache-Lines ist das, wie A.K. schon 
gesagt hat, egal. Aber für die Cache-Lokalität ist es besser, wenn die 
Objekte nah beieinanderliegen, weil die CPU dann schon spekulativ 
angrenzende Speicherbereiche in den Cache legt. Dafür gibt es meines 
Wissens 2 Optimierungsmöglichkeiten:
- Arbeiten mit binären Arrays, in denen die Daten stecken. Um die Arrays 
kann man sich noch Wrapper mit Getter/Setter legen.
- Einmalige Erzeugung von Objekten in einer Schleife. Ohne Garantie kann 
man darauf hoffen, dass so erzeugte Objekte nah beieinander im Speicher 
liegen. Die zu verarbeitenden Daten werden dann in Objekte aus diesem 
Pool kopiert.
Ist halt immer ein Kompromiss :-). Deshalb will ich erstmal messen, ob 
meine Anwendung beim Caching Performance verschenkt, bevor ich etwas 
optimiere.

@TestX:
ja, es werden im Moment schon 2 Server eingesetzt, und es kann weiter 
skaliert werden. Aber was sind schon die Kosten eines Entwicklers gegen 
Hardware und nötige Lizenzen. Vor allem, wenn die Firma noch im 
Budgetdenken feststeckt und dann die nötige Hardware erst in einem Jahr 
beschafft werden kann.

von hansel (Gast)


Lesenswert?

Klaus H. schrieb:
> Aber was sind schon die Kosten eines Entwicklers gegen
> Hardware und nötige Lizenzen.

Machst du Witze?

von Gerd E. (robberknight)


Lesenswert?

Klaus H. schrieb:
> Die Vorberechnung von
> Produktregeln ist keine Option, die Daten müssen mindestens tagesaktuell
> sein. Da sich durch Kombinatorik so viele Möglichkeiten ergeben, müßte
> die Vorberechnung so schnell sein, dass das keinen Vorteil gegenüber
> einer Live-Berechnung bietet.

Ja, komplett vorberechnen funktioniert meist nicht.

Aber kann man nicht evtl. teilweise vorberechnen? Also z.B. Gruppen 
bilden und dann deren Teilergebnisse vorberechnen und speichern. Dann 
muss die Live-Abfrage nur noch die vorberechneten Gruppen abfragen und 
kombinieren anstatt immer von Adam und Eva an neu zu berechnen.

> ja, es werden im Moment schon 2 Server eingesetzt, und es kann weiter
> skaliert werden. Aber was sind schon die Kosten eines Entwicklers gegen
> Hardware und nötige Lizenzen. Vor allem, wenn die Firma noch im
> Budgetdenken feststeckt und dann die nötige Hardware erst in einem Jahr
> beschafft werden kann.

Das wäre mal ne Aufgabe die Du an Deinen Chef vergeben kannst: hier die 
Strukturen anpassen und auf die Höhe der Zeit bringen.

Dynamisch Rechenleistung bei Bedarf anzufordern (entweder aus dem 
eigenen RZ oder zugemietet bei Dienstleistern) und zuzuschalten ist da 
eigentlich Stand der Technik (und deren Organisation). Nen Jahr für die 
Entscheidung für nen neuen Rechner zu brauchen war schon im letzten 
Jahrzehnt nicht mehr zeitgemäß.

von Klaus H. (Gast)


Lesenswert?

Hier nochmal für Interessierte ein Link auf eine Klasse, die "etwas" 
optimiert wurde. Ist auch gut kommentiert, warum das so gemacht wurde.
http://hg.openjdk.java.net/code-tools/jmh/file/cde312963a3d/jmh-core/src/main/java/org/openjdk/jmh/logic/BlackHole.java

von Sheeva P. (sheevaplug)


Lesenswert?

Klaus H schrieb:
> auf der Arbeit entwickele ich gerade ein System, das Produktregeln und
> Verfügbarkeiten checkt. Ich stehe noch am Anfang und probiere
> verschiedene Ansätze. Ziel ist es, 20000 Anfragen pro Sekunde von einem
> Server durchrechnen (plus Daten holen aus Redis) zu lassen, und das
> Ganze soll durch das Hinzufügen von Servern skalierbar sein.

Vielleicht liege ich falsch, aber nach dem, was ich auf die Schnelle 
über vert.x und LMAX' Disruptor gelesen habe, dienen diese beiden 
Werkzeuge der Kommunikation zwischen verschiedenen Threads und 
Prozessen. Leider weiß ich zu wenig über Deinen Anwendungsfall, aber 
möglicherweise könnten Ansätze aus dem Distributed Computing wie zum 
Beispiel Apache Spark oder Apache Storm die Forderung nach horizontaler 
Skalierung besser erfüllen.

von Klaus H. (Gast)


Lesenswert?

Die horizontale Skalierung läuft über Redis. Die Arbeitspakete werden in 
eine Redis-Queue gePUSHt (blödes Wort), und und somit können beliebig 
viele Instanzen, die nichts voneinander wissen, die Arbeitspakete 
abarbeiten. Hat sich voll bewährt, Redis ist sehr schnell 
(Zugriffszeiten bei Datenpakenten von 1 kB: < 1ms), nur bei der 
Serialisierung/Deserialisierung muss man sich etwas Gedanken machen.
Wenn die Redis-Performance nicht mehr ausreicht, dann kann die Queue 
immer noch nach Produkttyp auf mehrere Redis-Instanzen verteilt werden.

Ich weiß nicht, ob hier im Forum noch mehr Diskussion gewünscht wird, 
denn im Moment habe ich keine Fragen mehr. Ich kann halt, falls 
gewünscht, Erfahrungen posten:

Der LMAX Disruptor funktioniert analog zu einer CPU-Pipeline. Ich teile 
mein Arbeitspaket in mehrere Teilschritte auf, und jedes Element im 
Ringpuffer durchläuft dann jeden Teilschritt (voneinander unabhängige 
Teilschritte können auch Out-of-Order durchlaufen werden). Wie bei einer 
Pipeline bestimmt dann der längste Teilschritt den "Takt". Also muss 
immer wieder der längste Teilschritt bestimmt werden, und dann entweder 
optimiert oder weiter aufgeteilt werden (wobei die max. Anzahl der 
Teilschritte wiederum durch die Anzahl der CPUs begrenzt wird).
Mein längster Teilschritt (mit I/O über einen lokalen TCP-Socket) liegt 
im Moment bei 900 ns, die kürzesten bei ~100 ns. Mein Ziel ist es, dass 
die Teilschritte < 500 ns dauern. Dazu habe ich auch ein paar Ideen...

von Sheeva P. (sheevaplug)


Lesenswert?

Klaus H. schrieb:
> Die horizontale Skalierung läuft über Redis.

Klar, aber das betrifft ja nur den Speicher. Meine Idee zielte eher 
darauf ab, auch die CPU-Last horizontal zu skalieren.

Welchen Redis-Client benutzt Du denn in Java?

von Klaus H. (Gast)


Lesenswert?

Ich benutze Jedis.
Die Arbeitspakete liegen in einer Redis-Queue, und an diese Queue kann 
man dann beliebig viele Instanzen hängen, die die Arbeitspakete 
abarbeiten. Ist es das, was du mit "horizontaler Skalierung" meinst?
Die Consumer am Ringpuffer laufen jeweils in einem eigenen Thread, damit 
wird pro Teilschritt eine CPU verwendet. Wenn es keinen Stau im 
Ringpuffer gibt, als die Teilschritte gleich schnell sind, dann ergibt 
sich eine optimale CPU-Auslastung. Nehmen wir an, der Prozess braucht 8 
CPUs, und der Server hat 32. Dann werden mit Hilfe von Docker 2 
Container deployed, die zusammen 16 CPUs belegen. Oder, je nach Last, 
können auch 4 Container deployed werden. Oder auch 8 Container auf 2 
Servern.

von Larry (Gast)


Lesenswert?

--- schrieb:
> Das einfachste wäre natürlich eine CPU mit mehr Cache zu nehmen.
> SPARC M7 hat 32 Cores und 256 Threads.
> Und nicht dieses lächerliche Intel-Gehampel.

Je größer der Cache, desto langsamer der Cache.

Je länger der Tag, desto mehr erzählt Larry.

von (prx) A. K. (prx)


Lesenswert?

Larry schrieb:
>> Und nicht dieses lächerliche Intel-Gehampel.
>
> Je größer der Cache, desto langsamer der Cache.

Unabhängig vom Wahrheitsgehalt dieser Aussage:

SPARC M7: 16KB L1 Cache pro Core
Intel:    64KB L1 Cache pro Core

von Sheeva P. (sheevaplug)


Lesenswert?

Klaus H. schrieb:
> Ich benutze Jedis.
> Die Arbeitspakete liegen in einer Redis-Queue, und an diese Queue kann
> man dann beliebig viele Instanzen hängen, die die Arbeitspakete
> abarbeiten.

Du meinst eine Redis "List", an die ein Provider mit RPUSH Elemente 
anhängt, die Deine Consumer sich dann mit BLPOP abholen?

> Ist es das, was du mit "horizontaler Skalierung" meinst?

Mit "horizontaler Skalierung" meine ich eine Skalierung, die über die 
Anzahl der Maschinen skaliert, im Gegensatz zu einer vertikalen 
Skalierung, welche über den Austausch von vorhandenen über größere 
Maschinen skaliert.

> Die Consumer am Ringpuffer laufen jeweils in einem eigenen Thread, damit
> wird pro Teilschritt eine CPU verwendet.

Mir ist noch nicht ganz klar, wozu Du den Ringpuffer benötigst.

von Klaus H. (Gast)


Lesenswert?

Technisch gesehen eine Redis List, aber nach dem "Reliable Queue" 
Pattern von Redis:
https://redis.io/commands/rpoplpush


Der Ring-Puffer soll Objekte, die "nebeneinander" im Speicher liegen, 
beinhalten. Das begint, dass nicht jedes Mal ein neues Objekt erzeugt 
wird, sondern bestehende werden wiederverwendet. Da bietet sich ein 
Ringpuffer an.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.