{"id":35851,"date":"2019-10-31T22:07:03","date_gmt":"2019-10-31T19:07:03","guid":{"rendered":"https:\/\/prohoster.info\/blog\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java\/"},"modified":"2019-10-31T22:07:03","modified_gmt":"2019-10-31T19:07:03","slug":"bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java","status":"publish","type":"post","link":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java","title":{"rendered":"Ein ausf\u00fchrliches Interview mit Cliff Click \u2013 dem Vater der JIT-Kompilierung in Java","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Ein ausf\u00fchrliches Interview mit Cliff Click \u2013 dem Vater der JIT-Kompilierung in Java\" src=\"\/wp-content\/uploads\/2019\/07\/9ea9740ef74c0ae14d334482af115222.png\" style=\"display:block;margin: 0 auto;\" \/><strong>Cliff Click<\/strong> \u2013 CTO von Cratus (IoT-Sensoren zur Verbesserung von Prozessen), Gr\u00fcnder und Mitgr\u00fcnder mehrerer Startups (darunter Rocket Realtime School, Neurensic und H2O.ai) mit mehreren erfolgreichen Exits. Cliff schrieb mit 15 Jahren seinen ersten Compiler (Pascal f\u00fcr den TRS Z-80)! Er ist am bekanntesten f\u00fcr seine Arbeit an C2 in Java (dem Sea of Nodes IR). Dieser Compiler zeigte der Welt, dass JIT hochwertigen Code erzeugen kann, was zu einem der Faktoren wurde, die Java zu einer der Hauptplattformen der modernen Software machten. Anschlie\u00dfend half Cliff dem Unternehmen Azul Systems, einen 864-kernigen Mainframe mit Software in reinem Java zu entwickeln, der GC-Pausen auf einem 500-Gigabyte-Haufen innerhalb von 10 Millisekunden unterst\u00fctzte. Insgesamt hat Cliff in allen Aspekten der JVM gearbeitet.<br clear=\"all\"><br \/>\n\u00a0<br \/>\nDieser Hub-Post ist ein ausf\u00fchrliches Interview mit Cliff. Wir werden die folgenden Themen besprechen:<\/p>\n<p><\/p>\n<ul>\n<li>\u00dcbergang zu Low-Level-Optimierungen<\/li>\n<li>Wie man eine gro\u00dfe Umstrukturierung vornimmt<\/li>\n<li>Kostenmodell<\/li>\n<li>Einf\u00fchrung in Low-Level-Optimierungen<\/li>\n<li>Praktische Beispiele zur Verbesserung der Leistung<\/li>\n<li>Warum man eine eigene Programmier sprache erstellen sollte<\/li>\n<li>Karriere als Performance-Ingenieur<\/li>\n<li>Technische Herausforderungen<\/li>\n<li>Ein bisschen \u00fcber Registerzuweisung und Multikerntechnik<\/li>\n<li>Die gr\u00f6\u00dfte Herausforderung im Leben<\/li>\n<\/ul>\n<p><\/p>\n<p>Interviewer:<\/p>\n<p><\/p>\n<ul>\n<li><strong>Andrei Satarygin<\/strong> von Amazon Web Services. In seiner Karriere hat er an ganz unterschiedlichen Projekten gearbeitet: Er testete die verteilte NewSQL-Datenbank bei Yandex, ein Cloud-Detektionssystem im Kaspersky-Labor, ein Mehrspieler-Spiel bei Mail.ru und einen W\u00e4hrungsberechnungsdienst bei der Deutschen Bank. Er interessiert sich f\u00fcr die Testung gro\u00dfangelegter Backend- und verteilter Systeme.<\/li>\n<li><strong>Wladimir Sitnikow<\/strong> von Netcracker. Seit zehn Jahren arbeitet er an der Leistungsf\u00e4higkeit und Skalierbarkeit des NetCracker OS \u2013 der Software, die von Telekommunikationsanbietern zur Automatisierung von Netzwerkmanagementprozessen und Netzwerkhardware verwendet wird. Er ist fasziniert von Fragen zur Java-Leistung und Oracle Database. Autor von mehr als einem Dutzend Leistungsverbesserungen im offiziellen PostgreSQL JDBC-Treiber.<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"perehod-k-nizkourovnevym-optimizaciyam\">\u00dcbergang zu Low-Level-Optimierungen<\/h1>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Sie sind eine bekannte Pers\u00f6nlichkeit in der Welt der JIT-Compilation, in Java und allgemein in der Leistungsverbesserung, richtig?\u00a0<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Ganz genau!<\/p>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Lassen Sie uns mit allgemeinen Fragen zur Leistungsoptimierung beginnen. Was halten Sie von der Wahl zwischen High-Level- und Low-Level-Optimierungen, wie zum Beispiel der Arbeit auf CPU-Ebene?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Hier ist alles ganz einfach. Der schnellste Code ist derjenige, der nie ausgef\u00fchrt wird. Deshalb sollte man immer mit einem hohen Niveau beginnen und an den Algorithmen arbeiten. Eine bessere O-Notation wird eine schlechtere \u00fcbertreffen, es sei denn, es kommen einige ausreichend gro\u00dfe Konstanten ins Spiel. Niedrigstufige Dinge kommen am Schluss. Normalerweise, wenn man den gesamten Rest des Stacks gut genug optimiert hat und immer noch etwas Interessantes \u00fcbrig bleibt \u2013 das ist der niedrigere Level. Aber wie f\u00e4ngt man auf hohem Niveau an? Wie wei\u00df man, dass gen\u00fcgend Arbeit auf hohem Niveau geleistet wurde? Nun... es gibt keine fertigen Rezepte. Man muss das Problem verstehen, entscheiden, was man tun m\u00f6chte (damit man keine unn\u00f6tigen Schritte unternehmen muss) und dann den Profiler einsetzen, der n\u00fctzliche Informationen liefern kann. Irgendwann merkt man selbst, dass man unn\u00f6tige Dinge losgeworden ist und es an der Zeit ist, sich um die Feinabstimmung auf niedrigem Niveau zu k\u00fcmmern. Das ist eindeutig eine besondere Art von Kunst. Viele Menschen machen unn\u00f6tige Dinge, bewegen sich aber so schnell, dass sie keine Zeit f\u00fcr Performance-Optimierung haben. Aber das bleibt so, bis die Frage dr\u00e4ngend wird. In der Regel interessiert es 99 % der Zeit niemanden, was ich mache, bis zum Zeitpunkt, wo auf dem kritischen Pfad eine wichtige Sache auftritt, die jemandem wichtig ist. Und dann fangen alle an, dich zu fragen, warum es von Anfang an nicht perfekt funktioniert hat. Generell gibt es immer etwas zu verbessern, wenn es um Performance geht. Aber 99 % der Zeit fehlen dir die Ansatzpunkte! Du versuchst einfach, dass etwas funktioniert, und w\u00e4hrenddessen verstehst du, was wichtig ist. Man kann nie im Voraus wissen, dass dieses spezifische St\u00fcck perfekt sein muss, also muss man im Grunde in allem perfekt sein. Und das ist unm\u00f6glich und so macht man es nicht. Es gibt immer eine Menge Dinge zu reparieren \u2013 und das ist vollkommen normal.<\/p>\n<p><\/p>\n<h1 id=\"kak-delat-bolshoy-refaktoring\">Wie man eine gro\u00dfe Umstrukturierung vornimmt<\/h1>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Wie arbeiten Sie an der Performance? Das ist schlie\u00dflich ein durchg\u00e4ngiges Problem. Hatten Sie zum Beispiel schon einmal mit Herausforderungen zu k\u00e4mpfen, die durch die Interaktion mit einer Vielzahl bereits bestehender Funktionen entstanden sind?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Ich versuche, das zu vermeiden. Wenn ich wei\u00df, dass die Leistung ein Problem werden k\u00f6nnte, denke ich schon nach, bevor ich mit dem Programmieren beginne, insbesondere bei den Datenstrukturen. Aber oft erkennt man all das erst viel sp\u00e4ter. Und dann muss man extremere Ma\u00dfnahmen ergreifen und das tun, was ich \u201eSchreiben und Herrschen\u201c nenne: Man muss einen ausreichend gro\u00dfen Abschnitt erfassen. Teile des Codes m\u00fcssen ohnehin aufgrund von Performance-Problemen oder aus anderen Gr\u00fcnden neu geschrieben werden. Welche auch immer der Anlass f\u00fcr das Neuschreiben des Codes ist, es ist fast immer besser, einen gr\u00f6\u00dferen Abschnitt als einen kleineren zu \u00fcberarbeiten. In diesem Moment geraten alle in Panik: \u201eOh Gott, man kann nicht so viel Code anfassen!\u201c Aber in der Tat funktioniert dieser Ansatz fast immer viel besser. Man sollte sich sofort dem gr\u00f6\u00dferen Problem widmen, einen gro\u00dfen Kreis darum ziehen und sagen: Alles, was innerhalb dieses Kreises ist, werde ich neu schreiben. Die Grenze ist schlie\u00dflich viel kleiner als der Inhalt innerhalb, der ersetzt werden soll. Und wenn eine solche Abgrenzung es erm\u00f6glicht, die Arbeit im Inneren perfekt zu gestalten \u2013 hast du freie Hand, mach, was du willst. Sobald du das Problem verstanden hast, verl\u00e4uft der Neuschreibungsprozess viel einfacher, also bei\u00dfe in einen gro\u00dfen Abschnitt!<br \/>\nW\u00e4hrend man an gr\u00f6\u00dferen Umstellungen arbeitet und erkennt, dass die Performance ein Problem werden k\u00f6nnte, beginnt man oft, sich dar\u00fcber Gedanken zu machen. Das f\u00fchrt h\u00e4ufig zu einfachen \u00dcberlegungen wie: \"Kopiere keine Daten, sondern verwalte sie so effizient wie m\u00f6glich, halte sie klein.\" Bei umfangreichen Anpassungen gibt es bew\u00e4hrte Methoden zur Leistungsverbesserung, die in der Regel immer um die Datenorganisation kreisen.<\/p>\n<p><\/p>\n<h1 id=\"model-stoimosti\">Kostenmodell<\/h1>\n<p><\/p>\n<p><strong>Andrei<\/strong>: In einem Ihrer Podcasts haben Sie \u00fcber Preismodelle im Kontext der Performance gesprochen. K\u00f6nnen Sie erl\u00e4utern, was Sie damit gemeint haben?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Nat\u00fcrlich. Ich bin in einer Zeit geboren, in der die Prozessorleistung von entscheidender Bedeutung war. Und diese \u00c4ra kehrt zur\u00fcck \u2013 das Schicksal ist nicht ohne Ironie. Ich habe in einer Zeit von 8-Bit-Maschinen gelebt, mein erster Computer hatte 256 Byte. Genau, Byte. Alles war sehr klein. Man musste die Anweisungen z\u00e4hlen, und je mehr wir in der Programmierung vorankamen, desto mehr \u00fcbernahmen die Sprachen. Zuerst gab es Assembler, dann Basic, dann C, und C \u00fcbernahm viele Details wie das Register-Management und die Anweisungsauswahl. Aber es war alles recht klar, und wenn ich einen Zeiger auf eine Variable machte, bekam ich ein Load, und die Kosten dieser Anweisung sind bekannt. Die Hardware gibt eine bekannte Anzahl von Maschinenzyklen aus, sodass die Ausf\u00fchrungsgeschwindigkeit verschiedener Operationen einfach berechnet werden kann, indem man alle Anweisungen, die man ausf\u00fchren m\u00f6chte, zusammenz\u00e4hlt. Jedes Compare\/Test\/Branch\/Call\/Load\/Store konnte summiert werden, und man konnte sagen: Hier ist die Ausf\u00fchrungszeit. Wenn man die Performance verbessert, f\u00e4llt einem sicherlich auf, welche Zahlen den kleinen hei\u00dfen Zyklen entsprechen.\u00a0<br \/>\nSobald Sie jedoch auf Java, Python und \u00e4hnliche Sprachen umschalten, entfernen Sie sich schnell von der Hardware auf niedriger Ebene. Wie hoch sind die Kosten f\u00fcr den Aufruf eines Getters in Java? Wenn der JIT im HotSpot es korrekt optimiert hat, <noindex><a rel=\"nofollow\" href=\"https:\/\/wiki.openjdk.java.net\/display\/HotSpot\/Inlining\">wird dies inline<\/a><\/noindex>, aber wenn dies nicht der Fall ist, wird es zu einem Funktionsaufruf. Da sich der Aufruf in einer hei\u00dfen Schleife befindet, hebt dies alle anderen Optimierungen in dieser Schleife auf. Daher werden die tats\u00e4chlichen Kosten viel h\u00f6her sein. Sie verlieren sofort die F\u00e4higkeit, einen Codeabschnitt zu betrachten und zu verstehen, was es uns kosten wird, dies in Bezug auf CPU-Taktfrequenz, verwendeten Speicher und Cache auszuf\u00fchren. All dies wird nur interessant, wenn man wirklich in die Performance eintaucht.<br \/>\nWir befinden uns jetzt in einer Situation, in der die Taktraten der Prozessoren seit einem Jahrzehnt kaum gewachsen sind. Die alten Zeiten kehren zur\u00fcck! Sie k\u00f6nnen nicht mehr auf eine gute Einzelthread-Leistung z\u00e4hlen. Aber wenn es um paralleles Rechnen geht \u2013 das ist unglaublich komplex, und alle schauen Sie an, als w\u00e4ren Sie James Bond. Zehnfache Beschleunigungen treten hier normalerweise dort auf, wo jemand etwas \u00fcbersehen hat. Parallelit\u00e4t erfordert viel Arbeit. Um diese zehnfache Beschleunigung zu erreichen, ist es wichtig, das Kostensystem zu verstehen. Was kostet was? Um das zu begreifen, m\u00fcssen Sie verstehen, wie die Programmiersprache auf die zugrunde liegende Hardware wirkt.<br \/>\nMartin Thompson hat ein gro\u00dfartiges Wort f\u00fcr seinen Blog gew\u00e4hlt <noindex><a rel=\"nofollow\" href=\"https:\/\/mechanical-sympathy.blogspot.com\/\">Mechanische Sympathie<\/a><\/noindex>! Es ist wichtig zu verstehen, was die Hardware tun wird, wie sie es tun wird und warum sie \u00fcberhaupt das tut, was sie tut. Mit diesem Wissen ist es relativ einfach, Anleitungen zu erstellen und herauszufinden, wo die Ausf\u00fchrungszeit verloren geht. Wenn du jedoch nicht die entsprechende Vorbereitung hast, suchst du einfach nach einer schwarzen Katze im dunklen Raum. Ich sehe st\u00e4ndig Leute, die die Leistung optimieren, ohne die geringste Ahnung zu haben, was sie eigentlich tun. Sie strengen sich sehr an und kommen nicht wirklich voran. Wenn ich denselben Code nehme, ein paar kleine Hacks einf\u00fcge und dadurch eine f\u00fcnf- oder zehnfache Beschleunigung erziele, sagen sie: 'Das ist unfair, wir wussten schon, dass du besser bist.' Unglaublich. Wor\u00fcber rede ich \u2026 das Kostenmodell \u2013 es geht darum, welchen Code du schreibst und wie schnell er im Durchschnitt in das Gesamtbild passt.<\/p>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Und wie beh\u00e4lt man ein solches Volumen im Kopf? Erreicht man das durch viel Erfahrung, oder? Wo wird diese Erfahrung gesammelt?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Nun, meine Erfahrungen habe ich nicht auf die einfachste Weise gesammelt. Ich habe in Assembler programmiert, als man noch jede einzelne Anleitung verstehen konnte. Das klingt dumm, aber seitdem ist mir eine Menge Z80-Anweisungen im Kopf geblieben. Ich kann mir die Namen von Menschen schon eine Minute nach dem Gespr\u00e4ch nicht mehr merken, aber den Code, den ich vor 40 Jahren geschrieben habe, schon. Lustig, es sieht aus wie das Syndrom von<noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A1%D0%B8%D0%BD%D0%B4%D1%80%D0%BE%D0%BC_%D1%81%D0%B0%D0%B2%D0%B0%D0%BD%D1%82%D0%B0\">dem genialen Trottel<\/a><\/noindex>\u00bb.<\/p>\n<p><\/p>\n<h1 id=\"obuchenie-nizkourovnevym-optimizaciyam\">Einf\u00fchrung in Low-Level-Optimierungen<\/h1>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Gibt es einen einfacheren Weg, um in die Materie einzusteigen?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Ja und nein. Die Hardware, die wir alle nutzen, hat sich in dieser Zeit nicht wesentlich ver\u00e4ndert. Alle verwenden x86, mit Ausnahme von Smartphones, die auf Arm basieren. Wenn man sich nicht mit extremem Embedded-Development besch\u00e4ftigt, hat man genau das Gleiche. Gut, weiter. Die Anweisungen haben sich auch \u00fcber Jahrhunderte nicht ver\u00e4ndert. Man muss hingehen und etwas in Assembly schreiben. Ein wenig, aber genug, um zu beginnen, es zu verstehen. Sie l\u00e4cheln, aber ich meine das ganz ernst. Es ist wichtig, die Beziehung zwischen der Programmiersprache und der Hardware zu verstehen. Danach sollten Sie schreiben, ein wenig, und einen kleinen Spielcompiler f\u00fcr eine kleine Spielsprache erstellen. \u201eSpiel\u201c bedeutet, dass es in einem angemessenen Zeitrahmen geschehen muss. Er kann sehr einfach sein, aber er sollte Anweisungen generieren. Die Akt der Generierung von Anweisungen erlaubt es, das Kostenmodell f\u00fcr die Br\u00fccke zwischen dem hochgradigen Code, in dem alle schreiben, und dem Maschinen-Code, der auf der Hardware ausgef\u00fchrt wird, zu verstehen. Diese Beziehung wird sich in den K\u00f6pfen festsetzen, w\u00e4hrend man den Compiler schreibt. Selbst den einfachsten Compiler. Danach kann man anfangen, sich Java anzusehen und zu verstehen, dass ihre semantische Kluft viel tiefer ist, und dass es viel schwieriger ist, Br\u00fccken dar\u00fcber zu schlagen. In Java ist es viel schwieriger zu verstehen, ob unsere Br\u00fccke gut oder schlecht ist, was sie zum Einsturz bringt und was nicht. Aber man braucht einen Ausgangspunkt, wenn man den Code betrachtet und denkt: \u201eAha, dieser Getter sollte jedes Mal in-line gesetzt werden.\u201c Und dann stellt sich heraus, dass das manchmal passiert, mit Ausnahme der F\u00e4lle, in denen die Methode zu gro\u00df wird und JIT anf\u00e4ngt, alles M\u00f6gliche in-line zu setzen. Die Leistung solcher Stellen kann man sofort vorhersagen. Normalerweise funktionieren Getter gut, aber dann schaut man sich gro\u00dfe hei\u00dfe Schleifen an und erkennt, dass dort einige Funktionsaufrufe herumschwirren, deren Verhalten unklar ist. Das ist das Problem mit dem weit verbreiteten Gebrauch von Gettern; der Grund, warum sie nicht in-line gesetzt werden, ist, dass unklar ist, ob es sich um einen Getter handelt. Wenn man eine super kleine Codebasis hat, kann man sie einfach auswendig lernen und sagen: Das ist ein Getter und das ist ein Setter. In einer gro\u00dfen Codebasis hat jede Funktion ihre eigene Geschichte, die niemand wirklich kennt. Der Profiler sagt, dass wir 24% Zeit in einer bestimmten Schleife verloren haben, und um zu verstehen, was diese Schleife tut, muss man sich jede Funktion dort drinnen ansehen. Es ist unm\u00f6glich, dies zu verstehen, ohne die Funktion zu studieren, und das verlangsamt den Prozess des Verstehens erheblich. Deshalb benutze ich keine Getter und Setter \u2013 ich habe eine neue Stufe erreicht!<br \/>\nWoher bekommt man ein Preismodell? Nun, man kann sicherlich etwas lesen\u2026 Aber ich denke, der beste Weg ist, aktiv zu werden. Erstelle einen einfachen Compiler, und das wird der beste Weg sein, um das Preismodell zu verstehen und es sich selbst klarzumachen. Ein kleiner Compiler, der sich gut f\u00fcr die Programmierung einer Mikrowelle eignen w\u00fcrde \u2013 das ist eine Aufgabe f\u00fcr Anf\u00e4nger. Ich meine, wenn du bereits Programmierkenntnisse hast, sollten diese ausreichen. All diese Dinge, wie das Parsen eines Strings, der irgendeinen algebraischen Ausdruck enth\u00e4lt, die Anweisungen f\u00fcr mathematische Operationen in der richtigen Reihenfolge herauszuziehen, und die richtigen Werte aus Registern zu nehmen \u2013 das ist alles ganz einfach. Und w\u00e4hrend du das tust, wird es sich in deinem Gehirn einpr\u00e4gen. Ich denke, jeder wei\u00df, was ein Compiler macht. Und das wird Verst\u00e4ndnis f\u00fcr das Preismodell schaffen.<\/p>\n<p><\/p>\n<h1 id=\"prakticheskie-primery-uluchsheniya-proizvoditelnosti\">Praktische Beispiele zur Verbesserung der Leistung<\/h1>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Worauf sollte man noch achten, wenn man an der Leistung arbeitet?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Datenstrukturen. \u00dcbrigens, ich habe schon lange keine Kurse mehr dazu gegeben\u2026 <noindex>Rocket School<\/noindex>. Es war unterhaltsam, aber es erforderte so viel Einsatz, und ich habe schlie\u00dflich auch ein Leben! Nun, eines der gro\u00dfen und interessanten Themen, \"Wohin geht Ihre Leistung\", habe ich den Studenten ein Beispiel gegeben: zweieinhalb Gigabyte an Fintech-Daten wurden aus einer CSV-Datei gelesen, und dann musste die Anzahl der verkauften Produkte berechnet werden. Gew\u00f6hnliche Tick-Daten des Marktes. UDP-Pakete, die seit den 70er Jahren in ein Textformat umgewandelt wurden. Chicago Mercantile Exchange \u2013 verschiedene Dinge wie \u00d6l, Mais, Sojabohnen und dergleichen. Es galt, diese Produkte zu z\u00e4hlen, die Anzahl der Transaktionen, das durchschnittliche Volumen von Mitteln und Waren usw. zu berechnen. Das ist ziemlich einfache Handelsmathematik: Den Produktcode finden (das sind 1-2 Zeichen in einer Hash-Tabelle), die Summe erhalten, sie in eines der Transaktionssets hinzuf\u00fcgen, das Volumen hinzuf\u00fcgen, den Preis hinzuf\u00fcgen und ein paar andere Dinge. Sehr einfache Mathematik. Die einfache Implementierung war sehr geradlinig: Alles befindet sich in der Datei, ich lese die Datei und gehe durch sie hindurch, teile einzelne Datens\u00e4tze in Java-Strings auf, suche nach den ben\u00f6tigten Informationen und addiere sie gem\u00e4\u00df der oben beschriebenen Mathematik. Und das geschieht mit einem gewissen Tempo. <\/p>\n<p><\/p>\n<p>Mit diesem Ansatz ist offensichtlich, was passiert, und paralleles Rechnen wird hier nicht helfen, oder? Es stellt sich heraus, dass man die f\u00fcnfmal h\u00f6here Leistungsf\u00e4higkeit nur durch die Wahl der richtigen Datenstrukturen erreichen kann. Sogar erfahrene Programmierer sind dar\u00fcber erstaunt! In meinem speziellen Fall lag der Fokus darin, dass man keine Speicherzuweisungen in einer hei\u00dfen Schleife t\u00e4tigen sollte. Nun, das ist nicht die ganze Wahrheit, aber im Gro\u00dfen und Ganzen \u2013 man sollte nicht 'einmal in X' zuweisen, wenn X gro\u00df genug ist. Wenn X zweieinhalb Gigabyte betr\u00e4gt, sollte man nichts 'einmal pro Zeichen', 'einmal pro Zeile' oder 'einmal f\u00fcr ein Feld' zuweisen, nichts dergleichen. Genau das kostet Zeit. Wie funktioniert das \u00fcberhaupt? Stellen Sie sich vor, ich mache einen Aufruf <code>String.split()<\/code> oder <code>BufferedReader.readLine()<\/code>. <code>Readline<\/code> stellt eine Zeichenkette aus einer Menge von Bytes zusammen, die \u00fcber das Netzwerk kommen, einmal f\u00fcr jede Zeile, f\u00fcr jede der Millionen von Zeilen. Ich nehme diese Zeile, analysiere sie und verwende sie nicht mehr. Warum nicht? Nun, ich habe sie bereits verarbeitet, das ist alles. Also, f\u00fcr jedes Byte, das aus diesen 2,7 GB gelesen wird, werden zwei Zeichen in der Zeichenkette geschrieben, das sind bereits 5,4 GB, und ich brauche sie danach nicht mehr, deshalb werden sie verworfen. Wenn wir die Speicherdurchsatzrate betrachten, laden wir 2,7 GB, die durch den Speicher und die Speicherschnittstelle in die CPU gelangen, und dann wird doppelt so viel in eine Zeichenkette im Speicher geschrieben, und all das wird bei der Erstellung jeder neuen Zeichenkette \u00fcberarbeitet. Aber ich muss sie lesen, die Hardware liest sie, auch wenn sie danach alle \u00fcberarbeitet werden. Und ich muss sie schreiben, weil ich eine Zeichenkette erstellt habe und die Caches \u00fcberlaufen sind \u2013 der Cache kann nicht 2,7 GB aufnehmen. Insgesamt lese ich f\u00fcr jedes gelesene Byte zwei zus\u00e4tzliche Bytes und schreibe zwei zus\u00e4tzliche Bytes, und somit haben wir ein Verh\u00e4ltnis von 4:1 \u2013 in diesem Verh\u00e4ltnis verschwenden wir die Speicherdurchsatzrate. Und es stellt sich heraus, dass wenn ich das mache <code>String.split()<\/code> \u2013 das werde ich ganz bestimmt nicht zum letzten Mal tun, denn darin k\u00f6nnen noch 6-7 Felder enthalten sein. Deshalb f\u00fchrt der klassische CSV-Lese-Code mit anschlie\u00dfender Zeilenanalyse zu einem Speicherbandbreitenverlust von etwa 14:1 im Vergleich zu dem, was Sie tats\u00e4chlich haben m\u00f6chten. Wenn Sie diese Zuweisungen weglassen, k\u00f6nnen Sie eine f\u00fcnffache Beschleunigung erreichen. <\/p>\n<p><\/p>\n<p>Und das ist nicht besonders kompliziert. Wenn Sie den Code aus dem richtigen Blickwinkel betrachten, wird alles ziemlich einfach, sobald Sie das Wesen des Problems erkannt haben. Es ist nicht notwendig, das Speichermanagement vollst\u00e4ndig einzustellen: Die einzige Schwierigkeit besteht darin, dass Sie etwas zuweisen und es sofort wieder verloren geht, wobei es wichtige Ressourcen verbrennt, die in diesem Fall \u2013 die Speicherbandbreite \u2013 sind. All dies f\u00fchrt zu einem Leistungsabfall. Bei x86 muss man normalerweise intensiv Taktzyklen verbrauchen, aber hier haben Sie den gesamten Speicher viel fr\u00fcher verbrannt. Die L\u00f6sung besteht darin, die Anzahl der Zuweisungen zu verringern.\u00a0<br \/>\nEin weiteres Problem ist, dass, wenn Sie den Profiler starten, wenn der Speicher voll ist, genau in dem Moment, in dem es passiert, normalerweise darauf warten, dass der Cache zur\u00fcckkommt, weil er mit M\u00fcll gef\u00fcllt ist, den Sie gerade erzeugt haben, mit all diesen Objekten. Daher wird jede Load- oder Store-Operation langsam, da sie Cache-Misses verursachen \u2013 der gesamte Cache wird langsam, w\u00e4hrend er darauf wartet, dass der M\u00fcll entfernt wird. Der Profiler zeigt daher lediglich einen warmen, zuf\u00e4lligen Rauschen, der sich \u00fcber den gesamten Zyklus erstreckt \u2013 es wird keine separate hei\u00dfe Anweisung oder Stelle im Code geben. Nur Rauschen. Und wenn Sie sich die GC-Zyklen ansehen, werden diese alle zum Young Generation z\u00e4hlen und extrem schnell sein \u2013 Mikrosenden oder maximal Millisekunden. Denn all dieser Speicher stirbt sofort. Sie allocieren Milliarden von Gigabyte, und er schneidet sie ab, und schneidet sie ab, und schneidet sie wieder ab. All das geht sehr schnell. Es gibt also wenig kostspielige GC-Zyklen, warmes Rauschen \u00fcber den gesamten Zyklus, aber wir m\u00f6chten eine 5-fache Beschleunigung. An diesem Punkt sollte etwas in Ihrem Kopf sich schlie\u00dfen und zu der Frage f\u00fchren: \"Warum ist das so?!\". Speicher\u00fcberl\u00e4ufe werden im klassischen Debugger nicht angezeigt; Sie m\u00fcssen den Debugger f\u00fcr Hardware-Leistungsz\u00e4hler starten und es selbst direkt sehen. Indirekt kann man dies aus diesen drei Symptomen heraus vermuten. Das dritte Symptom ist, wenn Sie sehen, was Sie allocieren, den Profiler fragen, und er antwortet: \"Sie haben eine Milliarde Objekte erstellt, aber der GC hat kostenlos gearbeitet\". Sobald dies passiert, verstehen Sie, dass Sie zu viele Objekte produziert haben und den gesamten Speicher verbrannt haben. Es gibt M\u00f6glichkeiten, das herauszufinden, aber sie sind nicht offensichtlich.\u00a0<\/p>\n<p><\/p>\n<p>Ein Problem in der Datenstruktur: eine nackte Struktur, die dem gesamten Geschehen zugrunde liegt, ist zu gro\u00df, es sind 2,7 GB auf der Festplatte, daher ist es sehr unerw\u00fcnscht, diese zu kopieren \u2013 man m\u00f6chte sie direkt aus dem Netzwerk-Byte-Puffer in die Register laden, um nicht st\u00e4ndig hin und her lesen und schreiben zu m\u00fcssen. Leider bietet Java standardm\u00e4\u00dfig keine solche Bibliothek im JDK an. Aber das ist doch trivial, oder? Faktisch sind das 5-10 Zeilen Code, die zur Implementierung eines eigenen gepufferten String-Loaders f\u00fchren, der das Verhalten der String-Klasse nachahmt und dabei eine Wrapper um den zugrunde liegenden Byte-Puffer ist. Infolgedessen arbeitet man fast wie mit Strings, aber tats\u00e4chlich bewegen sich dabei nur Zeiger auf den Puffer, und rohe Bytes werden nirgendwo kopiert, wodurch immer dieselben Puffer wiederverwendet werden, immer wieder. Und das Betriebssystem ist froh, sich um Dinge zu k\u00fcmmern, f\u00fcr die es vorgesehen ist, wie das versteckte doppelte Pufferung dieser Byte-Puffer, sodass man selbst keinen endlosen Strom unerw\u00fcnschter Daten mehr verarbeiten muss. \u00dcbrigens, versteht man denn nicht, dass bei der Arbeit mit GC garantiert wird, dass jede Speicherzuweisung nach dem letzten GC-Zyklus nicht mehr vom Prozessor gesehen wird? Deshalb kann all das nicht im Cache sein, und dann passiert ein 100% garantierter Missgriff. Bei der Arbeit mit einem Zeiger dauert das Auslesen eines Registers aus dem Speicher auf x86 1-2 Takte, und sobald das geschieht, zahlt man, zahlt man, zahlt man, denn der gesamte Speicher ist auf <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Cache_inclusion_policy\">NEUN Caches<\/a><\/noindex> \u2013 und das ist der Preis f\u00fcr die Zuweisung von Speicher. Der tats\u00e4chliche Preis.<\/p>\n<p><\/p>\n<p>Mit anderen Worten, Datenstrukturen sind das, was am schwierigsten zu \u00e4ndern ist. Und sobald Sie realisieren, dass Sie die falsche Datenstruktur gew\u00e4hlt haben, die letztendlich die Leistung beeintr\u00e4chtigt, ist es normalerweise erforderlich, erhebliche Arbeiten zu leisten. Wenn dies nicht getan wird, wird es nur noch schlimmer. Zun\u00e4chst m\u00fcssen Sie \u00fcber Datenstrukturen nachdenken, denn das ist wichtig. Die Hauptkosten liegen in den komplexen Datenstrukturen, die in der Form \"Ich habe Datenstruktur X in Datenstruktur Y kopiert, weil ich Y ansprechender finde\" verwendet werden. Aber der Kopiervorgang (der als g\u00fcnstig erscheint) verbraucht tats\u00e4chlich Speicherbandbreite, und hier steckt die verlorene Zeit. Wenn ich also eine riesige JSON-Zeichenkette habe und sie in einen strukturierten DOM-Baum aus POJO oder \u00c4hnlichem umwandeln m\u00f6chte, wird der Parsing-Prozess dieser Zeichenkette und das Erstellen von POJO sowie der sp\u00e4tere Zugriff auf das POJO erhebliche Kosten verursachen \u2013 das ist nicht g\u00fcnstig. Es sei denn, Sie greifen viel h\u00e4ufiger auf das POJO zu als auf die Zeichenkette. Stattdessen k\u00f6nnte man versuchen, die Zeichenkette zu dechiffrieren und nur die ben\u00f6tigten Elemente herauszuziehen, ohne sie in ein POJO umzuwandeln. Wenn all dies zu einem Zeitpunkt geschieht, an dem maximale Leistung erforderlich ist, sollten Sie sich von POJOs fernhalten \u2013 und direkt mit der Zeichenkette arbeiten.<\/p>\n<p><\/p>\n<h1 id=\"zachem-sozdavat-svoy-yazyk-programmirovaniya\">Warum man eine eigene Programmier sprache erstellen sollte<\/h1>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Sie haben gesagt, dass man f\u00fcr ein besseres Verst\u00e4ndnis des Kostenmodells eine eigene kleine Sprache schreiben sollte\u2026<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Nicht eine Sprache, sondern einen Compiler. Sprache und Compiler sind zwei verschiedene Dinge. Der wichtigste Unterschied liegt im eigenen Kopf.\u00a0<\/p>\n<p><\/p>\n<p><strong>Andrei<\/strong>: \u00dcbrigens, soweit ich wei\u00df, experimentieren Sie mit der Erstellung eigener Sprachen. Warum?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Weil ich es kann! Ich bin zur H\u00e4lfte im Ruhestand, also ist das mein Hobby. Ich habe mein ganzes Leben lang die Programmiersprachen anderer Menschen umgesetzt. Au\u00dferdem habe ich viel an meinem Codierungsstil gearbeitet. Und auch, weil ich Probleme in anderen Sprachen sehe. Ich erkenne, dass es bessere M\u00f6glichkeiten gibt, gewohnte Dinge zu tun. Und ich w\u00fcrde sie nutzen. Ich habe einfach genug davon, Probleme in mir selbst, in Java, in Python oder in jeder anderen Sprache zu sehen. Momentan schreibe ich als Hobby in React Native, JavaScript und Elm; das hat nichts mit meinem Ruhestand zu tun, sondern mit aktiver Arbeit. Ich programmiere auch in Python und werde wahrscheinlich weiterhin im Bereich Maschinelles Lernen f\u00fcr Java-Backends arbeiten. Es gibt viele beliebte Sprachen, und jede hat ihre eigenen interessanten Besonderheiten. Jede ist auf ihre Weise gut und es k\u00f6nnte spannend sein, all diese Features zu kombinieren. Daher besch\u00e4ftige ich mich mit dem Studium interessanter Dinge f\u00fcr mich, dem Verhalten von Sprachen, und versuche, eine sinnvolle Semantik zu entwickeln. Und bisher gelingt mir das! Momentan k\u00e4mpfe ich mit der Semantik des Speichers, denn ich m\u00f6chte, dass sie so funktioniert wie in C und Java, und ich m\u00f6chte ein starkes Speicher- und Semantikmodell f\u00fcr Loads und Stores erhalten. Gleichzeitig m\u00f6chte ich eine automatische Typableitung wie in Haskell haben. Also versuche ich, eine Haskell-\u00e4hnliche Typableitung mit einem Speichermodell zu kombinieren, das wie in C und Java funktioniert. Daran arbeite ich seit den letzten 2-3 Monaten, zum Beispiel.<\/p>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Wenn Sie eine Sprache entwickeln, die die besten Aspekte anderer Sprachen aufgreift, haben Sie dann dar\u00fcber nachgedacht, dass jemand Ihre Ideen aufgreifen und f\u00fcr sich nutzen k\u00f6nnte?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: So entstehen neue Sprachen! Warum \u00e4hnelt Java C? Weil C eine gute Syntax hatte, die jeder verstand, und Java sich von dieser Syntax inspirieren lie\u00df, indem es Typensicherheit, Array-Grenzpr\u00fcfungen, GC hinzuf\u00fcgte und einige Dinge aus C verbesserte. Sie haben eigene Ideen beigef\u00fcgt. Aber sie waren ziemlich stark inspiriert, oder? Alle stehen auf den Schultern von Riesen, die vor dir waren \u2013 so entsteht Fortschritt.<\/p>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Soweit ich verstehe, wird Ihre Sprache sicher im Hinblick auf die Speichernutzung sein. Haben Sie dar\u00fcber nachgedacht, etwas wie den Borrow Checker aus Rust zu implementieren? Haben Sie ihn sich angeschaut? Was halten Sie davon?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Ich programmiere schon seit einer Ewigkeit in C, mit all diesen malloc und free, und ich verwalte die Lebensdauer manuell. Sie wissen, 90-95% des manuell verwalteten Lebenszyklus haben eine \u00e4hnliche Struktur. Und es ist sehr, sehr schmerzhaft, sich damit manuell auseinanderzusetzen. Ich w\u00fcnschte, der Compiler k\u00f6nnte einfach sagen, was da passiert und was ich mit meinen Aktionen erreicht habe. In einigen F\u00e4llen erledigt der Borrow Checker das von Haus aus. Au\u00dferdem sollte er automatisch Informationen bereitstellen, alles verstehen und mich nicht mit der Aufgabe belasten, dieses Verst\u00e4ndnis zu formulieren. Er sollte mindestens eine lokale Escape-Analyse durchf\u00fchren, und nur wenn das nicht funktioniert, sollten Typannotationen hinzugef\u00fcgt werden, die die Lebensdauer beschreiben \u2013 und dieses Schema ist viel komplizierter als der Borrow Checker oder \u00fcberhaupt irgendein existierender Speicherpr\u00fcfer. Die Wahl zwischen \u201ealles in Ordnung\u201c und \u201eich habe nichts verstanden\u201c \u2013 das sollte besser sein.\u00a0<br \/>\nAls jemand, der viel C-Code geschrieben hat, halte ich die Unterst\u00fctzung f\u00fcr das automatische Management des Lebenszyklus f\u00fcr \u00e4u\u00dferst wichtig. Au\u00dferdem \u00e4rgert es mich, wie sehr Java Speicher verbraucht, mit der Hauptbeschwerde im Zusammenhang mit dem Garbage Collector. Bei der Speicherzuweisung in Java erh\u00e4lt man den Speicher, der im letzten GC-Zyklus lokal war, nicht zur\u00fcck. In Sprachen mit pr\u00e4ziserer Speicherverwaltung ist das nicht der Fall. Wenn man malloc aufruft, erh\u00e4lt man sofort Speicher, der gerade verwendet wurde. Normalerweise f\u00fchrt man mit dem Speicher einige tempor\u00e4re Operationen durch und gibt ihn sofort zur\u00fcck. Und er wird sofort wieder in den malloc-Pool zur\u00fcckgegeben, sodass der n\u00e4chste malloc-Zyklus ihn erneut verwenden kann. Daher reduziert sich der tats\u00e4chliche Speicherverbrauch auf die Menge an lebenden Objekten zu einem bestimmten Zeitpunkt, plus eventuelle Speicherlecks. Wenn es keine gravierenden Lecks gibt, wird der Gro\u00dfteil des Speichers in Caches und CPU gespeichert, was schnell funktioniert. Allerdings erfordert dies viel manuelles Speichermanagement durch malloc und free, die in der richtigen Reihenfolge und am richtigen Ort aufgerufen werden m\u00fcssen. Rust kann dies selbst richtig handhaben und in vielen F\u00e4llen sogar eine bessere Leistung bieten, da der Speicherverbrauch nur auf die aktuellen Berechnungen beschr\u00e4nkt ist \u2013 im Gegensatz zur Erwartung des n\u00e4chsten GC-Zyklus, der den Speicher freigibt. Letztendlich haben wir einen sehr interessanten Weg gefunden, um die Leistung zu verbessern. Und es ist ziemlich m\u00e4chtig \u2013 ich habe an solchen Dingen bei der Datenverarbeitung f\u00fcr Fintech gearbeitet, und das hat eine Beschleunigung von etwa f\u00fcnf erm\u00f6glicht. Das ist eine erhebliche Beschleunigung, besonders in einer Welt, in der die Prozessoren nicht schneller werden, w\u00e4hrend wir weiterhin auf Verbesserungen warten.<\/p>\n<p><\/p>\n<h1 id=\"karera-performans-inzhenera\">Karriere als Performance-Ingenieur<\/h1>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Ich w\u00fcrde auch gerne insgesamt etwas \u00fcber die Karriere fragen. Sie wurden durch Ihre Arbeit bei JIT in HotSpot ber\u00fchmt und wechselten dann zu Azul \u2013 und das ist auch ein JVM-Unternehmen. Dort besch\u00e4ftigten Sie sich jedoch mehr mit Hardware als mit Software. Und dann wechselten Sie pl\u00f6tzlich zu Big Data und Machine Learning und danach zur Betrugserkennung. Wie kam es dazu? Das sind sehr unterschiedliche Bereiche der Entwicklung.<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Ich besch\u00e4ftige mich schon seit geraumer Zeit mit Programmierung und habe in den verschiedensten Bereichen Erfahrungen gesammelt. Wenn Menschen sagen: \u201eOh, du bist derjenige, der JIT f\u00fcr Java gemacht hat!\u201c, finde ich das immer am\u00fcsant. Vorher habe ich an einem Klon von PostScript gearbeitet \u2013 der Sprache, die Apple einst f\u00fcr seine Laserdrucker verwendet hat. Davor habe ich das Forth-Sprache umgesetzt. Ich denke, das gemeinsame Thema f\u00fcr mich ist die Entwicklung von Werkzeugen. Mein ganzes Leben lang habe ich Werkzeuge geschaffen, mit denen andere Menschen ihre gro\u00dfartigen Programme schreiben. Aber ich habe auch Betriebssysteme, Treiber, Kernel-Debugger und Programmiersprachen f\u00fcr Betriebssysteme entwickelt, die urspr\u00fcnglich trivial waren, aber mit der Zeit immer komplexer wurden. Aber das Hauptthema ist dennoch die Entwicklung von Werkzeugen. Ein gro\u00dfer Teil meines Lebens verging zwischen Azul und Sun, und es ging dabei um Java. Aber als ich mich mit Big Data und Machine Learning besch\u00e4ftigte, setzte ich wieder meinen feierlichen Hut auf und sagte: \u201eOh, jetzt haben wir ein nicht triviales Problem, und es passiert hier eine Menge interessanter Dinge mit Menschen, die etwas tun.\u201c Das ist ein hervorragender Entwicklungsweg, den man einschlagen sollte. <\/p>\n<p><\/p>\n<p>Ja, ich liebe verteilte Rechnersysteme sehr. Mein erster Job w\u00e4hrend des Studiums war in C, an einem Werbeprojekt. Dabei ging es um verteilte Berechnungen auf Zilog Z80-Chips, die Daten f\u00fcr die analoge optische Texterkennung sammelten, die von einem echten analogen Analysator durchgef\u00fchrt wurde. Das war ein spannendes und v\u00f6llig verr\u00fccktes Thema. Aber es gab Probleme: Ein Teil der Daten wurde nicht korrekt erkannt, weshalb die Bilder angezeigt werden mussten, um sie von jemandem lesen zu lassen, der sie bereits visuell erfasst hatte, um Feedback zu geben. Dadurch entstanden Jobs mit Daten, die ihre eigene Sprache hatten. Es gab ein Backend, das alles verarbeitete \u2013 parallel arbeitende Z80 mit laufenden vt100-Terminals \u2013 jeweils eines pro Person, und es gab ein Modell f\u00fcr paralleles Programmieren auf Z80. Ein gemeinsamer Speicherbereich, den alle Z80 innerhalb einer Sternkonfiguration teilten; auch das Backplane wurde geteilt, und die H\u00e4lfte des RAM wurde innerhalb des Netzwerks geteilt, w\u00e4hrend die andere H\u00e4lfte privat war oder f\u00fcr etwas anderes genutzt wurde. Eine bedeutend komplexe parallele verteilte Systemarchitektur mit geteilter... halbgeteilter Speicher. Wann das war... das ist schon so lange her, irgendwo in den 80ern. Ziemlich lange her.\u00a0<br \/>\nJa, nehmen wir an, dass 30 Jahre eine betr\u00e4chtliche Zeitspanne sind. Die Herausforderungen im Bereich der verteilten Datenverarbeitung bestehen schon lange, und die Menschen haben sich bereits fr\u00fcher damit auseinandergesetzt. <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/Beowulf_(%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80)\">Beowulf<\/a><\/noindex>-Cluster. Solche Cluster sehen so aus: Es gibt Ethernet, und dein leistungsstarker x86 ist mit diesem Ethernet verbunden. Jetzt m\u00f6chtest du eine gef\u00e4lschte Shared Memory haben, weil es damals zu schwierig war, sich mit der Programmierung verteilter Systeme zu besch\u00e4ftigen. Daher gab es eine gef\u00e4lschte Shared Memory mit Speicherschutz auf x86. Wenn du in diesen Speicher geschrieben hast, haben wir den anderen Prozessoren mitgeteilt, dass sie die gleiche Shared Memory von dir laden m\u00fcssen, wenn sie darauf zugreifen wollten. So entstand eine Art Protokoll zur Unterst\u00fctzung der Cache-Koh\u00e4renz und die entsprechende Software. Eine interessante Konzeptualisierung. Das echte Problem lag nat\u00fcrlich woanders. Das Ganze funktionierte, aber man bekam schnell Leistungsprobleme, da niemand das Leistungsmodell auf einem ausreichenden Niveau verstand \u2013 welche Zugriffszenarien es gab, wie man verhindern konnte, dass die Knoten sich endlos gegenseitig anpingen, und so weiter. <\/p>\n<p><\/p>\n<p>In H2O habe ich Folgendes ausgekl\u00fcgelt: Die Entwickler selbst sind daf\u00fcr verantwortlich, den Parallelismus zu erkennen und zu wissen, wo er nicht vorhanden ist. Ich habe ein Codierungsmodell entwickelt, das es einfach macht, leistungsstarken Code zu schreiben. Andererseits ist es eine Herausforderung, ineffizienten Code zu erstellen, und dieser wird schlecht aussehen. Es erfordert erhebliche Anstrengungen, um langsamen Code zu produzieren; man muss unkonventionelle Methoden anwenden. Langsame Codeabschnitte sind auf den ersten Blick erkennbar. In der Regel wird also Code geschrieben, der schnell funktioniert, jedoch muss man sich mit dem Umgang mit gemeinsam genutztem Speicher auseinandersetzen. Dies ist alles mit gro\u00dfen Arrays verbunden, und das Verhalten ist vergleichbar mit nicht-volatilen gro\u00dfen Arrays in parallelem Java. Stellen Sie sich vor, zwei Threads schreiben in ein paralleles Array, wobei einer gewinnt und der andere entsprechend verliert, ohne dass man wei\u00df, welcher welcher ist. Wenn sie nicht volatil sind, kann die Reihenfolge beliebig sein \u2013 und das funktioniert tats\u00e4chlich gut. Die Menschen k\u00fcmmern sich wirklich um die Reihenfolge der Operationen, sie platzieren volatile richtig und erwarten an den richtigen Stellen Performance-Probleme im Zusammenhang mit dem Speicher. Andernfalls w\u00fcrden sie einfach Code in Form von Schleifen von 1 bis N schreiben, wobei N eine unvorstellbar gro\u00dfe Zahl ist, und hoffen, dass alle komplexen F\u00e4lle automatisch parallelisiert werden \u2013 und das klappt nicht. Aber in H2O, das ist weder Java noch Scala; man k\u00f6nnte es als \"Java minus minus\" betrachten, wenn man m\u00f6chte. Es ist ein sehr verst\u00e4ndlicher Programmierstil und \u00e4hnelt dem Schreiben von einfachem Code in C oder Java mit Schleifen und Arrays. Dennoch kann der Speicher in Terabytes verarbeitet werden. Ich verwende H2O immer noch. Gelegentlich setze ich es in verschiedenen Projekten ein \u2013 und es ist nach wie vor das schnellste Tool, das seine Konkurrenten um ein Vielfaches \u00fcbertrifft. Wenn Sie Big Data mit spaltenbasierten Daten verarbeiten, ist es sehr schwierig, H2O zu \u00fcbertreffen.<\/p>\n<p><\/p>\n<h1 id=\"tehnicheskie-chellenzhi\">Technische Herausforderungen<\/h1>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Was war die gr\u00f6\u00dfte Herausforderung in Ihrer gesamten Karriere?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Sprechen wir \u00fcber den technischen oder den nicht-technischen Teil? Ich w\u00fcrde sagen, die gr\u00f6\u00dften Herausforderungen sind nicht technischer Natur.\u00a0<br \/>\nWas die technischen Herausforderungen betrifft, habe ich sie einfach gemeistert. Ich kann nicht einmal sagen, welche am gr\u00f6\u00dften war, aber es gab einige ziemlich interessante, die viel Zeit und geistige Auseinandersetzung erforderten. Als ich zu Sun ging, war ich \u00fcberzeugt, einen schnellen Compiler zu entwickeln, w\u00e4hrend viele meiner Senior-Kollegen sagten, dass ich damit niemals Erfolg haben w\u00fcrde. Aber ich verfolgte diesen Weg, schrieb den Compiler bis hin zum Register-Allocator, und er war ziemlich schnell. Er war so schnell wie das moderne C1, aber damals war der Allocator viel langsamer, und r\u00fcckblickend war das eine Herausforderung aufgrund der gro\u00dfen Datenstrukturen. Ich ben\u00f6tigte diese, um einen grafischen Register-Allocator zu schreiben, und ich verstand die Dilemmata zwischen Ausdruckskraft des Codes und Geschwindigkeit, die in dieser \u00c4ra sehr wichtig waren. Es stellte sich heraus, dass die Datenstruktur normalerweise die Cache-Gr\u00f6\u00dfe der damaligen x86-Prozessoren \u00fcberschreitet und daher, w\u00e4hrend ich urspr\u00fcnglich annahm, dass der Register-Allocator 5-10 Prozent der gesamten JIT-Zeit in Anspruch nehmen w\u00fcrde, letztendlich 50 Prozent ausmachten. <\/p>\n<p><\/p>\n<p>Die Zeit verging, der Compiler wurde immer pr\u00e4ziser und leistungsf\u00e4higer, h\u00f6rte auf, in den meisten F\u00e4llen schlechten Code zu generieren, und die Leistung \u00e4hnelte zunehmend dem, was ein C-Compiler liefert. Es sei denn, man schreibt solchen Mist, den nicht einmal C schneller macht. Wenn du Code schreibst, der wie C aussieht, erh\u00e4ltst du auch in den meisten F\u00e4llen eine Leistung wie bei C. Und immer h\u00e4ufiger produzierte der Code asymptotisch \u00e4hnliche Ergebnisse wie C, der Register-Allocator wurde zu etwas, das vollendet wirkte... unabh\u00e4ngig davon, ob dein Code schnell oder langsam lief. Ich arbeitete weiterhin am Allocator, um bessere Speicherzuweisungen zu erreichen. Er wurde immer langsamer, lieferte aber immer bessere Leistungen in F\u00e4llen, in denen niemand mehr damit zurechtkam. Ich konnte in den Register-Allocator eintauchen, dort einen Monat Arbeit vergraben, und pl\u00f6tzlich begann der gesamte Code, 5 % schneller zu laufen. Das geschah immer wieder, und der Register-Allocator wurde zu etwas wie einem Kunstwerk \u2013 alle mochten oder hassten ihn, und die Leute aus der Akademie stellten Fragen wie: 'Warum wird alles genau so gemacht?' Warum nicht <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Register_allocation#Linear_Scan\">lineares Scanning<\/a><\/noindex>, und wo der Unterschied liegt. Die Antwort bleibt dieselbe: Ein Allocator auf Basis der Graphf\u00e4rbung, kombiniert mit sehr pr\u00e4ziser Arbeit am Puffer-Code, ist das Werkzeug des Sieges, die beste Kombination, die niemand schlagen kann. Und das ist ziemlich unauff\u00e4llig. Alles andere, was der Compiler macht, sind recht gut erforschte Dinge, obwohl sie ebenfalls auf ein Kunstniveau gebracht wurden. Ich habe immer versucht, Dinge zu machen, die den Compiler zu einem Kunstwerk machen sollten. Aber nichts davon war au\u00dfergew\u00f6hnlich \u2013 mit Ausnahme des Register-Allocators. Der Trick liegt darin, dass man sorgf\u00e4ltig <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Register_allocation\">abschneiden<\/a><\/noindex> unter Last, und wenn das passiert (ich kann das gerne n\u00e4her erkl\u00e4ren, falls es interessiert), bedeutet das, dass man aggressiver inline gehen kann, ohne das Risiko einzugehen, \u00fcber den Performance-Grafikbruchpunkt hinauszukommen. Damals gab es eine Menge vollwertiger Compiler, die mit Spielereien und Extras vollgepackt waren, in denen Register-Allocatoren enthalten waren, aber niemand konnte so etwas mehr erreichen. <\/p>\n<p><\/p>\n<p>Das Problem ist, dass, wenn Sie Inline-Methoden hinzuf\u00fcgen und den Bereich des Inlinings immer weiter vergr\u00f6\u00dfern, die Anzahl der verwendeten Werte sofort die Anzahl der Register \u00fcbersteigt, und Sie mit dem Spilling konfrontiert werden. Ein kritischer Punkt tritt normalerweise ein, wenn der Allocator versagt, und ein guter Kandidat f\u00fcr das Spilling kostet den anderen, sodass Sie teilweise wirklich verr\u00fcckte Dinge spillen. Der Wert des Inlinings liegt darin, dass Sie einen Teil des Overheads, den Overhead f\u00fcr Aufrufe und Speicherungen, verlieren; Sie k\u00f6nnen die Werte intern sehen und weiter optimieren. Die Kosten des Inlinings liegen darin, dass eine gro\u00dfe Anzahl lebender Werte entsteht, und wenn Ihr Register-Allocator mehr spillt, als notwendig ist, verlieren Sie sofort. Daher haben die meisten Allocatoren ein Problem: Wenn das Inlining einen bestimmten Punkt \u00fcberschreitet, beginnt alles zu spillen, und die Leistung kann dann ins Klo gesp\u00fclt werden. Compiler-Entwickler f\u00fcgen einige Heuristiken hinzu: Beispielsweise wird das Inlining ab einer ausreichend gro\u00dfen Gr\u00f6\u00dfe gestoppt, da alle Allokationen den Prozess ruinieren k\u00f6nnen. So entsteht ein Bruch im Leistungskurve \u2013 Sie inline, inline, die Leistung w\u00e4chst langsam \u2013 und dann bam! \u2013 f\u00e4llt sie abrupt ab, weil Sie zu viel inlined haben. So lief alles bis zur Einf\u00fchrung von Java. Java erfordert deutlich mehr Inlining, daher musste ich meinen Allocator viel aggressiver gestalten, damit er sich anpasst und nicht abst\u00fcrzt. Wenn Sie zu viel inlined haben, beginnt er zu spillen, aber irgendwann kommt der Punkt, an dem \"kein Spilling mehr\" stattfinden kann. Dies ist eine interessante Beobachtung, die mir einfach aus dem Nichts kam, nicht offensichtlich, aber gut bezahlt. Ich habe mich ans aggressive Inlining gewagt, und das f\u00fchrte mich zu Territorien, in denen die Leistung von Java und C Hand in Hand geht. Sie sind tats\u00e4chlich sehr nah beieinander \u2013 ich kann Java-Code schreiben, der signifikant schneller ist als C-Code und \u00e4hnliche Dinge, aber im Durchschnitt, im gro\u00dfen Bild, sind sie ungef\u00e4hr vergleichbar. Ich denke, ein Teil dieses Erfolges liegt am Register-Allocator, der mir erlaubt, maximal unreflektiert inlined. Ich inline einfach alles, was ich sehe. Die Frage ist, ob der Allocator gut funktioniert und ob sich am Ende sinnvoll funktionierender Code ergibt. Das war eine gro\u00dfe Herausforderung: das alles zu verstehen und zum Laufen zu bringen.<\/p>\n<p><\/p>\n<h1 id=\"nemnogo-pro-allokaciyu-registrov-i-mnogoyadernost\">Ein bisschen \u00fcber Registerzuweisung und Multikerntechnik<\/h1>\n<p><\/p>\n<p><strong>Wladimir<\/strong>: Probleme wie die Allokation von Registern erscheinen als ein immerw\u00e4hrendes Thema. Interessant w\u00e4re, ob eine Idee einmal vielversprechend schien und dann in der Praxis gescheitert ist?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Nat\u00fcrlich! Die Registerallokation ist ein Bereich, in dem du versuchst, einige Heuristiken zur L\u00f6sung eines NP-vollst\u00e4ndigen Problems zu finden. Und du wirst niemals eine perfekte L\u00f6sung erreichen k\u00f6nnen, richtig? Das ist einfach unm\u00f6glich. Sieh dir die Ahead-of-Time-Kompilierung an \u2013 die funktioniert auch nicht gut. Hier geht es um durchschnittliche F\u00e4lle. Um die typischen Leistungswerte, sodass du etwas messen kannst, das du f\u00fcr eine gute, typische Leistung h\u00e4ltst \u2013 schlie\u00dflich arbeitest du an deren Verbesserung! Die Registerallokation ist ein Thema, das sich ganz auf die Leistung konzentriert. Sobald du einen ersten Prototyp hast, der funktioniert und das macht, was n\u00f6tig ist, beginnt die Arbeit an der Performance. Du musst lernen, gut zu messen. Warum ist das wichtig? Wenn es klare Daten gibt, kannst du verschiedene Bereiche betrachten und sehen: Aha, das hat hier geholfen, aber dort ist alles kaputt! Es kommen einige gute Ideen auf, du f\u00fcgst eine neue Heuristik hinzu, und pl\u00f6tzlich funktioniert alles im Durchschnitt ein wenig besser. Oder es funktioniert nicht. Ich hatte viele F\u00e4lle, in denen wir um f\u00fcnf Prozent Leistung gek\u00e4mpft haben, die unsere Entwicklung von dem vorherigen Allokator unterschieden. Und jedes Mal sieht es so aus: irgendwo gewonnene, irgendwo verlorene. Wenn du gute Werkzeuge zur Leistungsanalyse hast, kannst du die schlechten Ideen finden und verstehen, warum sie verlieren. Vielleicht solltest du alles so belassen, wie es ist, oder vielleicht ernsthaft an der Feinabstimmung arbeiten, oder etwas anderes reparieren. Das ist ein ganzes Set von Dingen! Ich habe diesen coolen Hack gemacht, aber du brauchst auch diesen, und diesen, und diesen \u2013 und deren Kombination f\u00fchrt zu bestimmten Verbesserungen. Einzelne k\u00f6nnen scheitern. So ist die Natur der Arbeit an der Leistung NP-vollst\u00e4ndiger Probleme.<\/p>\n<p><\/p>\n<p><strong>Wladimir<\/strong>: Es scheint, als ob Themen wie das Lackieren in Allokatoren bereits gel\u00f6st sind. Nun, f\u00fcr Sie scheint es gel\u00f6st zu sein, wenn man Ihren Erz\u00e4hlungen glaubt. Ist es dann \u00fcberhaupt sinnvoll\u2026<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Sie ist nicht als solche gel\u00f6st. Du musst sie in eine \u201egel\u00f6ste\u201c umwandeln. Es gibt schwierige Aufgaben, und die m\u00fcssen gel\u00f6st werden. Wenn das erledigt ist, ist es Zeit, sich auf die Leistung zu konzentrieren. Diese Arbeit sollte mit dem entsprechenden Ernst angegangen werden \u2013 Benchmarks durchf\u00fchren, Metriken sammeln, Situationen erl\u00e4utern, in denen beim Zur\u00fccksetzen auf die vorherige Version dein alter Hack wieder funktioniert hat (oder umgekehrt, nicht mehr funktioniert). Und nicht aufgeben, bis du etwas erreicht hast. Wie ich schon sagte, es gibt viele gro\u00dfartige Ideen, die nicht funktioniert haben, aber im Bereich der Registerallokierung sind die Ideen nahezu unbegrenzt. Du kannst beispielsweise wissenschaftliche Ver\u00f6ffentlichungen lesen. Obwohl sich dieser Bereich mittlerweile langsamer entwickelt hat und klarer geworden ist als in seinen Anfangszeiten. Trotzdem arbeiten in diesem Bereich unz\u00e4hlige Menschen, und es lohnt sich, all ihre Ideen auszuprobieren, sie warten alle auf ihre Gelegenheit. Und du kannst nicht beurteilen, wie gut sie sind, wenn du es nicht ausprobierst. Wie gut sie sich in dein gesamtes Allokator-Setup integrieren, denn ein Allokator macht viele Dinge, und einige Ideen funktionieren in deinem spezifischen Allokator nicht, w\u00e4hrend sie in einem anderen problemlos funktionieren k\u00f6nnen. Der Hauptansatz zum Erfolg f\u00fcr einen Allokator besteht darin, langsame Dinge aus dem Hauptweg zu entfernen und zwanghaft an den Grenzen langsamer Wege zu splitten. Wenn du also GC ausl\u00f6sen, den langsamen Weg gehen, deoptimieren und Ausnahmen werfen m\u00f6chtest, all das \u2013 du wei\u00dft, dass diese Dinge relativ selten sind. Und sie sind wirklich selten, ich habe es \u00fcberpr\u00fcft. Du leistest zus\u00e4tzliche Arbeit, und dadurch verschwinden viele Einschr\u00e4nkungen auf diesen langsamen Wegen, aber das ist nicht sehr wichtig, denn sie sind langsam und werden selten benutzt. Zum Beispiel, ein Nullzeiger \u2013 der passiert nie, richtig? Man muss verschiedene Wege f\u00fcr unterschiedliche Dinge haben, aber sie sollten sich nicht im Hauptweg st\u00f6ren.\u00a0<\/p>\n<p><\/p>\n<p><strong>Wladimir<\/strong>: Was halten Sie von Multithreading, wenn Tausende von Kernen vorhanden sind? Ist das n\u00fctzlich?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Der Erfolg von GPUs zeigt, dass es ziemlich n\u00fctzlich ist!<\/p>\n<p><\/p>\n<p><strong>Wladimir<\/strong>: Sie sind ziemlich spezialisiert. Wie sieht es mit allgemeinen Prozessoren aus?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Nun, das war das Gesch\u00e4ftsmodell von Azul. Die Antwort kam noch aus einer Zeit, als die Menschen sehr auf vorhersehbare Leistung wertlegten. Damals war es schwierig, parallelen Code zu schreiben. Das H2O-Coding-Modell skaliert gut, ist aber kein allgemeines Modell. Vielleicht etwas allgemeiner als bei der Nutzung von GPUs. Sprechen wir \u00fcber die Komplexit\u00e4t der Entwicklung solcher Systeme oder \u00fcber die Nutzungskomplexit\u00e4t? Zum Beispiel, eine interessante Lektion, die ich von Azul gelernt habe, ist: Kleine Caches sind in Ordnung.\u00a0<\/p>\n<p><\/p>\n<h1 id=\"samyy-bolshoy-chellenzh-v-zhizni\">Die gr\u00f6\u00dfte Herausforderung im Leben<\/h1>\n<p><\/p>\n<p><strong>Wladimir<\/strong>: Wie steht es um nicht-technische Herausforderungen?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Die gr\u00f6\u00dfte Herausforderung bestand darin, nicht nett und freundlich zu den Menschen zu sein. Infolgedessen fand ich mich st\u00e4ndig in extrem konfliktreichen Situationen wieder. Situationen, in denen ich wusste, dass alles schiefgeht, aber nicht wusste, wie ich mit diesen Problemen vorankommen sollte und nicht in der Lage war, sie zu bew\u00e4ltigen. Viele langandauernde Probleme, die \u00fcber Jahrzehnte hinweg entstanden sind, haben sich genau so entwickelt. Die Tatsache, dass es in Java die Compiler C1 und C2 gibt, ist eine direkte Folge davon. Ebenso, dass es in Java \u00fcber ein Jahrzehnt lang keine mehrstufige Kompilierung gab. Es war offensichtlich, dass wir ein solches System ben\u00f6tigten, aber es war nicht klar, warum es nicht vorhanden war. Ich hatte Probleme mit einem Ingenieur... oder einer Ingenieursgruppe. Vor langer Zeit, als ich bei Sun anfing, war ich... Nun, nicht nur damals, ich hatte stets meine eigene Meinung zu allem. Und ich hielt es f\u00fcr wahr, dass man einfach seine eigene Wahrheit direkt aussprechen kann. Zumal ich die meiste Zeit erschreckend oft recht hatte. Und wenn dir dieser Ansatz nicht gef\u00e4llt... insbesondere wenn du offensichtlich falsch liegst und Unsinn machst... Insgesamt konnten nur wenige Menschen mit dieser Kommunikationsform tolerant umgehen. Obwohl einige es konnten, zum Beispiel ich. Mein ganzes Leben habe ich auf meritokratischen Prinzipien aufgebaut. Wenn du mir etwas Falsches zeigst, werde ich mich sofort umdrehen und sagen: Du hast Unsinn gesagt. Dabei entschuldige ich mich nat\u00fcrlich und all das, erkenne die Verdienste an, wenn es welche gibt, und handel dann entsprechend. Auf der anderen Seite habe ich erstaunlich oft recht, was in zwischenmenschlichen Beziehungen nicht sehr gut funktioniert. Ich versuche nicht, nett zu sein, sondern pr\u00e4sentiere die Sache direkt. \u201eDas wird niemals funktionieren, weil eins, zwei und drei\u201c. Und sie sagen: \u201eOh!\u201c. Es gab auch andere Konsequenzen, die ich wahrscheinlich besser auslasse: zum Beispiel die, die zu meiner Scheidung von meiner Frau gef\u00fchrt haben und zu einem Jahrzehnt Depressionen danach.<\/p>\n<p><\/p>\n<p>Eine Herausforderung besteht darin, mit den Vorstellungen der Menschen dar\u00fcber zu k\u00e4mpfen, was man kann und was nicht, was wichtig ist und was nicht. Es gab viele Herausforderungen zum Thema Programmierstil. Ich schreibe weiterhin viel Code, und damals musste ich sogar langsamer werden, weil ich zu viele parallele Aufgaben gemacht habe und sie schlecht erledigte, anstatt mich auf eine einzige zu konzentrieren. R\u00fcckblickend habe ich die H\u00e4lfte des Codes f\u00fcr das Java JIT-Team oder das C2-Team geschrieben. Der schnellste Programmierer nach mir war schon halb so langsam, der n\u00e4chste war nochmals halb so langsam, und das war ein exponentieller R\u00fcckgang. Die siebte Person in dieser Reihe war extrem langsam \u2013 das ist oft der Fall! Ich habe viel Code durchgesehen. Ich habe beobachtet, wer was schreibt, ohne Ausnahmen. Ich habe mir ihren Code angesehen, alle von ihnen bewertet und immer noch mehr geschrieben als jeder von ihnen. Mit Menschen funktioniert so ein Ansatz oft nicht gut. Einige m\u00f6gen das nicht. Und wenn sie damit nicht umgehen k\u00f6nnen, entstehen allerhand Beschwerden. Zum Beispiel wurde mir einmal gesagt, ich solle aufh\u00f6ren, Code zu schreiben, weil ich zu viel schreibe und das die Teamdynamik gef\u00e4hrdet, was sich f\u00fcr mich wie ein Witz anh\u00f6rte: Wenn das gesamte restliche Team verschwinden w\u00fcrde und ich weiterhin Code schreibe, w\u00fcrdest du nur die H\u00e4lfte des Teams verlieren. Auf der anderen Seite, wenn ich weiterschreibe und du die H\u00e4lfte des Teams verlierst \u2013 das klingt nach sehr schlechtem Management. Ich habe nie wirklich dar\u00fcber nachgedacht oder es angesprochen, aber es war immer irgendwo in meinem Kopf. Irgendwo in den Hintergr\u00fcnden meines Bewusstseins kreiste der Gedanke: \u201eMacht ihr Witze?\u201c Also war mein gr\u00f6\u00dftes Problem ich und meine Beziehungen zu den Menschen. Heute verstehe ich mich viel besser. Ich habe lange Zeit als Teamleiter f\u00fcr Programmierer gearbeitet, und jetzt sage ich den Leuten direkt: \u201eWisst ihr, so bin ich, und ihr m\u00fcsst damit umgehen \u2013 ist es in Ordnung, wenn ich hier stehe?\u201c Und als sie damit klarkamen, hat alles funktioniert. Tats\u00e4chlich bin ich weder schlecht noch gut; ich habe keine schlechten Absichten oder egoistischen Bestrebungen, es ist einfach mein Wesen, und man muss irgendwie damit leben.<\/p>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Vor kurzem wurde viel \u00fcber Selbstbewusstsein f\u00fcr Introvertierte und generell \u00fcber Soft Skills gesprochen. Was kann man dazu sagen?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Ja, das war ein Verst\u00e4ndnis und eine Lektion, die ich aus meiner Scheidung mit meiner Frau gezogen habe. Was ich aus der Scheidung gewonnen habe, ist das Verst\u00e4ndnis meiner selbst. Dadurch begann ich, andere Menschen besser zu verstehen. Ich lernte, wie diese Interaktionen funktionieren. Das f\u00fchrte zu Einsichten, eine nach der anderen. Es entstand das Bewusstsein, wer ich bin und was ich darstelle. Was ich tue: Entweder bin ich mit der Aufgabe besch\u00e4ftigt, oder ich vermeide Konflikte, oder etwas anderes \u2013 und ein solches Ma\u00df an Selbstbewusstsein hilft wirklich, sich selbst zu kontrollieren. Danach geht alles viel einfacher. Eine Sache, die ich nicht nur bei mir, sondern auch bei anderen Programmierern entdeckt habe, ist die Unf\u00e4higkeit, Gedanken zu verbalisieren, wenn man sich in einem Zustand emotionalen Stresses befindet. Zum Beispiel, man sitzt da und programmiert, ist im Flow, und pl\u00f6tzlich kommen Leute angehetzt und schreien in Panik, dass etwas kaputt ist und sofortige Ma\u00dfnahmen ergriffen werden m\u00fcssen. Und man kann kein Wort sagen, weil man sich in einem emotionalen Stresszustand befindet. Das erworbene Wissen hilft, sich auf diesen Moment vorzubereiten, ihn zu \u00fcberstehen und zu einem R\u00fcckzugsplan \u00fcberzugehen, nach dem man etwas unternehmen kann. Also ja, wenn man anf\u00e4ngt zu verstehen, wie all das funktioniert \u2013 es ist ein riesiges, lebensver\u00e4nderndes Ereignis.\u00a0<br \/>\nIch konnte die richtigen Worte nicht finden, erinnerte mich jedoch an die Abfolge der Schritte. Der Punkt ist, dass diese Reaktion sowohl physisch als auch verbal ist und dass du Raum brauchst. Einen solchen Raum im zenartigen Sinne. Genau das muss erkl\u00e4rt werden, und dann musst du sofort einen Schritt zur\u00fccktreten \u2013 physisch zur\u00fccktreten. Wenn ich in Worten schweige, kann ich die Situation emotional verarbeiten. W\u00e4hrend das Adrenalin das Gehirn erreicht und dich in den \"Kampf oder Flucht\"-Modus versetzt, kannst du nichts mehr sagen; du bist jetzt ein Idiot, ein Pr\u00fcgelknabe, unf\u00e4hig, eine w\u00fcrdige Antwort zu geben oder wenigstens die Attacke zu stoppen, und der Angreifer kann immer wieder angreifen. Zuerst musst du wieder du selbst werden, die Kontrolle zur\u00fcckerlangen und aus dem \"Kampf oder Flucht\"-Modus herauskommen. <\/p>\n<p><\/p>\n<p>Und daf\u00fcr ist verbalem Raum erforderlich. Einfach nur Raum, der frei ist. Wenn du \u00fcberhaupt etwas sagen m\u00f6chtest, kannst du genau das behaupten, und dann gehen und tats\u00e4chlich deinen \"Raum\" finden: spaziere durch den Park, sperre dich unter der Dusche ein \u2013 ganz gleich. Wichtig ist, sich vor\u00fcbergehend von der Situation zu l\u00f6sen. Sobald du auch nur f\u00fcr ein paar Sekunden abschaltest, kehrt die Kontrolle zur\u00fcck, und du beginnst, klar zu denken. \"Gut, ich bin ja kein Idiot, ich mache keine dummen Dinge, ich bin ein ziemlich n\u00fctzlicher Mensch.\" Sobald du dich selbst davon \u00fcberzeugen konntest, ist es Zeit, zum n\u00e4chsten Schritt \u00fcberzugehen: herauszufinden, was passiert ist. Du wurdest angegriffen, der Angriff kam aus unerwarteter Richtung, es war ein hinterh\u00e4ltiger Schlag. Das ist schlecht. Der n\u00e4chste Schritt besteht darin, zu verstehen, warum der Angreifer das n\u00f6tig hatte. Tats\u00e4chlich, warum? Vielleicht, weil er selbst w\u00fctend ist? Warum ist er w\u00fctend? Zum Beispiel, weil er selbst versagt hat und die Verantwortung nicht \u00fcbernehmen kann? So muss man vorsichtig die gesamte Situation aufarbeiten. Aber daf\u00fcr braucht man Raum f\u00fcr Man\u00f6ver, verbalem Raum. Der erste Schritt besteht darin, den verbalen Kontakt abzubrechen. Sich von der Wortdiskussion zu entfernen. Dies abzubrechen, so schnell wie m\u00f6glich wegzugehen. Wenn es sich um ein Telefongespr\u00e4ch handelt \u2013 lege einfach auf \u2013 das ist eine F\u00e4higkeit, die ich aus der Kommunikation mit meiner Ex-Frau gelernt habe. Wenn das Gespr\u00e4ch zu nichts Gutem f\u00fchrt, sag einfach \"Auf Wiedersehen\" und leg auf. Auf der anderen Seite des Telefons: \"bla-bla-bla\", du antwortest: \"hm, tsch\u00fcss!\" und legst auf. Du brichst einfach das Gespr\u00e4ch ab. F\u00fcnf Minuten sp\u00e4ter, wenn du wieder klar denken kannst, hast du dich ein wenig abgek\u00fchlt, und es wird m\u00f6glich, \u00fcber das, was passiert ist und was als N\u00e4chstes kommt, nachzudenken. Und du beginnst, eine durchdachte Antwort zu formulieren, statt nur emotional zu reagieren. F\u00fcr mich war der Durchbruch im Selbstbewusstsein genau das, dass ich in Zeiten emotionalen Stresses nicht sprechen kann. Aus diesem Zustand herauszukommen, nachzudenken und zu planen, wie man antwortet und Probleme ausgleicht \u2013 das sind die richtigen Schritte, wenn man nicht sprechen kann. Der einfachste Weg ist, aus der Situation zu entfliehen, in der emotionaler Stress entsteht, und einfach nicht mehr an diesem Stress teilzunehmen. Danach gewinnst du die F\u00e4higkeit, zu denken, und wenn du denken kannst, wird das Sprechen ebenfalls m\u00f6glich und so weiter.<\/p>\n<p><\/p>\n<p>\u00dcbrigens versucht der Anwalt der Gegenseite im Gericht, dies mit dir zu tun \u2013 jetzt wird klar, warum. Denn er hat die M\u00f6glichkeit, dich so zu \u00fcberw\u00e4ltigen, dass du nicht einmal in der Lage bist, deinen eigenen Namen auszusprechen. Im wahrsten Sinne des Wortes kannst du nicht sprechen. Wenn dir das passiert und du wei\u00dft, dass du an einem Ort sein wirst, wo verbale K\u00e4mpfe toben, wie im Gerichtssaal, dann solltest du mit deinem Anwalt kommen. Der Anwalt wird dich vertreten und die verbale Attacke stoppen, und das auf ganz legale Weise, sodass du dein verlorenes Zen-Gef\u00fchl zur\u00fcckgewinnst. Zum Beispiel musste ich ein paar Mal meine Familie anrufen, der Richter war dabei durchaus freundlich, doch der Anwalt der Gegenseite schrie und schrie mich an, ich konnte nicht einmal ein Wort einf\u00fcgen. In solchen F\u00e4llen funktioniert f\u00fcr mich am besten, einen Vermittler zu nutzen. Der Vermittler stoppt den konstanten Druck, der ununterbrochen auf dir lastet; du findest den notwendigen Raum f\u00fcr dein Zen, und mit ihm kommt auch die F\u00e4higkeit zur\u00fcck zu sprechen. Das ist ein ganzes Wissensgebiet, in dem man viel lernen und in sich selbst entdecken muss, und all das f\u00fchrt zu hochgradigen strategischen Entscheidungen, die f\u00fcr verschiedene Menschen unterschiedlich sind. Manche Menschen haben die oben beschriebenen Probleme nicht, normalerweise haben sie keine, wenn sie professionell im Verkauf t\u00e4tig sind. All diese Menschen, die ihren Lebensunterhalt mit Worten verdienen \u2013 ber\u00fchmte S\u00e4nger, Dichter, religi\u00f6se F\u00fchrer und Politiker, sie haben immer etwas zu sagen. Sie haben keine solchen Probleme, w\u00e4hrend ich sie habe.<\/p>\n<p><\/p>\n<p><strong>Andrei<\/strong>: Es war\u2026 unerwartet. Gut, wir haben bereits viel gesprochen und es ist Zeit, dieses Interview zu beenden. Wir werden uns auf der Konferenz treffen und diesen Dialog fortsetzen. Treffen wir uns auf der Hydra!<\/p>\n<p><\/p>\n<blockquote><p>Die Unterhaltung mit Cliff wird auf der Hydra 2019 Konferenz fortgesetzt, die am 11. und 12. Juli 2019 in Sankt Petersburg stattfinden wird. Er wird mit einem Vortrag kommen <noindex><a rel=\"nofollow\" href=\"https:\/\/hydraconf.com\/2019\/talks\/2jix5mst7iduyp9linqhfj\/?utm_source=habr&amp;utm_medium=45871\">\u00abDie Azul Hardware Transactional Memory Erfahrung\u00bb<\/a><\/noindex>. Tickets sind erh\u00e4ltlich <noindex><a rel=\"nofollow\" href=\"https:\/\/hydraconf.ru\/?utm_source=habr&amp;utm_medium=458718\">von der offiziellen Webseite<\/a><\/noindex>.<\/p><\/blockquote>\n<p>Quelle: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/jugru\/blog\/458718\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041a\u043b\u0438\u0444\u0444 \u041a\u043b\u0438\u043a \u2014 CTO \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 Cratus (IoT \u0441\u0435\u043d\u0441\u043e\u0440\u044b \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432), \u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438 \u0441\u043e\u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u043e\u0432 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f Rocket Realtime School, Neurensic \u0438 H2O.ai) \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c\u0438 \u044d\u043a\u0437\u0438\u0442\u0430\u043c\u0438. \u041a\u043b\u0438\u0444\u0444 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432 15 \u043b\u0435\u0442 (Pascal \u0434\u043b\u044f TRS Z-80)! \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d \u0437\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u0434 \u04212 \u0432 Java (the Sea of Nodes IR). \u042d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043f\u043e\u043a\u0430\u0437\u0430\u043b [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-35851","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041a\u043b\u0438\u0444\u0444 \u041a\u043b\u0438\u043a \u2014 CTO \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 Cratus (IoT \u0441\u0435\u043d\u0441\u043e\u0440\u044b \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432), \u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438 \u0441\u043e\u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u043e\u0432 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f Rocket Realtime School, Neurensic \u0438 H2O.ai) \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c\u0438 \u044d\u043a\u0437\u0438\u0442\u0430\u043c\u0438. \u041a\u043b\u0438\u0444\u0444 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432 15 \u043b\u0435\u0442 (Pascal \u0434\u043b\u044f TRS Z-80)! \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d \u0437\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u0434 \u04212 \u0432 Java (the Sea of Nodes IR). \u042d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0411\u043e\u043b\u044c\u0448\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u044c\u044e \u0441 \u041a\u043b\u0438\u0444\u0444\u043e\u043c \u041a\u043b\u0438\u043a\u043e\u043c \u2014 \u043e\u0442\u0446\u043e\u043c JIT-\u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 \u0432 Java | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041a\u043b\u0438\u0444\u0444 \u041a\u043b\u0438\u043a \u2014 CTO \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 Cratus (IoT \u0441\u0435\u043d\u0441\u043e\u0440\u044b \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432), \u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438 \u0441\u043e\u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u043e\u0432 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f Rocket Realtime School, Neurensic \u0438 H2O.ai) \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c\u0438 \u044d\u043a\u0437\u0438\u0442\u0430\u043c\u0438. \u041a\u043b\u0438\u0444\u0444 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432 15 \u043b\u0435\u0442 (Pascal \u0434\u043b\u044f TRS Z-80)! \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d \u0437\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u0434 \u04212 \u0432 Java (the Sea of Nodes IR). \u042d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:07:03+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:07:03+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Gro\u00dfes Interview mit Cliff Click \u2014 dem Vater der JIT-Kompilierung in Java | ProHoster","description":"Cliff Click \u2014 CTO von Cratus (IoT-Sensoren zur Verbesserung der Prozesse), Gr\u00fcnder und Mitgr\u00fcnder mehrerer Start-ups (darunter Rocket Realtime School, Neurensic und H2O.ai) mit mehreren erfolgreichen Exits. Cliff schrieb seinen ersten Compiler mit 15 Jahren (Pascal f\u00fcr TRS Z-80)! Am bekanntesten f\u00fcr seine Arbeit an C2 in Java (der Sea of Nodes IR). Dieser Compiler zeigte","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0411\u043e\u043b\u044c\u0448\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u044c\u044e \u0441 \u041a\u043b\u0438\u0444\u0444\u043e\u043c \u041a\u043b\u0438\u043a\u043e\u043c \u2014 \u043e\u0442\u0446\u043e\u043c JIT-\u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 \u0432 Java | ProHoster","og:description":"\u041a\u043b\u0438\u0444\u0444 \u041a\u043b\u0438\u043a \u2014 CTO \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 Cratus (IoT \u0441\u0435\u043d\u0441\u043e\u0440\u044b \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432), \u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438 \u0441\u043e\u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u043e\u0432 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f Rocket Realtime School, Neurensic \u0438 H2O.ai) \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c\u0438 \u044d\u043a\u0437\u0438\u0442\u0430\u043c\u0438. \u041a\u043b\u0438\u0444\u0444 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432 15 \u043b\u0435\u0442 (Pascal \u0434\u043b\u044f TRS Z-80)! \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d \u0437\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u0434 \u04212 \u0432 Java (the Sea of Nodes IR). \u042d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043f\u043e\u043a\u0430\u0437\u0430\u043b","og:url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:07:03+00:00","article:modified_time":"2019-10-31T19:07:03+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"35851","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-22 01:01:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:55:25","updated":"2026-01-22 01:01:19"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/35851","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=35851"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/35851\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=35851"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=35851"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=35851"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}