Erfolg und Scheitern von Ninja (2020)
(neugierig.org)- Ninja, 2011 veröffentlicht, begann als ein Make-ähnliches Build-System und wurde zu einem repräsentativen Open-Source-Erfolgsfall, der sich bis zu Chrome, Teilen von Android, Meson und CMake-basierten Projekten verbreitete
- Entscheidend für Erfolg und Misserfolg waren weniger der Code selbst als vielmehr die Architektur und darüber hinaus soziale Fragen wie Wartung und Nutzererwartungen
- Ninja liest die in
ninja.buildnotierten Befehle, Eingaben und Ausgaben, prüft die Änderungszeitpunkte von Dateien und führt die nötigen Schritte parallel aus, wobei der Fokus auf schnellen inkrementellen Builds großer Projekte liegt - Die wichtigste Designentscheidung war, keine High-Level-Build-Funktionen selbst zu enthalten, sondern ein Executor für den Action Graph zu bleiben und komplexe Entscheidungen den Generatoren zu überlassen
- Die CMake-Integration, Windows-Unterstützung und parallele Ausführung als Standard förderten die Verbreitung, doch Anforderungen und Beiträge, die mit den Designzielen kollidierten, blieben eine große Last für die Open-Source-Wartung
Der Kontext, in dem Ninja weit verbreitet wurde
- Ninja ist ein Make-ähnliches Build-System, das vor etwa 9 Jahren veröffentlicht wurde; anfangs war es ein etwas peinlich geteiltes Side Project, wurde später aber breit eingesetzt
- Wichtige Einsatzfälle sind unter anderem:
- Chrome entfernte schließlich alle Nicht-Ninja-Builds
- Android verwendet Ninja für einige große Komponenten des Systems
- Das Projekt Meson verwendet Ninja und scheint in der Welt freier Software ein zunehmend genutztes Build-System zu sein
- Viele Projekte nutzen Ninja zusammen mit CMake, und auch die Swift-Build-Anleitung verlangt eine Ninja-Installation
- Ninja wurde 2011 veröffentlicht, 2014 ging das Eigentum am Projekt über und später wurde es an einen dritten Maintainer weitergegeben
- Die zentrale Erfahrung dieser Rückschau ist, dass Architektur mehr Einfluss hat als Code und soziale Themen noch mehr Einfluss haben als Architektur
Grundlegende Arbeitsweise für schnelle Builds
- Was Ninja tut, ist vergleichsweise einfach
- Der Benutzer trägt in die Datei
ninja.builddie auszuführenden Befehle, die von jedem Befehl konsumierten Dateien und die erzeugten Dateien ein - Ninja liest diese Datei und prüft die Änderungszeitpunkte mehrerer Dateien
- Es führt die Befehle parallel aus, die nötig sind, um einen aktuellen Zustand herzustellen
- Der Benutzer trägt in die Datei
- Im Vergleich zu Make bietet die Eingabe-Build-Sprache weniger Funktionen und konzentriert sich stattdessen auf eine Struktur, die wenige Funktionen sehr schnell ausführt
- Der Ausführungsablauf gliedert sich in drei Schritte
- Parsen und Interpretieren der Build-Datei
- Prüfen der Änderungszeitpunkte der Eingabedateien
- Ausführen der benötigten Befehle
- Ziel ist es, auch bei großen Projekten mit mehr als 100.000 Eingabedateien den dritten Schritt so schnell wie möglich zu erreichen
- Als Beispiel für Optimierungen bildet Ninja Eingabedateipfade möglichst früh auf eindeutige Speicherobjekte ab und verwendet später für Pfadgleichheit Pointer-Vergleiche
- Weitere Low-Level-Performance-Details behandelt das Ninja-Kapitel in The Performance of Open Source Software
- Im Laufe der Jahre gab es auch Reimplementierungen von Ninja
- Eine interessante 20%-Implementierung ist leicht, aber die restlichen 80% bestehen aus vielen Details, und eine schnellere Implementierung ist nicht bekannt
Wichtige Designentscheidungen
-
Graph-Darstellung
- Make kann schlecht mit Build-Regeln umgehen, bei denen eine einzelne Regel mehrere Dateien erzeugt
- Ninja verwendet keinen Graphen zwischen Dateien, sondern einen bipartiten Graphen zwischen Dateien und Befehlen
- Dateiknoten bilden Kanten zu Befehlsknoten, und Befehlsknoten haben wiederum Kanten zu Ausgabedateien
- Diese Struktur erfasst die tatsächliche Form eines Builds besser
- Wenn sich eine der Eingaben ändert, ist der Befehl veraltet, und wenn der Befehl ausgeführt wird, werden alle Ausgaben aktualisiert
- Eine bestimmte Datei darf höchstens eine eingehende Kante haben
- Auch die Befehlszeile selbst kann als Eingabe eines Befehlsknotens gesehen werden; ändern sich Flags der Befehlszeile, werden Befehl und Ausgaben veraltet
-
Deps-Log und C-Header-Abhängigkeiten
- Um C-Header-Abhängigkeiten korrekt zu behandeln, muss zusätzliche Abhängigkeitsinformation verarbeitet werden, die der C-Compiler erzeugt
- Nach einem Abwägen zwischen Datenbankeinführung und Einfachheit entstand das Deps-Log-Format
- Dieses Format ist recht kompakt, aber in entscheidender Hinsicht noch immer fehlerhaft
-
End-to-End- / Crash-only-Design
- Ninja ist kein dauerhaft laufender Daemon-Prozess, sondern beginnt bei jeder Ausführung von vorn
- Diese Wahl ist von dem End-to-End-Prinzip und Crash-only Software beeinflusst
- Wenn der Pfad vom Start weg schnell genug gemacht wird, braucht es keinen separaten „Online“-Codepfad
- Bei Projekten, die im Speicher resident bleiben können, wird die Start-Performance am Ende oft vernachlässigt
-
Prüfung des Dateizustands
- Ein Grund für die Erwartung, dass Build-Tools im Speicher resident sein sollten, ist das Caching des Dateizustands auf der Platte
- Tatsächlich cached der Kernel diese Information bereits im Speicher, und erneutes Caching im User Space spart meist nicht viel
- Unter Linux ist das Abrufen des Dateizustands sehr schnell, und Ninja erledigt das in einem einzelnen Thread
- Selbst auf Maschinen, die vor 10 Jahren als „schnell“ galten, lassen sich
stat-Aufrufe für 30.000 Dateien in wenigen Dutzend Millisekunden ausführen
-
Kompromiss zwischen Größenordnung und Spezifikation
- Als Faustregel gilt: eine Verdopplung des Maßstabs kann man mit Optimierungen bewältigen, eine Verzehnfachung erfordert eine Re-Architektur
- Ninja wurde mit Blick auf den Chrome-Build entworfen, der damals etwa 30.000 Build-Schritte hatte
- Heute wird es auch in kleineren Umgebungen verwendet, dort ist der Geschwindigkeitsvorteil aber womöglich nicht nötig
- In größeren Umgebungen wie Android-Builds stößt es an Skalierungsgrenzen, sodass womöglich ein anderer Ansatz nötig ist
- Ninja verlangt vom Benutzer genügend Informationen für parallele Ausführung, zwingt aber nicht dazu, den gesamten Build vollständig zu kennen
- Zwischen Korrektheit, Komfort und Performance gibt es Trade-offs; ein Tool, das zugunsten des Komforts etwas Korrektheit opfert, kann im gesamten Ökosystem am Ende richtigere Ergebnisse liefern als ein genaueres, aber unbequemes Tool
Die durch die „Assembler“-Metapher entstandene Trennung
- Build-Systeme bieten viele verschiedene High-Level-Funktionen, und die Art, wie sich einzelne Tools beschreiben, ist so breit gefächert, dass ein Vergleich schwerfällt
- Die Kernerkenntnis von Ninja war, dass ein Build-System unabhängig von den angebotenen High-Level-Funktionen am Ende einen Action Graph aufbauen muss, um Dateien aktuell zu halten
- Ninja führt nur diesen Action Graph aus und überlässt dem Benutzer, welchen Generator er darüber setzt
- Die Trennung dieser beiden Programme passte ursprünglich gut zum Chrome-Projekt, wurde später aber Ninjas wichtigster Beitrag
- Die Vorteile sind zweifach
- Ninja selbst bleibt einfach und schnell
- Teure Arbeiten wie das Globben von
*.cwerden auf die Generator-Seite verschoben
- Anders als Build-Systeme, die alles in einem Schritt erledigen, führt die Ninja-Struktur dazu, dass der berechnete Action Graph als Snapshot auf der Platte gespeichert wird
- Dadurch entsteht faktisch eine Form des Cachings des Action Graphs zwischen Builds
- Generatoren können beliebig High-Level sein
- So ist es etwa möglich, den gesamten Source Tree zu globben und Dateien mit
testim Namen als Tests zu identifizieren
- So ist es etwa möglich, den gesamten Source Tree zu globben und Dateien mit
- Entwickler müssen selbst entscheiden, wofür sie Kosten bezahlen wollen
- Wenn ein Generator die gesamte Platte globbt, ist das möglich, aber dann wird auch deutlicher, warum dieser Build langsam ist
- Die Trennung zwischen Generator und Action Graph ist in der Praxis nicht wirklich einfach, und auch in Ninja gibt es viele Details dazu, welche Schicht welche Aufgabe übernehmen soll
- Auch Xcode- oder Visual-Studio-Build-Systeme könnten theoretisch auf die gleiche Weise vorab berechnen und das Ergebnis als Snapshot speichern, doch die Versuchung, die Schichten zu vermischen, scheint dem im Weg zu stehen
- Make versuchte, Globbing, Variablenersetzung, Substrings und Funktionen für Programmierer in einem Tool zu vereinen, und wurde so zu einer Sprache, die zu schwach ist, um alle nötigen Funktionen auszudrücken, aber stark genug, um langsame Makefiles zu schreiben
- Ninja wollte genau diese Richtung vermeiden
Standardwerte bestimmen die wahrgenommene Performance
- Ninja führt gewünschte Befehle standardmäßig parallel aus
- Auch Make kann mit dem Flag
-jparallel arbeiten, standardmäßig läuft es aber seriell - Makefiles werden leicht so geschrieben, dass Abhängigkeiten nicht vollständig genug beschrieben sind, um sichere parallele Ausführung zu erlauben
- Ninja führt selbst auf Single-Core-Systemen immer parallel aus und legt solche Fehler dadurch früher offen
- Programme, die gut zu Ninja passen, werden meist auch für parallele Builds sicher
- Ninja besitzt kein fortgeschrittenes System zur Erkennung falscher Abhängigkeiten; vielmehr treten fehlerhafte Builds häufiger auf und machen das Problem sichtbar
- Weil Benutzer das Parallel-Flag von Make oft vergessen oder gar nicht kennen, kann sich Ninja allein durch diesen Standardwert in der Praxis „mehr als doppelt so schnell“ anfühlen wie Make
- Optimierungen, die Benutzer tatsächlich nie erleben, haben nur begrenzten Wert
Wichtige Metriken bei Geschwindigkeit
- Performance eines Build-Systems kann vieles bedeuten
- Wie lange ein vollständiger Build von Grund auf dauert
- Wie lange es dauert, nach einem bereits erfolgten Build eine Datei zu ändern und erneut zu bauen
- Ninja konzentriert sich auf die inkrementellen Builds großer Codebasen und darauf, den Edit-Compile-Loop zu beschleunigen
- Als Ninja ursprünglich entstand, war die Erinnerung an blaze, also bazel, als sehr schnelles System ein Antrieb, diese Geschwindigkeit zu erreichen
- Im Rückblick war blaze bei den eigentlich interessierenden Metriken aber nicht besonders schnell, und als Java-Programm war selbst die Ausgabe der Hilfe langsam
- Der Fokus auf inkrementelle Builds lag daran, dass die Dauer von Iterationen als stark prägend für die Zufriedenheit von Programmierern gesehen wurde
- Ninja wird im Edit-Compile-Loop verwendet, und dort ist der Unterschied zwischen 1 Sekunde und 4 Sekunden wichtig
- Die Bedeutung von „schnell“ ist dem Benutzer schwer zu vermitteln, und das Ninja-Handbuch warnt, dass sich der Geschwindigkeitsvorteil bei kleinen Projekten kaum bemerkbar machen kann
- Dennoch verkauft sich „schnell“ gut, weshalb auch Nutzer kleiner Apps, die vom Funktionsmangel frustriert sind, Ninja verwenden wollen
- Ninja konzentrierte sich zwar auf inkrementelle Rebuilds, aber einige Benutzer berichteten auch von besserer Full-Build-Performance
- Weil Ninja fast nichts tut, verbraucht es während des Builds wenig CPU und nimmt den eigentlichen Build-Schritten weniger Rechenzeit weg
- Auch die Ausgabe ist sehr knapp
- Erfolgreiche Builds geben meist nur eine einzige Zeile aus
- Andere Build-Systeme geben viele Schritte und Timing-Informationen aus und können dadurch schwerfälliger wirken
- Diese wortarme Struktur lässt Ninja stärker „unsichtbar“ wirken
CMake-Integration und Windows-Unterstützung als Wachstumstreiber
-
CMake
- Ninja wurde ursprünglich für die Verwendung mit dem speziellen Build-System von Chrome entwickelt
- Peter Collingbourne verband Ninja mit dem weiter verbreiteten CMake-Build-System
- Diese Integration begann für LLVM-Arbeit und erforderte neue Semantik nicht nur in CMake, sondern auch auf Ninja-Seite
- Peter wird als die Person genannt, die den größten Anteil am realen Erfolg von Ninja hatte
- Später übernahmen die CMake-Autoren die Integration, konnten auf Anfragen und Bedenken aber nicht ausreichend eingehen
- Der Entwickler selbst hat CMake bis heute nie direkt benutzt
-
Windows
- Da Chrome auch Windows als Ziel hatte, musste Ninja ebenfalls unter Windows laufen
- Ein erheblicher Teil der Windows-Unterstützung wurde von einem Mitwirkenden geschrieben
- Technisch ist Windows-Unterstützung mit großem Aufwand verbunden
- Prozessstart und Output-Capturing unterscheiden sich plattformspezifisch so stark, dass man neue APIs lernen muss
- Das Ninja-Design hängt davon ab, dass der Kernel die letzte Änderungszeit gecachter Dateien schnell liefern kann, und genau das gilt unter Windows nicht
- Trotzdem ist Windows eine Plattform mit sehr großer Entwicklerbasis
- Bei großartigen Linux-Tools ist der Impuls stark, sie zu teilen, bei Windows-Tools dagegen eher, sie zu verkaufen, weshalb frei verfügbare Werkzeuge dort vergleichsweise seltener sind
- Dass es unter den frühen Ninja-Nutzern viele Windows-Benutzer gab, war damals überraschend, ergibt sich aber aus der großen Zahl an Windows-Entwicklern: Schon wenn sich nur ein Teil davon interessiert, werden daraus viele Nutzer
Verwandte Arbeiten verstehen
- Ninja begann ursprünglich eher wie ein Wochenend-Demo-Hack, weshalb bedauert wird, vor der Entwicklung verwandte Arbeiten nicht gründlicher untersucht zu haben
- Wenn man etwas baut, ist es wichtig, den tatsächlichen Designraum zu verstehen
- Der Begriff „Action Graph“ war nicht die Bezeichnung, die beim Entwurf von Ninja verwendet wurde, sondern wurde aus Googles Build-System blaze/bazel übernommen
- bazel erklärt, dass ein Graph aus High-Level-Zielen wie „library“ oder „binary“ einen Action Graph als Graph von Befehlen erzeugt
- Die Idee, Befehlszeilentext wie eine Datei als Eingabe zu behandeln, ist ein Beispiel für inkrementelle Berechnung
- Inkrementelle Berechnung ist nicht nur mit Build-Systemen, sondern auch mit inkrementellen UIs verbunden
- Im Jane-Street-Blog gibt es eine Einführung in Incremental, die sich auch mit UI-Kompositionsweisen wie React verbinden lässt
- "Build Systems à la Carte" ist eine Arbeit, die inkrementelle Berechnung im Kontext von Build-Systemen diskutiert und als Quelle genannt wird, die man gern schon vor dem Schreiben von Ninja gehabt hätte
Die Last der Open-Source-Wartung
- Die Erfahrung als Open-Source-Maintainer war nicht besonders angenehm
- Die Gefühle zum Projekt mischen Stolz, wenn jemand etwas Nettes sagt, mit noch größerer Enttäuschung
- Weil der Code kostenlos veröffentlicht wurde, gab es zwar gelegentlich freundlich formulierte Anfragen, häufiger aber fordernde oder verärgerte Nutzer, und Dank war selten
- Manche drohten wiederholt mit einem Fork, falls ihre Forderungen nicht übernommen würden
- Auch sinnvolle Beiträge, die mit den Designzielen kollidierten, waren ein weiteres Problem
- Wenn freundliche und kluge Menschen Beiträge einreichten, wollte man eine Ablehnung mit einer angemessenen Erklärung erwidern, und schon diese Erklärung war erschöpfend
- Der Einstieg ins Programmieren kam über freie Software, und der Code wurde geschrieben, um etwas zurückzugeben; heute wirkt freie Software jedoch oft weniger wie Austausch unter Gleichgestellten als wie ein Verhältnis, in dem Nutzer sich als Kunden sehen und Autoren wie Personen behandeln, über die man sich beim Manager beschweren könne
- Die heutige Motivation liegt eher darin, eine kleine Zahl respektierter Hacker zu beeindrucken oder deren Erwartungen zu erfüllen, als darin, möglichst breit erfolgreich zu sein
- Der Erfolg von Ninja brachte viele Lernerfahrungen, doch vermutlich hätte auch ein kleinerer Erfolg ähnliche Einsichten ermöglicht
Maintainer und Mitwirkende
- Nico Weber war ein sorgfältiger Kollaborateur und übernahm viele Jahre lang die Rolle des Ninja-Maintainers
- Jan Niklas Hasse übernahm das Projekt nach Nico und scheint es gut weiterzuführen
- Darüber hinaus wirkten viele weitere Ninja-Mitwirkende am Projekt mit
1 Kommentare
Meinungen auf Hacker News
Die Formulierung, dass „Programmierung so klingt, als ginge es ums Schreiben von Code, tatsächlich aber Architektur wichtiger ist als Code und soziale Themen wichtiger werden als Architektur“, trifft genau einen Gedanken, den ich schon lange im Kopf hatte
Kapitel 1 des Buchs vertritt die These: „Die Hauptprobleme unserer Arbeit sind weniger technischer als vielmehr soziologischer Natur“, und behandelt soziale und politische Fragen wie Teamchemie und Zusammenhalt, „Flow-Zeit“, ruhige Arbeitsumgebungen und die Kosten von Fluktuation
[1] https://en.wikipedia.org/wiki/Peopleware:_Productive_Project...
Deshalb sehe ich Software Engineering viel stärker als Sozialwissenschaft, als viele zugeben oder angenehm finden. Sozialwissenschaften sind beweglicher und weniger vorhersagbar als Naturwissenschaften, daher scheint es die Tendenz zu geben, den sozialen Teil zu vermeiden oder ihn nur sehr primitiv zu behandeln. Man verbeißt sich in atomare technische Details, während das Produkt, das das Team baut, viel zu oft miserabel wird
„Organisationen, die Systeme entwerfen, sind gezwungen, Entwürfe zu produzieren, die Kopien der Kommunikationsstrukturen dieser Organisationen sind.“ — Melvin E. Conway, How Do Committees Invent?
Wurde damals auch diskutiert:
The Success and Failure of Ninja - https://news.ycombinator.com/item?id=23157783 - Mai 2020, 38 Kommentare
Nach etwa einem Jahr ist ein Repost in Ordnung, und der Link zum alten Thread ist für Leser gedacht, die mehr wissen möchten
Die Stelle „Android verwendet Ninja für irgendeine große Komponente des Systems, aber ich habe nie genau verstanden, wofür“ ist lustig
Ninja spielt tatsächlich eine große Rolle in AOSP. Das Build-System nutzte anfangs Makefiles, wurde aber durch soong, ein eigenes deklaratives Build-System, und eine gescheiterte, abgebrochene Bazel-Migration schnell kompliziert. Google entwickelte kati(https://github.com/google/kati), das Makefiles in Ninja-Build-Dateien umwandelt, und das Ergebnis ist wirklich riesig:
λ wc -l out/build-qssi.ninja3035442 out/build-qssi.ninjaDer Übergang von Makefile/soong zu Ninja ist schmerzhaft und dauert selbst auf modernen Maschinen ein paar Minuten, aber sobald Ninja übernimmt, läuft es enorm schnell
Ich frage mich auch, ob es sich lohnt, noch ein weiteres Tool einzuführen, nur um von einem Tool in ein anderes zu übersetzen. Erst recht, wenn Ninja-Dateien so groß und für Menschen schwer lesbar sein können
Bei der Stelle „Ich glaube, dass Programmierer Latenz spüren und dass sie ihre Stimmung beeinflusst, auch wenn sie es nicht bewusst merken. Google hat kürzlich Forschung in diesem Bereich betrieben und meinen Glauben bis zu einem gewissen Grad bestätigt“ frage ich mich, ob diese Latenzstudie tatsächlich veröffentlicht wurde
Ninja ist unter Spieleentwicklern ziemlich beliebt
Den Satz „Windows ist aus Entwicklersicht immer noch eine riesige Plattform, und diese Entwickler hungern nach Tools“ fand ich interessant. Als jemand, der hauptsächlich unter Windows entwickelt, wirken eher Linux-Entwickler, die ohne einen guten Debugger wie Visual Studio oder einen Profiler wie Superluminal leben, auf mich so, als würden sie nach Tools hungern. In letzter Zeit scheint die Lücke zwischen den beiden Plattformen kleiner zu werden, und ich mag auch Rust-Utilities, die im Allgemeinen plattformübergreifend laufen
Mir fällt keine Interaktion mit Visual Studio ein, die ich als „gut“ bezeichnen würde. Ein paar Dinge, die „gerade noch okay“ sind, fallen mir ein, aber Debugging gehört nicht dazu. Allerdings ist der Debugger in 2022 nicht mehr voller Bugs, vielleicht ist also das gemeint
Ich kenne UNIX gut genug, seit ich 1993 mit Xenix in Berührung kam und seitdem verschiedene Varianten genutzt habe, aber WSL verwende ich kaum außer zum Ausführen von Linux-Docker-Containern
Ein paar Dinge, die noch Ninja nutzten, habe ich auf samurai umgestellt, und es ist in jeder Hinsicht besser geworden
Trotzdem halte ich diese Art von Build-System für den falschen Ansatz. Was ich von einem Build-System will, ist, dass es die Inhalte aller transitiven Eingaben hasht und dann nachsieht, ob das Ergebnis in einer Registry vorhanden ist
Allerdings sind sie alle enorm komplex. Wenn man nur lokale Builds betrachtet, verwenden meines Wissens auch SCons und Waf Hashes zur Änderungserkennung
Etwas Ähnliches habe ich mit der Deno-Bibliothek „TDAR“[1] gebaut, und es funktioniert gut, aber es ist schon etwas Aufwand, Kommandozeilen-Tools, die davon ausgehen, auf einem veränderbaren Dateisystem zu arbeiten, so zu verpacken, als würde man sie wie reine Funktionen aufrufen
[1] Ist noch nicht aus dem übergeordneten Projekt[2] herausgelöst, aber ich habe in diesem YouTube-Video darüber gesprochen: https://youtu.be/sty29o8sUKI
[2] Wenn dich so etwas interessiert, darfst du mich gern anstupsen, den Sourcecode zu veröffentlichen. togos zero zero at gee mail dot comb
Und „nicht das, was ich wollte“ bedeutet nicht automatisch „falsch“. Es gibt schließlich Menschen mit anderen Vorlieben
Um C++20-Module in CMake zu verwenden, braucht man Ninja, also wird Ninja noch ziemlich lange bleiben
Am interessantesten fand ich die Stelle, dass man „oft zwischen Korrektheit und Bequemlichkeit oder Performance abwägen muss und sich bewusst sein sollte, wo man auf diesem Kontinuum steht“
Manche Programmierer betrachten diese Dynamik zu starr und meinen, ein Wert müsse selbstverständlich Vorrang haben. In Wirklichkeit ist das Zusammenspiel aber ziemlich subtil. Zum Beispiel kann ein Tool, das etwas Korrektheit zugunsten von Bequemlichkeit opfert, im gesamten Ökosystem zu korrekteren Ergebnissen führen als eine genauere, aber weniger bequeme Alternative. Nämlich dann, wenn Programmierer Letztere meiden
„Verzeiht den peinlichen Namen“ – dabei ist der Name großartig
PS: Wenn man das implementiert, könnte man es noch schneller machen: https://github.com/ninja-build/ninja/issues/2157 Allerdings hat dieses Tool, wie im Artikel beschrieben, absichtlich keinen Zustand, nicht einmal winzige Hinweise aus vorherigen Läufen