Inhaltsverzeichnis
Zuordnungsrate reduzieren
Die wichtigste Regel
Gleichzeitig können Ausnahmen früher erstellt werden, wenn die Kosten für die Objekterstellung so hoch sind, sodass sie andere Verarbeitungslogiken nicht beeinträchtigen.
Reduzieren Sie die Tiefe der Objekthierarchie
Referenzen zwischen Objekten reduzieren
Vermeiden Sie das Anheften von Objekten (Anheften)
Heim Backend-Entwicklung C#.Net-Tutorial Praktisches Tutorial zum Schreiben von Hochleistungs-.NET

Praktisches Tutorial zum Schreiben von Hochleistungs-.NET

Jun 25, 2017 am 09:08 AM
.net 代码 编写 翻译 高性能

Zuordnungsrate reduzieren

Dies bedarf fast keiner Erklärung. Es reduziert die Speichernutzung, was natürlich den Druck beim GC-Recycling verringert und gleichzeitig die Speicherfragmentierung und die CPU-Auslastung verringert. Es gibt Möglichkeiten, dies zu erreichen, es kann jedoch zu Konflikten mit anderen Designs kommen.

Sie müssen jedes Objekt beim Entwerfen sorgfältig prüfen und sich fragen:

  1. Brauche ich dieses Objekt wirklich?

  2. Ist dieses Feld das, was ich brauche?

  3. Kann ich die Größe des Arrays reduzieren?

  4. Kann ich die Größe von Grundelementen reduzieren (Int64 durch Int32 ersetzen usw.)?

  5. Werden diese Objekte nur in seltenen Fällen oder nur während der Initialisierung verwendet?

  6. Ist es möglich, einige Klassen in Strukturen umzuwandeln, sodass sie auf dem Stapel zugewiesen oder Teil eines Objekts werden können?

  7. Ordne ich viel Speicher zu, nutze aber nur einen winzigen Bruchteil davon?

  8. Kann ich relevante Daten von anderen Orten erhalten?

Kleine Geschichte: In einer Funktion auf dem Server, die auf Anfragen reagiert, haben wir festgestellt, dass in einer Anfrage etwas Speicher zugewiesen wird, der größer als das Speichersegment ist. Dies führt dazu, dass wir für jede Anfrage einen vollständigen GC auslösen. Dies liegt daran, dass die CLR erfordert, dass sich alle Objekte der Generation 0 in einem Speichersegment befinden. Wenn das aktuell zugewiesene Speichersegment voll ist, wird ein neues Speichersegment geöffnet Segment wird gleichzeitig geöffnet. Das Speichersegment wird für 2 Generationen recycelt. Dies ist keine gute Implementierung, da wir keine andere Möglichkeit haben, als die Speicherzuweisung zu reduzieren.

Die wichtigste Regel

Es gibt eine Grundregel für Hochleistungsprogrammierung mit Garbage Collection, die eigentlich eine Leitregel für das Codedesign ist.

Die zu sammelnden Objekte sind entweder in der Generation 0 oder gar nicht.
Sammeln Sie Objekte in der Generation 0 oder überhaupt nicht Das Objekt muss eine extrem kurze Lebensdauer haben und darf während der GC niemals berührt werden. Wenn dies nicht möglich ist, sollten sie so schnell wie möglich auf zwei Generationen übertragen werden und dort für immer bleiben. Sie werden niemals recycelt. Dies bedeutet, dass Sie einen Verweis auf ein langlebiges Objekt für immer behalten. Normalerweise bedeutet dies auch, dass Objekte wiederverwendet werden können, insbesondere Objekte in großen Objekthaufen. Das GC-Recycling für jede höhere Generation wird zeitaufwändiger sein als für die vorherige Generation. Wenn Sie viele Objekte der Generation 0,1 und wenige Objekte der Generation 2 behalten möchten. Selbst wenn der Hintergrund-GC für die Wiederverwertung über zwei Generationen aktiviert ist, verbraucht er viele CPU-Vorgänge. Möglicherweise möchten Sie diesen Teil der CPU lieber für die Anwendung als für GC verwenden.


Hinweis Sie haben vielleicht schon einmal ein Sprichwort gehört, dass jedes 10. Generation-0-Recycling zu einem 1-Generation-Recycling führt und jedes 10. Generation-1-Recycling zu einem 2-Generation-Recycling führt. Das ist eigentlich falsch, aber Sie müssen verstehen, dass Sie so viele schnelle Sammlungen der Generation 0 wie möglich und eine kleine Anzahl von Sammlungen der Generation 2 generieren möchten.

Sie sollten das Recycling der Generation 1 besser vermeiden, vor allem, weil Objekte, die von Generation 0 auf Generation 1 befördert wurden, zu diesem Zeitpunkt auf Generation 2 übertragen werden. Generation 1 ist ein Puffer für Objekte, die in Generation 2 eintreten.

Idealerweise sollte jedes Objekt, das Sie zuweisen, seinen Lebenszyklus vor der nächsten Sammlung der Generation 0 beenden. Sie können die Zeit zwischen GCs messen und sie mit der Lebensdauer von Objekten in Ihrer Anwendung vergleichen. Informationen zum Einsatz von Tools zur Messung von Lebenszyklen finden Sie am Ende dieses Kapitels.
Sie sind es vielleicht nicht gewohnt, so zu denken, aber diese Regel betrifft jeden Aspekt der Bewerbung. Sie müssen häufig darüber nachdenken und Ihre Mentalität grundlegend ändern, um diese wichtigste Regel umzusetzen.


Verkürzen Sie den Lebenszyklus von Objekten

Je kürzer der Umfang eines Objekts, desto geringer ist die Chance, dass es beim nächsten GC in die nächste Generation befördert wird. Generell gilt: Erstellen Sie Objekte erst, wenn Sie sie benötigen.

Gleichzeitig können Ausnahmen früher erstellt werden, wenn die Kosten für die Objekterstellung so hoch sind, sodass sie andere Verarbeitungslogiken nicht beeinträchtigen.

Darüber hinaus müssen Sie sicherstellen, dass das Objekt den Geltungsbereich so früh wie möglich verlässt. Bei lokalen Variablen können Sie deren Lebensdauer nach der letzten Verwendung oder sogar vor Ende der Methode beenden. Sie können den Code mit {} einschließen, was sich nicht auf Ihre Ausführung auswirkt, aber der Compiler geht davon aus, dass das Objekt in diesem Bereich seinen Lebenszyklus abgeschlossen hat und nicht mehr verwendet wird. Wenn Sie die Methode eines Objekts aufrufen müssen, versuchen Sie, das Zeitintervall zwischen dem ersten und dem letzten Aufruf zu verkürzen, damit der GC das Objekt so früh wie möglich wiederverwenden kann.

Wenn das Objekt mit einigen Objekten verknüpft (referenziert) ist, die über einen längeren Zeitraum beibehalten werden, müssen Sie deren Referenzbeziehungen freigeben. Möglicherweise verfügen Sie über mehr Nullprüfungen, wodurch der Code möglicherweise komplexer wird. Es kann auch zu einem Spannungsverhältnis zwischen dem verfügbaren Status des Objekts (immer der vollständige Status verfügbar) und der Effizienz führen, insbesondere beim Debuggen.
Eine Lösung besteht darin, das zu löschende Objekt auf andere Weise umzuwandeln, beispielsweise in eine Protokollnachricht, damit die relevanten Informationen beim anschließenden Debuggen abgefragt werden können.
Eine andere Methode besteht darin, dem Code konfigurierbare Optionen hinzuzufügen (ohne die Beziehung zwischen Objekten freizugeben): Beim Ausführen des Programms (oder beim Ausführen eines bestimmten Teils des Programms, z. B. einer bestimmten Anforderung) werden die Objekte in diesem Modus nicht freigegeben Referenzbeziehung, sondern um das Objekt für das Debuggen so praktisch wie möglich zu halten.

Reduzieren Sie die Tiefe der Objekthierarchie

Wie zu Beginn dieses Kapitels erwähnt, durchläuft der GC beim Recycling die Referenzbeziehung des Objekts. Im Server-GC-Modus wird der GC im Multithread-Modus ausgeführt. Wenn ein Thread jedoch ein Objekt auf einer tiefen Ebene verarbeiten muss, müssen alle Threads, die es verarbeitet haben, warten, bis dieser Thread die Verarbeitung abgeschlossen hat, bevor sie beendet werden . In zukünftigen CLR-Versionen müssen Sie diesem Problem nicht allzu viel Aufmerksamkeit schenken. GC wird einen besseren Markierungsalgorithmus für den Lastausgleich während der Multithread-Ausführung verwenden. Wenn Ihre Objektebene jedoch sehr tief ist, müssen Sie diesem Problem dennoch Aufmerksamkeit schenken.

Referenzen zwischen Objekten reduzieren

Dies hängt mit der Tiefe des vorherigen Abschnitts zusammen, aber es gibt auch einige andere Faktoren.
Wenn ein Objekt auf viele Objekte (Array, Liste) verweist, wird das Durchlaufen der Objekte viel Zeit in Anspruch nehmen. Es ist der GC, der lange Zeit ein Problem verursacht, da er über ein komplexes Beziehungsdiagramm verfügt.
Ein weiteres Problem besteht darin, dass Sie den Lebenszyklus des Objekts nicht genau vorhersagen können, wenn Sie nicht einfach bestimmen können, wie viele Referenzbeziehungen ein Objekt hat. Diese Komplexität zu reduzieren ist durchaus notwendig, nicht nur um den Code robuster zu machen, sondern auch um das Debuggen zu erleichtern und eine bessere Leistung zu erzielen.
Beachten Sie außerdem, dass Referenzen zwischen Objekten verschiedener Generationen ebenfalls zu GC-Ineffizienz führen, insbesondere Referenzen von alten Objekten auf neue Objekte. Wenn beispielsweise ein Objekt der 2. Generation eine Referenzbeziehung in einem Objekt der 0. Generation hat, müssen jedes Mal, wenn ein GC der Generation 0 auftritt, auch einige der Objekte der 2. Generation gescannt werden, um festzustellen, ob sie noch Verweise auf das Objekt der 0. Generation enthalten . Auch wenn dies kein vollständiger GC ist, ist es dennoch unnötige Arbeit und Sie sollten versuchen, diese Situation zu vermeiden.

Vermeiden Sie das Anheften von Objekten (Anheften)

Das Anheften von Objekten kann die Sicherheit der Daten gewährleisten, die vom verwalteten Code an den nativen Code übergeben werden. Üblich sind Arrays und Strings. Wenn Ihr Code nicht mit nativem Code interagieren muss, müssen Sie sich über den Leistungsaufwand keine Sorgen machen.
Das Anheften eines Objekts bedeutet, dass das Objekt während der Speicherbereinigung (Komprimierungsphase) nicht verschoben werden kann. Obwohl das Anheften von Objekten keinen großen Overhead verursacht, behindert es den GC-Recyclingvorgang und erhöht die Möglichkeit einer Speicherfragmentierung. Der GC zeichnet die Position von Objekten während des Recyclings auf, sodass der Raum zwischen ihnen bei der Neuzuweisung genutzt werden kann. Wenn jedoch viele angeheftete Objekte vorhanden sind, führt dies zu einer erhöhten Speicherfragmentierung.
Peg kann explizit oder implizit sein. Was gezeigt wird, ist, es mit dem Parameter GCHandleType.Pinned festzulegen, wenn GCHandle verwendet wird, oder das Schlüsselwort „fixed“ im unsicheren Modus zu verwenden. Der Unterschied zwischen der Verwendung des festen Schlüsselworts und GCHandle besteht darin, ob die Dispose-Methode explizit aufgerufen wird. Obwohl die Verwendung von „fixed“ sehr praktisch ist, kann sie in asynchronen Situationen nicht verwendet werden. Sie können jedoch dennoch ein Handle-Objekt (GCHandle) erstellen und es während des Rückrufs zurückgeben und verarbeiten.
Implizites Anheften kommt häufiger vor, ist aber schwerer zu erkennen und zu entfernen. Das offensichtlichste Beispiel ist die Übergabe von Objekten an nicht verwalteten Code über Plattformaufrufe (P/Invoke). Es geht nicht nur um Ihren Code – einige der verwalteten APIs, die Sie häufig aufrufen, rufen tatsächlich auch nativen Code auf und pinnen Objekte an.
Die CLR wird auch einige seiner eigenen Daten festlegen, aber dies ist normalerweise etwas, das Sie sich nicht interessieren.
Idealerweise sollten Sie versuchen, Objekte nicht so weit wie möglich zu stecken. Wenn dies nicht möglich ist, befolgen Sie die vorherigen wichtigen Regeln und versuchen Sie, diese festgehaltenen Objekte so bald wie möglich zu veröffentlichen. Wenn das Objekt einfach angeheftet und freigegeben wird, besteht kaum eine große Chance, dass es den Erfassungsvorgang beeinträchtigt. Sie möchten auch vermeiden, dass mehrere Objekte gleichzeitig festgehalten werden. Es ist etwas besser, wenn festgestellte Objekte gegen die 2. Generation ausgetauscht oder in LOH zugewiesen werden. Gemäß dieser Regel können Sie dem großen Objektheap einen großen Puffer zuweisen und den Puffer entsprechend dem tatsächlichen Bedarf selbst verwalten. Oder pufferen Puffer für Paar kleine Objekte zu und upgraden Sie sie dann auf die Generation 2 ein, bevor Sie sie festhalten. Dies ist besser, als das Objekt direkt an Generation 0 anzuheften.

Das obige ist der detaillierte Inhalt vonPraktisches Tutorial zum Schreiben von Hochleistungs-.NET. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Was soll ich tun, wenn die mit dem Edge-Browser gelieferte Übersetzungswebseite fehlt? Was soll ich tun, wenn die mit dem Edge-Browser gelieferte Übersetzungswebseite fehlt? Mar 14, 2024 pm 08:50 PM

Der Edge-Browser verfügt über eine Übersetzungsfunktion, die es Benutzern ermöglicht, jederzeit und überall zu übersetzen, was den Benutzern großen Komfort bietet. Viele Benutzer sagen jedoch, dass die integrierte Übersetzungswebseite fehlt. Was soll ich dann tun? Die von mir mitgebrachte Übersetzungsseite fehlt? Auf dieser Website erfahren Sie, wie Sie die mit dem Edge-Browser gelieferte übersetzte Webseite wiederherstellen können, wenn diese fehlt. So stellen Sie die fehlende Übersetzungswebseite des Edge-Browsers wieder her 1. Überprüfen Sie, ob die Übersetzungsfunktion aktiviert ist: Klicken Sie im Edge-Browser auf das Symbol mit den drei Punkten in der oberen rechten Ecke und wählen Sie dann die Option „Einstellungen“. Wählen Sie auf der linken Seite der Einstellungsseite die Option Sprache aus. Stellen Sie sicher, dass „Übersetzen&rd“ aktiviert ist.

Was tun, wenn der Bluescreen-Code 0x0000001 auftritt? Was tun, wenn der Bluescreen-Code 0x0000001 auftritt? Feb 23, 2024 am 08:09 AM

Was tun mit dem Bluescreen-Code 0x0000001? Der Bluescreen-Fehler ist ein Warnmechanismus, wenn ein Problem mit dem Computersystem oder der Hardware vorliegt. Der Code 0x0000001 weist normalerweise auf einen Hardware- oder Treiberfehler hin. Wenn Benutzer bei der Verwendung ihres Computers plötzlich auf einen Bluescreen-Fehler stoßen, geraten sie möglicherweise in Panik und sind ratlos. Glücklicherweise können die meisten Bluescreen-Fehler mit ein paar einfachen Schritten behoben werden. In diesem Artikel werden den Lesern einige Methoden zur Behebung des Bluescreen-Fehlercodes 0x0000001 vorgestellt. Wenn ein Bluescreen-Fehler auftritt, können wir zunächst versuchen, neu zu starten

Machen Sie sich keine Sorgen, wenn Sie Filme ohne Untertitel ansehen! Xiaomi kündigt die Einführung von Echtzeit-Untertiteln von Xiaoai Translation für japanische und koreanische Übersetzungen an Machen Sie sich keine Sorgen, wenn Sie Filme ohne Untertitel ansehen! Xiaomi kündigt die Einführung von Echtzeit-Untertiteln von Xiaoai Translation für japanische und koreanische Übersetzungen an Jul 22, 2024 pm 02:11 PM

Laut Nachrichten vom 22. Juli gab das offizielle Weibo von Xiaomi ThePaper OS heute bekannt, dass die Xiaoai-Übersetzung aktualisiert wurde. Echtzeit-Untertitel wurden zu japanischen und koreanischen Übersetzungen hinzugefügt und untertitelfreie Videos und Live-Konferenzen können transkribiert und übersetzt werden in Echtzeit. Das Simultandolmetschen von Angesicht zu Angesicht unterstützt die Übersetzung in 12 Sprachen, darunter Chinesisch, Englisch, Japanisch, Koreanisch, Russisch, Portugiesisch, Spanisch, Italienisch, Französisch, Deutsch, Indonesisch und Hindi. Die oben genannten Funktionen unterstützen derzeit nur die folgenden drei neuen Telefone: Xiaomi MIX Fold 4 Xiaomi MIX Flip Redmi K70 Extreme Edition Es wird berichtet, dass im Jahr 2021 die KI-Untertitel von Xiao Ai zu japanischen und koreanischen Übersetzungen hinzugefügt werden. KI-Untertitel nutzen Xiaomis selbst entwickelte Simultandolmetschertechnologie, um ein schnelleres, stabileres und genaueres Leseerlebnis für Untertitel zu ermöglichen. 1. Laut offizieller Aussage kann Xiaoai Translator nicht nur in Audio- und Video-Veranstaltungsorten verwendet werden

Beheben Sie den Fehlercode 0xc000007b Beheben Sie den Fehlercode 0xc000007b Feb 18, 2024 pm 07:34 PM

Beendigungscode 0xc000007b Bei der Verwendung Ihres Computers treten manchmal verschiedene Probleme und Fehlercodes auf. Unter ihnen ist der Beendigungscode am störendsten, insbesondere der Beendigungscode 0xc000007b. Dieser Code weist darauf hin, dass eine Anwendung nicht ordnungsgemäß gestartet werden kann, was zu Unannehmlichkeiten für den Benutzer führt. Lassen Sie uns zunächst die Bedeutung des Beendigungscodes 0xc000007b verstehen. Bei diesem Code handelt es sich um einen Fehlercode des Windows-Betriebssystems, der normalerweise auftritt, wenn eine 32-Bit-Anwendung versucht, auf einem 64-Bit-Betriebssystem ausgeführt zu werden. Es bedeutet, dass es so sein sollte

Universal-Fernbedienungscode-Programm von GE auf jedem Gerät Universal-Fernbedienungscode-Programm von GE auf jedem Gerät Mar 02, 2024 pm 01:58 PM

Wenn Sie ein Gerät aus der Ferne programmieren müssen, hilft Ihnen dieser Artikel. Wir teilen Ihnen die besten Universal-Fernbedienungscodes von GE für die Programmierung aller Geräte mit. Was ist eine GE-Fernbedienung? GEUniversalRemote ist eine Fernbedienung, mit der mehrere Geräte wie Smart-TVs, LG, Vizio, Sony, Blu-ray, DVD, DVR, Roku, AppleTV, Streaming-Media-Player und mehr gesteuert werden können. GEUniversal-Fernbedienungen gibt es in verschiedenen Modellen mit unterschiedlichen Merkmalen und Funktionen. GEUniversalRemote kann bis zu vier Geräte steuern. Top-Universalfernbedienungscodes zum Programmieren auf jedem Gerät GE-Fernbedienungen werden mit einer Reihe von Codes geliefert, die es ihnen ermöglichen, mit verschiedenen Geräten zu arbeiten. Sie können

So verwenden Sie Copilot zum Generieren von Code So verwenden Sie Copilot zum Generieren von Code Mar 23, 2024 am 10:41 AM

Als Programmierer bin ich begeistert von Tools, die das Programmiererlebnis vereinfachen. Mithilfe von Tools der künstlichen Intelligenz können wir Democode generieren und die erforderlichen Änderungen entsprechend den Anforderungen vornehmen. Das neu eingeführte Copilot-Tool in Visual Studio Code ermöglicht es uns, KI-generierten Code mit Chat-Interaktionen in natürlicher Sprache zu erstellen. Durch die Erläuterung der Funktionalität können wir die Bedeutung des vorhandenen Codes besser verstehen. Wie verwende ich Copilot zum Generieren von Code? Um zu beginnen, müssen wir zunächst die neueste PowerPlatformTools-Erweiterung herunterladen. Um dies zu erreichen, müssen Sie zur Erweiterungsseite gehen, nach „PowerPlatformTool“ suchen und auf die Schaltfläche „Installieren“ klicken

Wie kann das Problem gelöst werden, dass die integrierte Übersetzung von Google Chrome fehlschlägt? Wie kann das Problem gelöst werden, dass die integrierte Übersetzung von Google Chrome fehlschlägt? Mar 13, 2024 pm 08:46 PM

Browser verfügen im Allgemeinen über integrierte Übersetzungsfunktionen, sodass Sie sich keine Sorgen machen müssen, dass Sie beim Surfen auf fremdsprachigen Websites etwas nicht verstehen können! Google Chrome ist keine Ausnahme, aber einige Benutzer stellen fest, dass beim Öffnen der Übersetzungsfunktion von Google Chrome keine Reaktion erfolgt oder ein Fehler auftritt. Was sollten sie tun? Sie können die neueste Lösung ausprobieren, die ich gefunden habe. Bedienungsanleitung: Klicken Sie auf die drei Punkte in der oberen rechten Ecke und dann auf Einstellungen. Klicken Sie auf Sprache hinzufügen, fügen Sie Englisch und Chinesisch hinzu und nehmen Sie die folgenden Einstellungen vor. Die Einstellung „Englisch“ fragt, ob Webseiten in dieser Sprache übersetzt werden sollen, und Chinesisch muss davor verschoben werden kann als Standardsprache eingestellt werden. Wenn Sie die Webseite öffnen und keine Übersetzungsoption angezeigt wird, klicken Sie mit der rechten Maustaste und wählen Sie Chinesisch übersetzen, OK.

Schreiben Sie eine Methode zur Berechnung der Potenzfunktion in der Sprache C Schreiben Sie eine Methode zur Berechnung der Potenzfunktion in der Sprache C Feb 19, 2024 pm 01:00 PM

Wie schreibe ich eine Potenzierungsfunktion in der C-Sprache? Potenzierung (Potenzierung) ist eine häufig verwendete Operation in der Mathematik, die die Operation des mehrmaligen Multiplizierens einer Zahl mit sich selbst darstellt. In der Sprache C können wir diese Funktion implementieren, indem wir eine Potenzfunktion schreiben. Im Folgenden wird detailliert beschrieben, wie eine Power-Funktion in der C-Sprache geschrieben wird, und es werden spezifische Codebeispiele gegeben. Bestimmen Sie die Eingabe und Ausgabe der Funktion. Die Eingabe der Potenzfunktion enthält normalerweise zwei Parameter: Basis und Exponent, und die Ausgabe ist das berechnete Ergebnis. deshalb wir

See all articles