Ergänzende Wissenspunkte für das Horizont-Interview

1. Der Unterschied zwischen vector.resize() und vector.reserve()

Klären Sie zunächst die beiden Attribute im Vektor:

Größe: Gibt die Anzahl der im aktuellen Container gespeicherten Elemente an

Kapazität: Gibt an, wie viele Elemente gespeichert werden dürfen, bevor eine Neuzuweisung erfolgt

vector<int>vc(5);
	cout << "初始化分配5个元素----------" << endl;
	cout << &vc[0] << endl;
	cout << vc.size() << endl;
	cout << vc.capacity() << endl;
	cout << "reserve(6)----------" << endl;
	vc.reserve(6);
	cout << &vc[0]<<endl;
	cout << vc.size()<<endl;
	cout << vc.capacity() << endl;
	cout << "resize(6)----------" << endl;
	vc.resize(6);
	cout << &vc[0] << endl;
	cout << vc.size() << endl;
	cout << vc.capacity() << endl;
	cout << "reserve(4)----------" << endl;
	vc.reserve(4);
	cout << &vc[0] << endl;
	cout << vc.size() << endl;
	cout << vc.capacity() << endl;
	cout << "resize(4)----------" << endl;
	vc.resize(4);
	cout << &vc[0] << endl;
	cout << vc.size() << endl;
	cout << vc.capacity() << endl;

Bildbeschreibung hier einfügen
Wenn die Größe des Rücklaufs größer als die aktuelle Kapazität ist, wird die Größe nicht geändert, aber die Kapazität wird geändert.
Wenn die Größe des Rücklaufs kleiner als die aktuelle Kapazität ist, wird die Größe nicht geändert und die Kapazität wird nicht geändert
. Fällig Aufgrund der geringen Einfügungseffizienz von Vektor wird Reverse hauptsächlich verwendet, um Speicherplatz bei der Größenänderung vorab zuzuweisen.
Wenn die Größe größer als die aktuelle Kapazität ist, ändern Sie sowohl Größe als auch Kapazität auf die Größe der Größenänderung.
Wenn die Größe der Größenänderung kleiner als die aktuelle Kapazität ist , wird nur die Größe geändert, aber die Kapazität wird nicht geändert

reserve(size_type) ändert nur den Wert von capaciy, und diese Speicherplätze können zu diesem Zeitpunkt noch "wild" sein. Wenn der Operator [] für den Zugriff verwendet wird, kann ein Array-out-of-bounds-Problem auftreten, und wenn der []-Operator für den Zugriff verwendet wird, kann ein Array-out-of-bounds-Problem auftreten.

2. Prinzip und Implementierung der virtuellen Funktion

Virtuelles Funktionsprinzip und Implementierung

3. Vier Konvertierungsarten

Vier Arten der Konvertierung

4. Vier intelligente Zeiger

Vier intelligente Zeiger

5. Prozess- und Threadwechsel, Prinzip des Kontextwechsels, wo gespeichert werden soll

Kurzer Überblick:
Der Kontext des Prozesses sind die Register der CPU, sie werden auf der Leiterplatte im Speicher abgelegt

CPU-Register:
Es müssen mindestens sechs Arten von Registern in der CPU vorhanden sein:
Datenregister (DR): Die Hauptfunktion besteht darin, als Übertragungsstation für die Informationsübertragung zwischen CPU, Hauptspeicher und Peripheriegeräten zu dienen, um die Lücke auszugleichen zwischen CPU, Hauptspeicher und Peripheriegeräten Der Unterschied in der Betriebsgeschwindigkeit zwischen
Befehlsregister (IR): Wird verwendet, um einen gerade ausgeführten Befehl zu speichern
Programmzähler (PC): Wird verwendet, um auf die Adresse des nächsten Befehls im Hauptspeicher hinzuweisen.
Adressregister (AR): Wird verwendet, um das Adressakkumulationsregister (AC) der Hauptspeichereinheit zu speichern, auf die gegenwärtig von der CPU zugegriffen wird
: Wenn die Arithmetik-Logik-Einheit ALU der Arithmetikeinheit arithmetische oder logische Operationen durchführt, stellt sie einen Arbeitsbereich für die bereit ALU, die für die ALU verwendet werden kann. Speichern Sie vorübergehend einen Operanden oder ein Operationsergebnis.
Programmstatuswortregister (PSW): Wird verwendet, um den aktuellen Betriebszustand und den Arbeitsmodus des Programms darzustellen.
Seitentabellenregister : Es speichert die Startadresse und die Seitentabellenlänge der aktuellen Prozessseitentabelle. Vergleichen Sie die oben berechnete Seitentabellennummer mit der Länge der Seitentabelle, um zu bestätigen, dass sie innerhalb des Bereichs der Seitentabelle liegt, multiplizieren Sie dann die Seitentabellennummer und die Länge des Seitentabelleneintrags, um den relativen Versatz der Zielseite zu erhalten zur Basisadresse der Seitentabelle und schließlich hinzufügen Der Basisadressen-Offset der vorherigen Seitentabelle kann auf den entsprechenden Rahmen zugreifen.Nachdem die CPU die Startadresse des Rahmens erhalten hat, fügt sie die Offset-Adressein der Seite hinzu, um auf das letzte zuzugreifen Zieladresse.

Durch das Speichern dieser Informationen können alle Anforderungen der Thread- oder Prozessumschaltung erfüllt werden Je nach Umschaltmodus sind auch die Typen der gespeicherten Register unterschiedlich. Beispielsweise muss der Systemaufruf die Register, in denen die Benutzermodusdaten gespeichert sind, nicht speichern; die Thread-Umschaltung im selben Prozess muss die Seitentabellenregister nicht speichern usw.

Außerdem hat der User-Prozess sowohl User-Space-Informationen als auch Kernel-Space-Informationen.Während des Prozesswechselprozesses müssen alle Informationen des Prozesses gespeichertwerden, aber die Informationen des User-Space (Benutzer-Stack, Benutzerdaten usw.) werdengespeichert im User Space, und die Adresse (User Stack Pointer) zum Kernel, nur die Informationen des Kernel Space werden im Kernel State gespeichert.

Bildbeschreibung hier einfügen
Wie in der Abbildung gezeigt, handelt es sich um einen Kontextwechselprozess.Das obere Speicherkorn speichert den Kontext von Thread 1, das untere Speicherkorn speichertden Kontext von Thread 2 und das mittlere Korn speichert den Kernel-Zustand.
Beim Umschalten von Thread 1 auf Thread 2
wechselt 01CPU zunächst in den Kernel-Zustand
02 speichert den Wert im CPU-Register zu diesem Zeitpunkt in das Upper Memory Particle (pcb)
03 holt sich den Kontext des nächsten Prozesses aus dem in 02 verwendeten PCB. und in den Registern der CPU wiederherstellen.

Wenn ein Prozess erstellt wird, wird ein PCB erstellt, das im Kernelspace im Speicher gespeichert wird und die folgenden Daten enthält:
01 Prozessbeschreibungsinformationen: PID-Nummer, Benutzerkennung
02 Prozesssteuerungs- und Managementinformationen: aktueller Prozessstatus, Priorität, Blockierungsgrund, die nächste PCB-Adresse 03 Ressourcenzuordnungsliste: Die Datei, der Speicher, das IO-Gerät 04 Der CPU-Registerwert, der
dem aktuellen Prozess gehört , und die Daten nach dem Kontextwechsel werden an dieser Stelle gespeichert

Der Unterschied zwischen Prozessumschaltung und Systemaufrufen:
Erstens hat jeder Prozess zwei Stacks, Benutzermodus-Stack und Kernelmodus-
Stack. Register), müssen Sie die Benutzermodus-Ressourcen (virtueller Speicher, Stack usw.) des Prozesses speichern; danach Wenn Sie den Kernelmodus des nächsten Prozesses laden, müssen Sie den virtuellen Speicher und den Benutzerstapel des Prozesses aktualisieren.

Der Unterschied zwischen Prozesswechsel und Threadwechsel:
Die beiden Threads davor und danach gehören zu unterschiedlichen Prozessen: Da Ressourcen nicht gemeinsam genutzt werden, ist der Wechselprozess zu diesem Zeitpunkt derselbe wie der Prozesskontextwechsel.
Die beiden Threads davor und danach gehören zum selben Prozess:
Bildbeschreibung hier einfügen

Wenn ein Prozess über mehrere Threads verfügt, teilen sich diese Threads dieselben Ressourcen wie virtuellen Speicher und globale Variablen. Diese Ressourcen müssen während Kontextwechseln nicht geändert werden.
Threads haben auch ihre eigenen privaten Daten wie Stacks und Register, die auch während des Kontextwechsels gespeichert werden müssen.

Die tatsächliche Prozess -Thread-Kontextumschaltung der Linux-Leistungsoptimierung

6. GDB-Nutzung

Der vollständige Name von GDB lautet „Symbolischer GNU-Debugger". Es ist nicht schwer aus dem Namen zu erkennen, dass es im GNU-Projekt geboren wurde (GCC, Emacs usw. wurden auch zur gleichen Zeit geboren), und es ist ein allgemeiner Begriff verwendeter Programm-Debugger unter Linux. Bisher hat GDB viele Versionen iteriert. Die aktuelle GDB unterstützt das Debuggen von Programmen, die in mehreren Programmiersprachen geschrieben wurden, darunter C, C++, Go, Objective-C, OpenCL, Ada usw. In tatsächlichen Szenarien wird GDB häufiger zum Debuggen von C- und C++-Programmen verwendet. Videolehre
des gdb-Befehls Schreiben Sie zuerst einen einfachen Code in Linux: Verwenden Sie gcc -g, um gdb zum Kompilieren hinzuzufügen, und verwenden Sie dann gdb, um das kompilierte a.out zu öffnen: Verwenden Sie zuerst den list-Befehl, um den Code anzuzeigen und die Zeilennummer zu identifizieren Verwenden Sie b 4 in Setzen Sie einen Haltepunkt in die vierte Zeile, und verwenden Sie dann r, um einen laufenden Befehl auszugeben, und stellen Sie fest, dass er in der vierten Zeile stoppt, und geben Sie dann weiter n ein, um zur nächsten Zeile zu wechseln: Geben Sie c ein, um fortzufahren . und bis zum Ende des Programms ausführen. Das Ergebnis des ersten Drucks i ist 21845, da int i=0 zu diesem Zeitpunkt nicht ausgeführt wurde und dieses Stück Speicher nicht initialisiert wurde. Später können Sie sehen, dass der Wert von i wird nach jedem um 1 erhöht for(int i=0;i<10;i++) auf In Zeile 8, lassen Sie n initialisieren i. Zu diesem Zeitpunkt den Wert von watch i und dann c , ändert sich der Wert von i. Verwenden Sie alten Wert und neuen Wert, um die Änderungen anzuzeigen, und verwenden Sie info watch, um Beobachtungspunkte anzuzeigen, die nicht in der Abbildung platziert werden können.


Bildbeschreibung hier einfügen

Bildbeschreibung hier einfügen

Bildbeschreibung hier einfügen

Bildbeschreibung hier einfügen


Bildbeschreibung hier einfügen

Bildbeschreibung hier einfügen

Bildbeschreibung hier einfügen

7. Der dreistufige Cache der CPU, seine Funktion, wer ihn verwaltet und wie er verwaltet wird

Warum muss die CPU einen Cache verwenden?Einfach ausgedrückt, weil die CPU zu schnell und der Arbeitsspeicher zu langsam ist, wird ein Cache benötigt, um die Wartezeit der CPU zu verkürzen und die CPU-Leistung getarnt zu verbessern.

Schauen Sie sich zuerst den Prozess der CPU an, der das Programm ausführt:
Bildbeschreibung hier einfügen
Der Prozess der CPU, der das Programm ausführt, ist wie folgt:

Im ersten Schritt liest die CPU den Wert des „Programmzählers“, der die Speicheradresse des Befehls ist, und dann betreibt die „Steuereinheit“ der CPU den „Adressbus“, um die Speicheradresse anzugeben, auf die zugegriffen werden soll , und teilt dann der Speichervorrichtung mit, Daten vorzubereiten, Datenvorbereitung Nach Abschluss werden die Befehlsdaten über den „Datenbus“ an die CPU übertragen registrieren".
Im zweiten Schritt analysiert die CPU die Befehle im „Befehlsregister", um den Typ und die Parameter des Befehls zu bestimmen. Wenn es ein Befehl vom Berechnungstyp ist, übergibt sie den Befehl an die „Logikoperationseinheit" zur Operation; wenn es sich um eine Anweisung vom Speichertyp handelt, wird sie übergeben an Die „Steuereinheit" führt aus;
im dritten Schritt, nachdem die CPU die Anweisung ausführt, wird der Wert des „Programmzählers" inkrementiert, was anzeigt, dass er auf die zeigt nächste Anweisung. Die Größe dieses Selbstinkrements wird durch die Bitbreite der CPU bestimmt.Bei einer 32-Bit-CPU ist die Anweisung beispielsweise 4 Bytes und benötigt 4 Speicheradressenzum Speichern, also wird der Wert des "Programmzählers" gespeichert um 4 erhöht werden; eine kurze Zusammenfassung ist
: Wenn ein Programm ausgeführt wird, liest die CPU die auszuführende Anweisung aus dem Speicher in das Anweisungsregister zur Ausführung entsprechend der Speicheradresse im Programmzähler und erhöht dann entsprechend der Länge der Anweisung, um die nächste Anweisung sequentiell zu lesen.

Die CPU liest Anweisungen aus dem Programmzähler, führt sie aus und fährt mit der nächsten Anweisung fort. Dieser Prozess wird bis zum Ende der Programmausführung in einer Schleife fortgesetzt. Dieser kontinuierliche Schleifenprozess wird als CPU-Anweisungszyklus bezeichnet.

Quelle: https://blog.csdn.net/qq_34827674/article/details/109006026

Schauen wir uns noch einmal den CPU-Cache an.Lassen Sie uns zuerst ein Beispiel geben.Zum Beispiel benötigt die CPU 1-2 Taktzyklen, um eine Additionsoperation durchzuführen, und es dauert 100-300 Zyklen, um Daten aus dem Speicher zu lesen.Sie kann warten so lange, sonst wird die Hochgeschwindigkeits-CPU langsam, also kam ich auf den Cache Cache.

In der aktuellen Mainstream-CPU gibt es normalerweise drei Cache-Ebenen, die in L1, L2 und L3 unterteilt sind.Die Geschwindigkeit zwischen ihnen nimmt ab und die Kapazität nimmt zu.Es dauert etwa 3 Zyklen, um die Informationen in L1 zu lesen, und Die Geschwindigkeit des CPU-Verarbeitungsvorgangs ist unendlich.Es ist nah, der Lesezyklus von L2 beträgt etwa 10–15 Zyklen, und das Lesen von L3 ist noch langsamer, etwa 40–60 Zyklen.

Bildbeschreibung hier einfügen
Wie in der Abbildung gezeigt, werden L1 und L2 im CPU-Kern zwischengespeichert, und mehrere Kerne teilen sich einen L3-Cache.Die
Geschwindigkeit von L1L2L3 wird langsamer und niedriger, und der Preis wird immer niedriger (die Geschwindigkeit bezieht sich hier auf die Geschwindigkeit). an dem die CPU Daten oder Befehle von ihr liest und schreibt)

Die Rolle des CPU-Cache:
Der CPU-Cache (Cache Memory) ist ein temporärer Speicher, der sich zwischen der CPU und dem Speicher befindet.Seine Kapazität ist viel kleiner als der Speicher, aber die Austauschgeschwindigkeit ist viel schneller als der Speicher. Das Aufkommen des CPU-Cache dient hauptsächlich dazu, den Widerspruch zwischen der CPU-Betriebsgeschwindigkeit und der Lese- und Schreibgeschwindigkeit des Speichers zu lösen.
Wenn die CPU Daten lesen und Berechnungen durchführen muss, muss sie zuerst die erforderlichen Daten im CPU-Cache finden und sie in kürzester Zeit an die CPU liefern. Wenn die erforderlichen Daten nicht gefunden werden, stellt die CPU eine "Anforderung", aus dem Speicher über den Cache zu lesen, und kehrt dann zur Berechnung zur CPU zurück. Gleichzeitig werden die Daten, in denen sich diese Daten befinden, auch in den Cache übertragen, sodass zukünftig der gesamte Datenblock aus dem Cache gelesen werden kann, ohne den Speicher aufzurufen, und die Zugriffsgeschwindigkeit erhöht wird basierend auf dem Prinzip der zeitlichen Lokalität (wenn auf bestimmte Daten zugegriffen wird, wird wahrscheinlich in naher Zukunft erneut auf sie zugegriffen) und dem Prinzip der räumlichen Lokalität (wenn auf bestimmte Daten zugegriffen wird, werden wahrscheinlich auch die angrenzenden Daten zugegriffen bald zugegriffen werden) Wenn wir uns also das speicherinterne A-Byte von ansehen, können wir bald auf seine Nachbarn zugreifen. Sobald die Cache-Zeile in L1D vorhanden ist, kann der Ladebefehl fortfahren und sein Speicherlesen durchführen.

(2)

Cache-Konsistenz:
Es gibt ein Programmbeispiel, es gibt eine Datenstruktur mit langen Daten, unter der Annahme, dass die Cache-Zeile 64B ist, zwei Threads werden die int-Daten in dieser Datenstruktur modifizieren, 100 Mal durchlaufen und 100 Sekunden dauern.
Verbesserung: Fügen Sie 64B-Daten vor und nach den int-Daten dieser Datenstruktur hinzu, und der Zeitaufwand beträgt zu diesem Zeitpunkt viel weniger als 100 Sekunden, da vor der Änderung 8 geänderte Daten in einer Cache-Zeile vorhanden sind und ein Thread aktualisiert wird die Cache-Zeile Beim Modifizieren werden andere Threads benachrichtigt, die Cache-Zeile zu modifizieren.

Fast alle modernen Computer haben mehrere Kerne, mehrere Threads laufen in verschiedenen Kernen und jeder Kern hat seinen eigenen Cache. Zu diesem Zeitpunkt wird es ein Problem geben, dass mehrere CPUs gleichzeitig dieselben Daten modifizieren, was das berühmte Cache-Konsistenzproblem ist.

8. Zu urteilen, dass die verknüpfte Liste eine Ring- Hash -
Tabelle, schnelle und langsame Zeiger, leicht
hat
seinen Iterator erhalten

Ich denke du magst

Origin blog.csdn.net/qq_42567607/article/details/126322705
Empfohlen
Rangfolge