C ++ AbkĂŒrzung Spickzettel und mehr. Teil 2: "und nicht nur"

Dies ist der zweite und letzte Teil meines AbkĂŒrzungs-Spickzettel, den ein C ++ - Entwickler kennen sollte. C ++ wird hier nur erwĂ€hnt, weil ich den Spickzettel hauptsĂ€chlich fĂŒr mich selbst erstellt habe, aber ich bin genau der gleiche C ++ - Entwickler.

TatsĂ€chlich enthĂ€lt dieser Teil Konzepte, deren Umfang nicht auf C ++ beschrĂ€nkt ist. Daher kann die Auswahl fĂŒr ein breiteres Publikum von Interesse sein.



Wie im ersten Teil werden AbkĂŒrzungen gruppiert, wenn dies sinnvoll ist. Wenn es keinen Sinn macht, werden sie alphabetisch aufgelistet.

ParallelitÀt und atomare Operationen:
‱ CAS
‱ ABA
‱ FAA
‱ RCU

Datenspeicherung:
‱ SÄURE
‱ GAP
‱ PACELC
‱ BASIS

Prinzipien der Softwareentwicklung:
‱ TROCKEN
‱ KUSS
‱ YAGNI
‱ NIH
‱ FTSE
‱ GRASP
‱ FEST

Sonstiges:
‱ ABI
‱ KUH
‱ FBC, FBCP
‱ LRU

ParallelitÀt und atomare Operationen


Cas


Vergleichen und tauschen. Vergleich mit dem Austausch. Dies ist eine atomare Anweisung mit drei Argumenten: atomare Variable oder Speicheradresse, erwarteter Wert, neuer Wert. Wenn und nur wenn der Wert der Variablen mit dem erwarteten Wert ĂŒbereinstimmt, erhĂ€lt die Variable einen neuen Wert und die Anweisung wird erfolgreich abgeschlossen. CAS gibt entweder einfach einen booleschen Wert zurĂŒck (und kann dann als Vergleichen und Setzen bezeichnet werden), oder wenn dies fehlschlĂ€gt, gibt es auch den aktuellen Wert des ersten Arguments zurĂŒck.

Pseudocode
bool cas(int* addr, int& expected, int new_value) { if (*addr != expected) { expected = *addr; return false; } *addr = new_value; return true; } 

In C ++ wird CAS durch die Methodenfamilien std::atomic<T>::compare_exchange_weak , std::atomic<T>::compare_exchange_strong und die freien Funktionen std::atomic_compare_exchange_weak , std::atomic_compare_exchange_strong . Der Unterschied zwischen * schwach und * stark besteht darin, dass erstere falsch negative Ergebnisse liefern können. Das heißt, Wenn der Wert erwartet wird, geben sie false und ersetzen ihn nicht durch einen neuen. Der Grund fĂŒr das Vorhandensein von * schwachen Operationen ist, dass auf einigen Architekturen * stark relativ teuer ist. In den meisten FĂ€llen drehen sich CAS- Anweisungen in einer Schleife (der sogenannten CAS-Schleife). Die Verwendung von * schwach statt * stark Ă€ndert also nichts an der Logik, kann jedoch die Leistung verbessern.

CAS- Anweisungen werden verwendet, um Synchronisationsprimitive (wie Mutexe und Semaphoren) und sperrfreie Algorithmen zu implementieren. FĂŒhren oft zu einem ABA- Problem.

Lesen Sie mehr: einmal (Russisch) , zwei (Englisch)

ABA


ABA-Problem. ABA-Problem. Dieses Problem tritt bei parallelen Algorithmen auf, die auf Vergleichen mit Austauschen basieren (siehe CAS ), beispielsweise bei sperrfreien Algorithmen. Die Quintessenz ist, dass der Thread den Wert der atomaren Variablen liest, etwas anderes tut und diese Variable durch Vergleich mit dem Austausch aktualisiert. Das heißt, Die Ablauflogik lautet wie folgt: Wenn die Variable noch den vorherigen Wert enthĂ€lt, hat sich nichts geĂ€ndert, alles ist in Ordnung. Dies kann aber nicht so sein. Eine formellere Beschreibung des Problems:

  • Thread 1 liest den Wert der Variablen, er ist gleich A.
  • Thread 1 wird herausgedrĂŒckt, Thread 2 startet
  • Thread 2 Ă€ndert den Wert der Variablen von A nach B, nimmt eine Reihe von Änderungen vor (Ă€ndert einen der Variablen zugeordneten Wert oder gibt nur Speicher frei) und Ă€ndert dann erneut den Wert - von B nach A.
  • Thread 1 nimmt den Betrieb wieder auf, vergleicht den zuvor erhaltenen Wert mit dem aktuellen und kommt zu dem Schluss, dass sich nichts geĂ€ndert hat

Mögliche Lösungen fĂŒr das Problem:

  1. Am einfachsten und offensichtlichsten ist die Verwendung von Schlössern. Dies fĂŒhrt zu dem ĂŒblichen thread-sicheren Algorithmus mit kritischen Abschnitten. Aber es wird aufhören, frei von Schlössern zu sein. Wenn es jedoch um CAS und ABA geht , ist dies höchstwahrscheinlich keine Option.
  2. FĂŒgen Sie den verglichenen Werten spezielle Beschriftungen hinzu. Zum Beispiel ein ZĂ€hler fĂŒr die Anzahl der Änderungen. Einerseits kann dieser ZĂ€hler ĂŒberlaufen, andererseits unterstĂŒtzen moderne x86_64-Prozessoren 128-Bit- CAS- Operationen. Das heißt, Wenn Sie Zeiger mit einem ZĂ€hler vergleichen, können Sie bis zu 64 Bit angeben, und jemand hat festgestellt, dass dies fĂŒr 10 Jahre kontinuierlichen Betrieb des Algorithmus ausreicht.
  3. Einige Architekturen (z. B. ARM) bieten LL / SC-Anweisungen (Load Linked, Store Conditional), mit denen Sie nicht nur den aktuellen Wert der Adresse im Speicher abrufen, sondern auch nachvollziehen können, ob sich dieser Wert seit dem letzten Lesen geÀndert hat.

Um Datenstrukturen wie einen Stapel, eine Liste oder eine Warteschlange zu verwenden, die im Allgemeinen nicht blockiert werden können, wenn die Gefahr besteht, dass ein hĂ€ngender Zeiger auf einen Remote-Knoten verbleibt, gibt es eine ganze Familie von Lösungen fĂŒr das ABA- Problem, die auf dem verzögerten Entfernen von Knoten basieren. Dies umfasst den Garbage Collector, Hazard Pointers und den Read-Modify-Write-Mechanismus (siehe RCU ).

Lesen Sie mehr: eins (Russisch) , zwei (Englisch) , drei (Englisch)

FAA


Holen und hinzufĂŒgen. Ähm ... holen und hinzufĂŒgen (es scheint, dass dieses Konzept nie ins Russische ĂŒbersetzt wird). Eine atomare Operation mit zwei Argumenten: einer atomaren Variablen oder einer Adresse im Speicher und dem Wert, um den diese Variable geĂ€ndert werden muss. Wenn die Architektur dies zulĂ€sst, gibt die Operation den vorherigen Wert der geĂ€nderten Variablen zurĂŒck (x86 erlaubt seit i486). Im Gegensatz zu CAS ist die FAA immer erfolgreich.

Pseudocode
 int faa(int* addr, int diff) { int value = *addr; *addr = value + diff; return value; } 

In C ++ wird es als die Familien der Methoden std::atomic<T>::fetch_add , fetch_sub , fetch_and , fetch_or , fetch_xor und die entsprechenden freien Funktionen std::atomic_fetch_add usw. std::atomic_fetch_add .

Wie es sich fĂŒr eine atomare Anweisung gehört, wird die FAA bei der Implementierung von Synchronisationsprimitiven und sperrfreien Algorithmen und Datenstrukturen verwendet.

Lesen Sie mehr: einmal (Russisch) , zwei (Englisch)

RCU


Read-Copy-Update. Lesen-Ändern-Schreiben. Dies ist ein nicht blockierender Mechanismus zum Synchronisieren des Zugriffs auf die Datenstruktur (natĂŒrlich sperrenfrei). Es wird in FĂ€llen verwendet, in denen die Lesegeschwindigkeit kritisch ist. Es ist ein Beispiel fĂŒr den Kompromiss zwischen Zeit und GedĂ€chtnis (Raum-Zeit-Kompromiss).

Die Idee von RCU ist, dass der Writer-Stream die Daten nicht an Ort und Stelle Ă€ndert, sondern eine Kopie erstellt, die erforderlichen Änderungen daran vornimmt und die aktuellen Daten und die geĂ€nderte Kopie atomar austauscht. Gleichzeitig haben Leser-Threads stĂ€ndig Zugriff auf Daten - alte oder neue, wer auch immer Zeit hat. Wenn keine Leser mehr mit der veralteten Version arbeiten, löscht der Schreiber Daten, die nicht mehr benötigt werden, wodurch Speicherplatz frei wird.

Sehr vereinfachte RCU funktioniert folgendermaßen:

  • Viele Leser, ein Schriftsteller.
  • Lesen und Ändern erfolgen gleichzeitig.
  • Leser verwenden eine sehr leichte Synchronisation. TatsĂ€chlich muss der Leser den Verfasser nur beim Betreten des kritischen Abschnitts und beim Verlassen des kritischen Abschnitts benachrichtigen. Die Arbeit mit synchronisierten Daten erfolgt nur im kritischen Bereich.
  • Sobald der Verfasser die Daten atomar durch eine Kopie ersetzt, kĂŒndigt er den Beginn der sogenannten Nachfrist (Nachfrist) an. Die Nachfrist endet, wenn alle Leser, die sich zu Beginn dieser Frist in kritischen Abschnitten befanden, ihre kritischen Abschnitte verlassen haben. Jetzt kann der Writer veraltete Daten sicher löschen. Es wird vorausgesetzt, dass alle kritischen Abschnitte endlich sind, was die Endlichkeit der Nachfrist garantiert.

RCU eignet sich hervorragend fĂŒr Daten, die hĂ€ufig gelesen und selten aktualisiert werden. Dieser Mechanismus wird im Linux-Kernel aktiv verwendet, wo es recht einfach ist, festzustellen, wann die Kulanzfrist endet.

Nachteile:

  • Schlecht fĂŒr die Synchronisierung des Zugriffs auf hĂ€ufig geĂ€nderte Daten.
  • Im Benutzerbereich schwer zu implementieren.
  • HĂ€ngt von der FĂ€higkeit ab, Zeiger auf eine Adresse im Speicher atomar zu Ă€ndern, aber nicht alle Architekturen bieten diese FĂ€higkeit.

Lesen Sie mehr: einmal (Russisch) , zwei (Englisch)

Datenspeicherung


SÄURE


AtomizitÀt, Konsistenz, Isolation, Haltbarkeit. AtomizitÀt, Konsistenz, Isolation, Haltbarkeit. Dies ist eine Reihe von Transaktionsanforderungen in einem DBMS. ACID bietet auch bei Fehlern einen zuverlÀssigen und vorhersehbaren DBMS-Betrieb.

  • Atomicity stellt sicher, dass die Transaktion entweder vollstĂ€ndig abgeschlossen ist oder nichts tut. Ein Zwischenzustand ist unmöglich, es wird nicht so sein, dass eine Transaktionsoperation erfolgreich war und die andere nicht. Alles oder nichts.
  • Die Konsistenz stellt sicher, dass alle Daten in der Datenbank alle angegebenen Regeln und EinschrĂ€nkungen sowohl vor dem Start der Transaktion als auch nach deren Abschluss erfĂŒllen. WĂ€hrend der AusfĂŒhrung der Transaktion kann die Konsistenz verletzt werden.
  • Durch die Isolierung wird sichergestellt, dass sich gleichzeitige Transaktionen nicht gegenseitig beeinflussen. Keine Transaktion hat Zugriff auf inkonsistente Daten, die von einer anderen Transaktion verarbeitet werden.
  • Haltbarkeit bedeutet, dass das Ergebnis einer erfolgreichen Transaktion in der Datenbank gespeichert wird und nicht verloren gehen kann, unabhĂ€ngig davon, was unmittelbar nach Abschluss der Transaktion mit der Datenbank geschieht.

Alle wichtigen relationalen DBMS unterstĂŒtzen ACID vollstĂ€ndig. In der NoSQL-Welt ist eine solche vollstĂ€ndige UnterstĂŒtzung eher eine Ausnahme.

Lesen Sie mehr: einmal (Englisch) , zwei (Englisch)

Cap


CAP-Theorem. CAP-Theorem. Der Satz besagt, dass jedes verteilte System nicht mehr als zwei Eigenschaften aus der Liste haben kann: Datenkonsistenz ( Konsistenz ), VerfĂŒgbarkeit ( VerfĂŒgbarkeit ), BestĂ€ndigkeit gegen Trennung ( Partitionstoleranz ).

  • Konsistenz bedeutet in diesem Fall (vereinfachte) konsistente Konsistenz. Das heißt, Sobald der Datenaktualisierungsvorgang auf einem Knoten erfolgreich abgeschlossen wurde, verfĂŒgen alle anderen Knoten bereits ĂŒber diese aktualisierten Daten. Dementsprechend befinden sich alle Knoten in einem konsistenten Zustand. Dies ist nicht die von ACID geforderte Konsistenz.
  • VerfĂŒgbarkeit bedeutet, dass jeder ausgefallene Knoten in angemessener Zeit eine korrekte Antwort auf jede Anforderung (sowohl zum Lesen als auch zum Schreiben) zurĂŒckgibt. Es gibt keine Garantie dafĂŒr, dass die Antworten von verschiedenen Knoten ĂŒbereinstimmen.
  • Trennungswiderstand bedeutet, dass das System bei Verlust einer beliebigen Anzahl von Nachrichten zwischen seinen Knoten weiterhin ordnungsgemĂ€ĂŸ funktioniert.

Weil Alle drei Eigenschaften sind nicht erreichbar. Aus Sicht des CAP- Theorems fallen alle verteilten Systeme in drei Klassen: CA , CP und AP . CA- Systeme haben offensichtlich keinen Trennwiderstand. Weil In den allermeisten FĂ€llen impliziert die Verteilung die Verteilung ĂŒber ein reales Netzwerk, und in einem realen Netzwerk besteht immer eine Wahrscheinlichkeit ungleich Null, dass ein Paket verloren geht. Dann sind CA- Systeme von geringem Interesse.

Die Wahl liegt zwischen CP und AP , d. H. Zwischen Konsistenz und VerfĂŒgbarkeit. Herkömmliche relationale DBMS, die den ACID- Prinzipien folgen, bevorzugen Konsistenz. WĂ€hrend viele NoSQL-Lösungen Barrierefreiheit und BASE wĂ€hlen.

Im Falle eines normalen Betriebs des Netzwerks, d. H. Wenn keine Netzwerktrennung vorliegt, legt der CAP- Satz keine EinschrĂ€nkungen fĂŒr die Konsistenz und VerfĂŒgbarkeit fest. Das heißt, etwas zu spenden ist nicht notwendig.

Lesen Sie mehr: eins (Russisch) , zwei (Englisch) , drei (Englisch)

Pacelc


PACELC-Theorem. PACELC-Theorem. Dies ist eine Erweiterung des CAP- Theorems, das besagt, dass ein verteiltes System bei einer Netzwerkpartition ( Partition ) gezwungen ist, zwischen VerfĂŒgbarkeit ( Konsistenz ) und bei normalem Netzwerkbetrieb ( sonst ) zwischen Latenz ( Konsistenz) zu wĂ€hlen. )

Wenn der CAP- Satz zwei Klassen von Systemen unterscheidet, die bei der Netzwerktrennung stabil sind, hat PACELC dementsprechend vier davon: PA / EL , PA / EC , PC / EL und PC / EC . Einige NoSQL-Datenbanken können ihre Klasse abhÀngig von den Einstellungen Àndern.

Lesen Sie mehr: einmal (Russisch) , zwei (Englisch)

BASIS


GrundsĂ€tzlich verfĂŒgbar, weicher Zustand, eventuelle Konsistenz. GrundverfĂŒgbarkeit, fragiler Zustand, Konsistenz auf lange Sicht. Nach dem CAP- Theorem in verteilten Systemen muss etwas aufgegeben werden. Auf lange Sicht wird die strikte KohĂ€renz normalerweise zugunsten der KohĂ€renz aufgegeben. Dies bedeutet, dass das System eines Tages, wenn keine DatenĂ€nderungen vorgenommen werden, irgendwann einen konsistenten Zustand erreicht.

Um auf einen solchen Kompromiss hinzuweisen, wurde die AbkĂŒrzung BASE etwas enger verwendet , und es stellte sich ein Spiel chemischer Begriffe heraus ( ACID - AciditĂ€t, BASE - BasizitĂ€t).

  • GrundsĂ€tzlich verfĂŒgbar bedeutet, dass das System die DatenverfĂŒgbarkeit garantiert und auf jede Anfrage reagiert. Die Antwort kann jedoch veraltete oder inkonsistente Daten sein (oder deren Fehlen).
  • Weicher Zustand bedeutet, dass sich der Zustand des Systems im Laufe der Zeit Ă€ndern kann, auch wenn keine DatenĂ€nderungen angefordert werden. Denn zu jedem Zeitpunkt können Daten in einen konsistenten Zustand gebracht werden.
  • Eventuelle Konsistenz bedeutet, dass die Daten, wenn sie sich nicht mehr Ă€ndern, natĂŒrlich in einem konsistenten Zustand enden. Das heißt, Dieselbe Anfrage an verschiedene Knoten fĂŒhrt zu denselben Antworten.

Lesen Sie mehr: einmal (Englisch) , zwei (Englisch)

Prinzipien der Softwareentwicklung


TROCKEN


Wiederholen Sie sich nicht. Nicht wiederholen. Dies ist das Prinzip der Softwareentwicklung, dessen Hauptidee darin besteht, die Menge doppelter Informationen im System zu reduzieren. Ziel ist es, die KomplexitÀt des Systems zu verringern und seine Verwaltbarkeit zu verbessern.

Im Original ( The Pragmatic Programmer Book, von Andrew Hunt und David Thomas ) wird dieses Prinzip wie folgt formuliert: „Jedes Wissen sollte eine einzige, konsistente und maßgebliche Darstellung innerhalb des Systems haben.“ In diesem Fall bedeutet Wissen jeden Teil eines Themenbereichs oder Algorithmus: einen Code, ein Datenbankschema, ein bestimmtes Interaktionsprotokoll usw. Um eine Änderung am System vorzunehmen, muss nur ein „Wissen“ an einer Stelle aktualisiert werden.

Ein dummes Beispiel: Client und Server ĂŒbertragen strukturierte Daten untereinander. Weil Dies sind verschiedene Anwendungen, die auf verschiedenen Maschinen ausgefĂŒhrt werden. Beide mĂŒssen ihre eigenen Implementierungen dieser Strukturen haben. Wenn sich etwas Ă€ndert, mĂŒssen Änderungen an zwei Stellen vorgenommen werden. Ein naheliegender Schritt, um diese Wiederholung zu vermeiden, besteht darin, den gemeinsamen Code einer separaten Bibliothek zuzuweisen. Der nĂ€chste Schritt besteht darin, es gemĂ€ĂŸ der Beschreibung der Strukturen (z. B. Google Protocol Buffers) zu generieren, um nicht denselben Codetyp fĂŒr den Zugriff auf die Felder der Strukturen zu schreiben.

Die VervielfÀltigung von Codes ist nur ein Sonderfall einer DRY- Verletzung. Und das ist nicht immer der Fall. Wenn zwei Codeteile gleich aussehen, aber jeweils ihre eigene GeschÀftslogik implementieren, wird DRY nicht beschÀdigt.

Wie jedes andere Prinzip ist DRY ein Werkzeug, kein Dogma. Je grĂ¶ĂŸer das System ist, desto leichter ist es, dieses Prinzip zu verletzen. Erstens ist das Ideal unerreichbar. Und zweitens, wenn das blinde Folgen von DRY zu komplizierterem Code fĂŒhrt und das VerstĂ€ndnis erschwert, ist es besser, ihn aufzugeben.

Lesen Sie mehr: eins (russisch) , zwei (russisch)

KUSS


Halte es einfach, dumm. Machen Sie es einfacher (dumm in diesem Fall ist kein Anruf). Dies ist das Konstruktionsprinzip, nach dem die meisten Systeme besser funktionieren, wenn sie einfach bleiben. Das Prinzip stammt aus der Flugzeugindustrie und wird hÀufig angewendet, auch in der Softwareentwicklung.

Im letzteren Fall ist KISS sowohl beim Entwerfen als auch beim direkten Schreiben von Code nĂŒtzlich. Einfache Architektur und Code sind nicht nur einfacher zu verstehen, sondern auch einfacher zu verwenden, zu warten und zu entwickeln. MapReduce sollte nicht getĂ€uscht werden, wenn ein Produzent-Konsumenten-Paar ausreicht. Die Magie der Metaprogrammierung ist Ă€ußerst komplex, wenn Sie mit ein paar normalen Funktionen arbeiten und das erforderliche Leistungsniveau erreichen können.

Bei alledem darf man nicht vergessen, dass Einfachheit kein Ziel ist, sondern nur eine nicht funktionale Anforderung. Die Hauptsache ist, das Entwurfs- / Implementierungsziel zu erreichen, und es ist ratsam, dies auf möglichst einfache Weise zu tun.

Lesen Sie mehr: eins (russisch) , zwei (russisch) , drei (englisch)

YAGNI


Du wirst es nicht brauchen. Du brauchst es nicht. Dies ist das Prinzip der Softwareentwicklung, dessen Hauptidee die Ablehnung ĂŒbermĂ€ĂŸiger FunktionalitĂ€t ist, und das Ziel besteht darin, Ressourcen zu sparen, die fĂŒr die Entwicklung aufgewendet werden.

Laut YAGNI mĂŒssen Sie keine Funktionen entwerfen oder implementieren, die derzeit nicht benötigt werden. Auch wenn Sie sicher sind, dass sie in Zukunft benötigt werden. Sie mĂŒssen keine unnötigen Abstraktionen hinzufĂŒgen, deren Vorteile spĂ€ter erscheinen werden.

Das Problem ist, dass die Menschen die Zukunft nicht gut vorhersagen. Daher wird höchstwahrscheinlich alles, was "in Reserve" gemacht wird, einfach nicht nĂŒtzlich sein. Und es stellt sich heraus, dass die Zeit und das Geld, die fĂŒr solche Entwicklungen, Tests und Dokumentationen aufgewendet werden, verschwendet werden. Außerdem ist die Software komplizierter geworden, und es mĂŒssen zusĂ€tzliche Ressourcen aufgewendet werden, um sie erneut zu unterstĂŒtzen. Schlimmer noch, wenn sich herausstellt, dass es notwendig war, etwas anderes zu tun. Geld und Zeit werden auch fĂŒr die Korrektur aufgewendet.

Das YAGNI- Prinzip ist etwas radikaler als DRY und KISS . Wenn sie das System in verstÀndliche Teile zerlegen und Entscheidungen einfach treffen, schneidet YAGNI einfach unnötige Teile und Lösungen aus.

Lesen Sie mehr: eins (Russisch) , zwei (Englisch) , drei (Englisch)

Nih


Hier nicht erfunden. Hier nicht erfunden. Dies ist ein Syndrom der Ablehnung der Entwicklungen anderer Menschen, eine Position, die praktisch an die Erfindung eines Fahrrads grenzt. Oft geht das Syndrom mit der Überzeugung einher, dass die Entwicklung von Technologie im Unternehmen sowohl schneller als auch billiger ist und die Technologie selbst die Anforderungen des Unternehmens besser erfĂŒllt. In den meisten FĂ€llen ist dies jedoch nicht der Fall, und NIH ist ein Anti-Muster.

Hier sind einige FĂ€lle, die NIH rechtfertigen:

  • Die QualitĂ€t der Drittanbieterlösung ist nicht hoch genug oder der Preis ist nicht niedrig genug.
  • Eine Drittanbieterlösung unterliegt LizenzbeschrĂ€nkungen.
  • Die Verwendung einer Drittanbieterlösung fĂŒhrt zu einer AbhĂ€ngigkeit von ihrem Lieferanten und gefĂ€hrdet somit das GeschĂ€ft.

Lesen Sie mehr: eins (Russisch) , zwei (Englisch) , drei (Englisch)

Ftse


Grundsatz der Softwareentwicklung. Grundsatz der Softwareentwicklung. Eigentlich ist dies kein Satz, es gibt keinen Beweis. Dies ist ein berĂŒhmtes Sprichwort von Andrew Koenig:
Jedes Problem kann durch HinzufĂŒgen einer weiteren Abstraktionsebene gelöst werden.
Manchmal fĂŒgen sie diesem Satz hinzu: "... mit Ausnahme des Problems zu vieler Abstraktionsebenen." Im Allgemeinen ist der „Satz“ keine ernste Sache, aber es lohnt sich, darĂŒber Bescheid zu wissen.

Lesen Sie mehr: einmal (Englisch) , zwei (Englisch)

GRASP


Allgemeine Software-Muster fĂŒr die Zuweisung von Verantwortlichkeiten. Vorlagen fĂŒr die allgemeine ZustĂ€ndigkeitszuweisung. Diese neun Muster werden in Anwenden von UML und Mustern von Craig Larman formuliert. Jede Vorlage ist eine typische Lösung fĂŒr ein (aber eher allgemeines) Software-Designproblem.

  1. Informationsexperte . Problem: Was ist das allgemeine Prinzip der Verteilung der Verantwortlichkeiten zwischen Objekten? Entscheidung: Weisen Sie jemandem eine Pflicht zu, der ĂŒber die zur ErfĂŒllung dieser Verpflichtung erforderlichen Informationen verfĂŒgt.
  2. Schöpfer Problem: Wer sollte fĂŒr die Erstellung eines neuen Objekts verantwortlich sein? Lösung: Klasse B muss Instanzen der Klasse A erstellen, wenn eine oder mehrere der folgenden Bedingungen erfĂŒllt sind:
    - Klasse B aggregiert oder enthÀlt Instanzen von A.
    - B schreibt A.
    - B verwendet aktiv A.
    - B hat Initialisierungsdaten A.
  3. Niedrige Kupplung Problem: Wie können die Auswirkungen von VerĂ€nderungen verringert werden? Wie kann die Möglichkeit der Wiederverwendung erhöht werden? Lösung: Verteilen Sie die Verantwortlichkeiten so, dass die KonnektivitĂ€t gering ist. Die Kopplung ist ein Maß dafĂŒr, wie starr die Elemente miteinander verbunden sind und wie stark sie voneinander abhĂ€ngen. Das heißt, Es wird empfohlen, Objekte so zu verbinden, dass sie nur das erforderliche Minimum voneinander kennen.
  4. Hohes Getriebe ( hohe KohÀsion ). Problem: Wie gehe ich mit KomplexitÀt um? Lösung: Verteilen Sie die Verantwortlichkeiten so, dass ein hohes Engagement erhalten bleibt. Ein hohes Engagement bedeutet, dass sich die Verantwortlichkeiten eines Elements auf einen Bereich konzentrieren.
  5. Controller Problem: Wer sollte fĂŒr die Behandlung von Eingabeereignissen verantwortlich sein? Lösung: Weisen Sie eine verantwortliche Klasse zu, die entweder das gesamte System oder Subsystem als Ganzes (externer Controller) oder ein bestimmtes Skript (Skript oder Sitzungscontroller) darstellt. Gleichzeitig implementiert der Controller keine Reaktion auf Ereignisse, sondern delegiert diese an die entsprechenden AusfĂŒhrenden.
  6. Polymorphismus ( Polymorphismus ). Problem: Wie gehe ich je nach Typ mit unterschiedlichen Verhaltensweisen um? Lösung: Wenn das Verhalten vom Typ abhĂ€ngt, weisen Sie den Typ, der dieses oder jenes Verhalten implementiert, als verantwortlich zu, und greifen Sie ĂŒber eine verallgemeinerte Schnittstelle auf die Typen zu.
  7. ( Pure Fabrication ). : , ? : , .
  8. ( Indirection ). : , Low Coupling ? : .
  9. ( Protected Variations ). : , ? : , .

GRASP SOLID . , . . — .

: (.) , (.) , (.)

SOLID


SOLID. - ( ). (Robert Martin) 2000-. — , , .

  1. ( Single Responsibility Principle, SRP ). . « , ».
  2. / ( Open Closed Principle, OCP ). (, , ) , . : , .
  3. ( Liskov Substitution Principle, LSP ). , . Das heißt, - .
  4. ( Interface Segregation Principle, ISP ). , . , . , , . , .
  5. ( Dependency Inversion Principle, DIP ). . . . . , , , , ( Java C#).

: (.) , (.) , (.)

Andere


ABI


Application Binary Interface. . , ( , , ). ABI – , (, ).

ABI ELF Linux PE Windows. , (, , etc.) . , ELF PE , .

ABI , , , , , . . . .

C++ ABI , , . . . , C++ Unix- (Linux, FreeBSD, MacOS) x86_64 System V AMD64 ABI , ARM – ARM C++ ABI . Visual C++ ABI , . System V ABI, (mangling) ( Linux 6 , Windows – 4), .

API ABI , , . C++11 ( ). - GCC 5 ( COW ), .

: (.) , (.) .

COW


Beim Schreiben kopieren. Bei Aufnahme kopieren. Dies ist ein Ressourcenverwaltungsmechanismus, der auch als implizite Freigabe und verzögertes Kopieren bezeichnet wird. Die Idee ist, dass, wenn eine Kopie erforderlich ist, die Ressource nicht tatsĂ€chlich kopiert wird, sondern ein Link dazu erstellt wird. Und nur wenn ÄnderungswĂŒnsche vorliegen - im Original oder in der „Kopie“ - erfolgt erst dann die Erstellung einer vollstĂ€ndigen Kopie.

Der Vorteil von COW liegt auf der Hand: Das Kopieren eines Objekts erfolgt sofort. Wenn Objekte hÀufig kopiert, aber selten geÀndert werden, können Leistungssteigerungen erheblich sein.

Beispiele fĂŒr die Verwendung von COW :

  • Verwaltung des virtuellen Prozessspeichers unter Linux. Beim Aufruf der fork()Seiten wird der Prozessspeicher nicht kopiert, sondern nur als freigegeben markiert.
  • (snapshots) (Btrfs, ZFS) (MS SQL Server).
  • ++11 std::string COW . C++11 std::string (. ABI ).
  • Qt COW .

: (.) , (.)

FBC, FBCP


Fragile Base Class (Problem). . , , .

,
 struct Base { virtual void method1() { // ... } virtual void method2() { // ... method1(); // <--      } }; struct Derived : Base { void method1() override { method2(); } }; 

FBC , , Java ( C++ ). FBCP :

  • , ( final C++ Java).
  • , .
  • , , .

: (.) , (.) , (.)

LRU


Least Recently Used. . ( ). «-», — (hit ratio). , . , . — , , .

LRU , . , , . , . LRU — O(n), — O(1), — O(1). - .

,
 template <class K, class V> class LRU { private: using Queue = std::list<std::pair<K, V>>; using Iterator = typename Queue::iterator; using Hash = std::unordered_map<K, Iterator>; Queue queue_; Hash hash_; const size_t limit_; public: LRU(size_t limit) : limit_(limit) { } std::optional<V> get(const K& key) { const auto it = hash_.find(key); if (it == hash_.end()) { return {}; } it->second = reorder(it->second); return { it->second->second }; } void add(K&& key, V&& value) { if (hash_.size() >= limit_) { pop(); } queue_.emplace_front(std::move(key), std::move(value)); const auto it = queue_.begin(); hash_[it->first] = it; } private: Iterator reorder(Iterator it) { queue_.emplace_front(std::move(it->first), std::move(it->second)); queue_.erase(it); return queue_.begin(); } void pop() { hash_.erase(queue_.back().first); queue_.pop_back(); } }; 

LRU , . . n . LRU : MRU (Most Recently Used), LFU (Least Frequently Used), Segmented LRU, 2Q . .

: (.) , (.) , (.)

PS


- - — .

Source: https://habr.com/ru/post/de470317/


All Articles