English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

Gast
1 / ?

Wie Code-Sediment entsteht

Sedimentgestein entsteht durch Ablagerung, nicht durch Explosion. Jede Schicht: korrekt für ihre Zeit. Jede Schicht: älter als die darüber. Die ältesten Schichten verfestigten sich, bevor sich das Ökosystem um sie herum ausreifte. Kein Fehler verursachte sie. Die Zeit verursachte sie.

MOAD-0001 funktioniert auf dieselbe Weise.

MOAD-0001 Sediment Layers: code copied across decades as N grows

Die Entstehungsgeschichte

Ein Graph-Durchlauf, geschrieben 1993:

List<Node> visited = new ArrayList<>();
Stack<Node> stack = new Stack<>();
stack.push(start);
while (!stack.isEmpty()) {
Node n = stack.pop();
if (!visited.contains(n)) {  // O(N) linear scan
visited.add(n);
for (Node neighbor : n.getNeighbors())
stack.push(neighbor);
}
}

Dieser Code: korrekt. Tests: bestanden. 1993 hatten Graphen 50 Knoten.

50 Knoten: 50 × 25 im Durchschnitt gescannt = 1.250 Operationen. Unsichtbar.

Der Code gelangte in die Versionskontrolle. Wurde in Tutorials kopiert. Wurde in Bibliotheken eingebunden. Wurde in Build-Tools, Paketmanagern und Abhängigkeitsauflösern ausgeliefert. Bis 2024 lief dasselbe Muster auf Abhängigkeitsgraphen mit 50.000 Knoten.

50.000 Knoten: 50.000 × 25.000 im Durchschnitt gescannt = 1.250.000.000 Operationen.
Ein 1-Sekunden-Build wird zu 17 Minuten.

Der Code hat sich nicht geändert. Die Welt um ihn herum ist gewachsen. Jede Schicht des Sediments: korrekt bei der Ablagerung. Teuer bei der Ausgrabung.

Dein Sediment

Sedimentärer Code enthält keinen Logikfehler. Er enthält eine Performance-Annahme, die mit wachsender Skalierung nicht mehr gilt. Der Code liefert korrekte Ergebnisse; nur die Kosten haben sich geändert.

Diese Unterscheidung ist für die Diagnose wichtig. Ein Logikfehler erzeugt falsche Antworten. Ein sedimentärer Defekt erzeugt korrekte Antworten zu unakzeptablen Kosten.

Sedimentärer Code: korrekter Code, der mit zunehmender Skalierung teurer wird. Gib ein konkretes Beispiel aus Software, die du verwendet oder geschrieben hast, bei dem etwas bei kleiner Skalierung funktionierte und bei großer Skalierung zum Flaschenhals wurde. Was hat sich verändert: der Code, die Datenmenge oder das Nutzungsmuster?

Fünf Formen von MOAD-0001

MOAD-0001 tritt in fünf dokumentierten Formen in über 60 Software-Ökosystemen auf. Die Struktur: ein Membership-Test mit einem sequentiellen Container, verschachtelt in einer Schleife über dieselbe oder eine verwandte Collection.

Die fünf Formen

DomäneMusterSequentieller ContainerKorrekter Container
Graph-Traversalif (!stack.contains(n)) in DFS/TarjanArrayListHashSet
Routen-/Event-DeduplizierungTAILQ_FOREACH strcmp pro RequestLinked ListHashMap
KollisionsverfolgungfindLinearSearch() pro Physics-TickArrayunordered_set
Resource-Allocation-FilterList.contains() im Stream-FilterArrayListHashSet
A*-Pfadfindung Open-ListLocalVector::find() pro Nachbarvectorgespeicherter Heap-Index

Alle fünf teilen dieselbe Struktur: eine Mitgliedschaftsprüfung, die in einer Schleife verschachtelt ist und einen Container verwendet, der einen linearen Scan benötigt, um die Mitgliedschaftsfrage zu beantworten.

Die Scanning-Schlüsselwortliste

Das Auditieren von MOAD-0001 bedeutet, nach Mitgliedschafts-Test-Schlüsselwörtern innerhalb von Schleifen zu greppen:

- Python: in mit einer list-Variablen, .count(), list.index()

- Java: ArrayList.contains(), List.contains(), LinkedList.contains()

- JavaScript: Array.includes(), Array.indexOf(), Array.find()

- C++: std::vector::find(), manuelle Schleife mit ==-Vergleich

- Go: slices.Contains(), manuelle Schleife über ein Slice

Die Lösung in jedem Fall: Ersetze den sequentiellen Container durch einen Hash-Container. listset. ArrayListHashSet. ArraySet. vectorunordered_set. slicemap[T]struct{}.

Ein Schlüsselwort. Eine Substitution. Keine Verhaltensänderung. 1.000× Beschleunigung bei N=1.000.

Code-Fragment prüfen

Wende MOAD-0001-Mustererkennung auf ein reales Code-Fragment an.

Du prüfst eine JavaScript-Codebasis und findest innerhalb einer Schleife, die über alle Graph-Nachbarn läuft: `if (!openSet.includes(neighbor)) openSet.push(neighbor)`. Ist das MOAD-0001? Womit würdest du `openSet` ersetzen und wie ändert sich die Komplexität von O(?) zu O(?)?

Eine Zeile, vier Sprachen

Die Lösung für MOAD-0001 in vier Sprachen:

# Python
visited = []       # VORHER: O(N)-Mitgliedschaft
visited = set()    # NACHHER: O(1)-Mitgliedschaft
// Java
List<Node> visited = new ArrayList<>();    // VORHER
Set<Node> visited = new HashSet<>();       // NACHHER
// JavaScript
const visited = [];      // VORHER: Array.includes() O(N)
const visited = new Set(); // NACHHER: Set.has() O(1)
// visited.push(n) → visited.add(n)
// visited.includes(n) → visited.has(n)
// Go
visited := []NodeID{}              // VORHER: slices.Contains() O(N)
visited := make(map[NodeID]struct{}) // NACHHER: Map-Lookup O(1)
// _, ok := visited[n]  für die Mitgliedschaftsprüfung
// visited[n] = struct{}{}  für das Einfügen

In jedem Fall: gleiches Verhalten, gleiche Ausgabe, gleiche Tests bestanden. Nur die Wachstumsrate ändert sich.

Was die Korrektur NICHT ändert

- Das logische Verhalten des Algorithmus: Tiefensuche bleibt Tiefensuche

- Korrektheit der Ausgabe: dieselben Knoten werden in derselben Reihenfolge besucht (innerhalb von DFS)

- Testergebnisse: alle Tests, die auf Korrektheit prüfen, bleiben weiterhin bestanden

Was die Optimierung ÄNDERT

- Mitgliedschaftstest: O(N) → O(1)

- Gesamtlaufzeit: O(N²) → O(N)

- Bei N=1.000: 1.000× schneller

- Bei N=10.000: 10.000× schneller

Eine Einschränkung: Reihenfolge

Hash-Container (set, HashSet, unordered_set) speichern keine Einfügereihenfolge. In Python 3.7+ erhält dict die Einfügereihenfolge; ein normales set nicht. In Java behält HashSet keine Reihenfolge bei; LinkedHashSet schon.

Wenn Reihenfolge neben der Mitgliedschaft wichtig ist: zwei Strukturen parallel führen. Ein set (oder HashSet) für O(1)-Mitgliedschaftstests. Eine separate list (oder ArrayList) für die geordnete Traversierung. Oder LinkedHashSet in Java verwenden, das beides bietet.

Für MOAD-0001 bei der Graph-Traversierung: visited beantwortet eine binäre Frage („Wurde dieser Knoten bereits gesehen?“). Die Reihenfolge spielt für die Mitgliedschaftsfrage keine Rolle. Die Traversierungsreihenfolge ergibt sich aus dem Stack oder der Queue, nicht aus visited.

Mitgliedschaft vs. Reihenfolge

In Tarjans Algorithmus für stark zusammenhängende Komponenten verfolgt onStack, welche Knoten noch auf dem aktuellen DFS-Aufrufstack liegen. Es muss zwei Fragen beantworten: (1) Mitgliedschaft – liegt dieser Knoten gerade auf dem Stack? (2) Am Ende eines DFS-Pfads Knoten in der richtigen Reihenfolge entfernen, bis dieser Knoten erscheint.

Eine naive Implementierung verwendet eine List für onStack und beantwortet die Mitgliedschaftsfrage mit contains() – O(N) pro Prüfung, insgesamt O(N²) bei großen Graphen.

Du korrigierst eine Tarjan-SCC-Implementierung, indem du `onStack = []` durch `onStack = set()` ersetzt. Die Tests laufen durch. Ein Code-Reviewer fragt: „Aber was, wenn die Reihenfolge für onStack wichtig ist?“ Wie antwortest du?

Warum dies eine Disclosure und kein Patch ist

MOAD-0001 existiert in über 1.000 verifizierten Sites in mehr als 60 Software-Ökosystemen. Graphentraversierung in Build-Tools, Routen-Deduplizierung in Netzwerk-Routing-Daemons, Kollisionserkennung in Game-Engines, Ressourcenzuweisung in Betriebssystem-Schedulern.

Jede Site: korrekter Code. Jede Site: O(N²)-Wachstum, das darauf wartet, dass N einen Schwellenwert überschreitet.

Die Disclosure-Pipeline

Ein Patch ohne Upstream-Disclosure behebt nur eine einzelne Site. Das Muster tritt in der nächsten Version, der nächsten Bibliothek, dem nächsten Sprach-Port erneut auf. Disclosure: Benennen Sie das Muster, dokumentieren Sie es als CWE-407 (Algorithmic Complexity: Inefficient Algorithmic Complexity), veröffentlichen Sie die Erkennungsheuristiken und den Einzeiler-Fix. Dann kann jeder Entwickler, der die Disclosure liest, seine eigene Instanz erkennen und beheben.

Hamming nannte dies „einen Fisch geben vs. Fischen lehren“. Der Patch gibt einen Fisch. Die Disclosure – MOAD-0001 benannt, Muster dokumentiert, Fix verallgemeinert – lehrt die Heuristik.

Die Permacomputer-Erweiterung

Hamming arbeitete an punktuellen Lösungen: diesen Filter reparieren, diesen Code verbessern. Unhamming fügt die Offenlegungsschicht hinzu: das Muster benennen, die Heuristik veröffentlichen und sie dem Gemeingut übergeben.

Das Prinzip des zusammengesetzten Wissens gilt hier auf Ökosystemebene. Ein Forscher benennt ein Muster. Hundert Entwickler erkennen es in ihren eigenen Codebasen. Tausend Zeilen O(N²)-Code werden zu O(N). Die Infrastruktur wird schneller für alle, die darauf aufbauen.

Das ist der Drache, der wächst: dasselbe Feuer (an wichtigen Problemen arbeiten, Wissen kumulieren, systemisches Denken), anderer Flug (offene Offenlegung, Gemeingut-Besitz, kein Patron erforderlich).