Heim  >  Artikel  >  Web-Frontend  >  Schleifenabrollen in JavaScript?

Schleifenabrollen in JavaScript?

王林
王林Original
2024-07-24 13:18:52940Durchsuche

Loop Unrolling in JavaScript?

JavaScript kann sich sehr weit von der Hardware entfernt anfühlen, auf der es läuft, aber in begrenzten Fällen kann es dennoch nützlich sein, auf niedriger Ebene zu denken.

In einem aktuellen Beitrag von Kafeel Ahmad zur Schleifenoptimierung wurden eine Reihe von Techniken zur Verbesserung der Schleifenleistung beschrieben. Dieser Artikel hat mich zum Nachdenken über das Thema gebracht.

Vorzeitige Optimierung

Um es gleich vorweg zu nehmen: Dies ist eine Technik, die nur sehr wenige bei der Webentwicklung jemals in Betracht ziehen müssen. Wenn Sie sich zu früh auf die Optimierung konzentrieren, kann es außerdem schwieriger werden, Code zu schreiben und zu warten. Ein Blick auf einfache Techniken kann uns einen Einblick in unsere Werkzeuge und die Arbeit im Allgemeinen geben, auch wenn wir dieses Wissen nicht direkt anwenden können.

Was ist Loop Unrolling?

Das Abrollen einer Schleife dupliziert grundsätzlich die Logik innerhalb einer Schleife, sodass Sie während jeder Schleife mehrere Operationen ausführen. In bestimmten Fällen kann es schneller sein, den Code in der Schleife länger zu machen.

Durch die absichtliche Ausführung einiger Vorgänge in Gruppen statt einzeln kann der Computer möglicherweise effizienter arbeiten.

Beispiel zum Abrollen

Nehmen wir ein sehr einfaches Beispiel: Summieren von Werten in einem Array.

// 1-to-1 looping
const simpleSum = (data) => {
  let sum = 0;
  for(let i=0; i < data.length; i += 1) {
    sum += data[i];
  }
  return sum;
};

const parallelSum = (data) => {
  let sum1 = 0;
  let sum2 = 0;
  for(let i=0; i < data.length; i += 2) {
    sum1 += data[i];
    sum2 += data[i + 1];
  }
  return sum1 + sum2;
};

Das mag auf den ersten Blick sehr seltsam aussehen. Wir verwalten mehr Variablen und führen zusätzliche Operationen durch, die im einfachen Beispiel nicht vorkommen. Wie kann das schneller gehen?!

Den Unterschied messen

Ich habe einige Vergleiche mit verschiedenen Datengrößen und mehreren Durchläufen sowie sequentiellen oder verschachtelten Tests durchgeführt. Die Leistung von parallelSum schwankte, war jedoch fast immer besser, mit Ausnahme einiger seltsamer Ergebnisse bei sehr kleinen Datengrößen. Ich habe dies mit RunJS getestet, das auf der V8-Engine von Chrome basiert.

Unterschiedliche Datengrößen ergaben sehr ungefähr diese Ergebnisse:

  • Klein (< 10.000): Selten ein Unterschied
  • Mittel (10.000–100.000): Normalerweise ca. 20–80 % schneller
  • Groß (> 1M): Konstant doppelt so schnell

    Dann habe ich ein JSPerf mit 1 Million Datensätzen erstellt, um es in verschiedenen Browsern auszuprobieren. Probieren Sie es selbst aus!

    Chrome führte parallelSum doppelt so schnell aus wie simpleSum, wie aus den RunJS-Tests hervorgeht.

    Safari war fast identisch mit Chrome, sowohl in Prozent als auch in den Operationen pro Sekunde.

    Firefox auf demselben System hat für simpleSum fast die gleiche Leistung erbracht, aber parallelSum war nur etwa 15 % schneller, nicht doppelt so schnell.

    Diese Variante hat mich dazu gebracht, nach weiteren Informationen zu suchen. Obwohl es nichts Endgültiges ist, habe ich einen StackOverflow-Kommentar aus dem Jahr 2016 gefunden, in dem einige der Probleme der JS-Engine beim Abrollen von Schleifen besprochen werden. Es ist ein interessanter Blick darauf, wie Engines und Optimierungen den Code auf eine Weise beeinflussen können, die wir nicht erwarten.

    Variationen

    Ich habe auch eine dritte Version ausprobiert, die zwei Werte in einem einzigen Vorgang addierte, um zu sehen, ob es einen spürbaren Unterschied zwischen einer Variablen und zwei gab.

    const parallelSum = (data) => {
      let sum = 0
      for(let i=0; i < data.length; i += 2) {
        sum += data[i] + data[i + 1];
      }
      return sum;
    };
    

    Kurze Antwort: Nein. Die beiden „parallelen“ Versionen lagen innerhalb der gemeldeten Fehlertoleranz.

    Also, wie funktioniert es?

    Während JavaScript Single-Threaded ist, können die Interpreter, Compiler und die Hardware darunter Optimierungen für uns durchführen, wenn bestimmte Bedingungen erfüllt sind.

    Im einfachen Beispiel benötigt die Operation den Wert i, um zu wissen, welche Daten abgerufen werden sollen, und zum Aktualisieren den neuesten Wert von sum. Da sich beides in jeder Schleife ändert, muss der Computer warten, bis die Schleife abgeschlossen ist, um weitere Daten zu erhalten. Während es für uns offensichtlich erscheinen mag, was i += 1 bewirken wird, versteht der Computer meistens „Der Wert wird sich ändern, schauen Sie später noch einmal nach“ und hat daher Schwierigkeiten bei der Optimierung.

    Unsere Parallelversionen laden mehrere Dateneinträge für jeden Wert von i. Wir sind immer noch auf die Summe für jede Schleife angewiesen, können aber doppelt so viele Daten pro Zyklus laden und verarbeiten. Das heißt aber nicht, dass es doppelt so schnell läuft.

    Tieferer Tauchgang

    Um zu verstehen, warum das Schleifenabrollen funktioniert, schauen wir uns den Low-Level-Betrieb eines Computers an. Prozessoren mit superskalaren Architekturen können über mehrere Pipelines verfügen, um gleichzeitige Vorgänge auszuführen. Sie können die Ausführung außerhalb der Reihenfolge unterstützen, sodass Vorgänge, die nicht voneinander abhängig sind, so schnell wie möglich ausgeführt werden können. Bei einigen Vorgängen kann SIMD eine Aktion für mehrere Daten gleichzeitig ausführen. Darüber hinaus beschäftigen wir uns mit Caching, Datenabruf und Verzweigungsvorhersage...

    Aber das ist ein JavaScript-Artikel! Wir gehen nicht so tief. Wenn Sie mehr über Prozessorarchitekturen erfahren möchten, bietet Anandtech einige ausgezeichnete Deep Dives an.

    Grenzen und Nachteile

    Loop-Abrollen ist keine Zauberei. Aufgrund der Programm- oder Datengröße, der Operationskomplexität, der Computerarchitektur usw. gibt es Grenzen und sinkende Erträge. Aber wir haben nur ein oder zwei Vorgänge getestet und moderne Computer unterstützen oft vier oder mehr Threads.

    Um einige größere Inkremente auszuprobieren, habe ich ein weiteres JSPerf mit 1, 2, 4 und 10 Datensätzen erstellt und es auf einem Apple M1 Max MacBook Pro mit macOS 14.5 Sonoma und einem AMD Ryzen 9 3950X PC mit Windows 11 ausgeführt.

    Zehn Datensätze gleichzeitig waren 2,5-3,5-mal schneller als die Basisschleife, aber nur 12-15 % schneller als die Verarbeitung von vier Datensätzen auf dem Mac. Auf dem PC sahen wir immer noch die 2-fache Verbesserung zwischen einem und zwei Datensätzen, aber zehn Datensätze waren nur 2 % schneller als vier Datensätze, was ich für einen 16-Kern-Prozessor nicht erwartet hätte.

    Plattformen und Updates

    Diese unterschiedlichen Ergebnisse erinnern uns daran, bei der Optimierung vorsichtig zu sein. Eine Optimierung für Ihren Computer kann auf weniger leistungsfähiger oder einfach anderer Hardware zu einem schlechteren Erlebnis führen. Leistungs- oder Funktionsprobleme bei älterer Hardware oder Hardware der Einstiegsklasse sind ein häufiges Problem, wenn Entwickler an schnellen, leistungsstarken Maschinen arbeiten, und ich wurde in meiner Karriere schon mehrfach damit beauftragt.

    Für eine gewisse Leistungsskalierung verfügt ein derzeit erhältliches Einstiegs-Chromebook von HP über einen Intel Celeron N4120-Prozessor. Dies entspricht in etwa meinem 2013er Core i5-4250U MacBook Air. Es hat nur ein Neuntel die Leistung des M1 Max in einem synthetischen Benchmark. Auf diesem MacBook Air von 2013, auf dem die neueste Version von Chrome ausgeführt wurde, war die 4-Datensatz-Funktion schneller als die 10-Datensatz-Funktion, aber immer noch nur 60 % schneller als die Einzeldatensatz-Funktion!

    Auch Browser und Standards ändern sich ständig. Ein routinemäßiges Browser-Update oder eine andere Prozessorarchitektur könnte dazu führen, dass optimierter Code langsamer wird als eine normale Schleife. Wenn Sie feststellen, dass Sie stark optimieren, müssen Sie möglicherweise sicherstellen, dass Ihre Optimierung für Ihre Verbraucher relevant ist und relevant bleibt.

    Es erinnert mich an das Buch High Performance JavaScript von Nicholas Zakas, das ich 2012 gelesen habe. Es war ein großartiges Buch und enthielt viele Erkenntnisse. Bis 2014 wurden jedoch einige der im Buch identifizierten erheblichen Leistungsprobleme durch Browser-Engine-Updates behoben oder erheblich reduziert, und wir konnten uns mehr auf das Schreiben von wartbarem Code konzentrieren.

    Wenn Sie bei der Leistungsoptimierung auf dem neuesten Stand bleiben möchten, seien Sie auf Veränderungen und regelmäßige Validierungen vorbereitet.

    Lehren aus der Vergangenheit

    Bei der Recherche zu diesem Thema bin ich auf einen Linux-Kernel-Mailinglisten-Thread aus dem Jahr 2000 gestoßen, in dem es um die Entfernung einiger Loop-Unrolling-Optimierungen ging, die letztendlich die Anwendungsleistung verbesserten. Es enthielt diesen immer noch relevanten Punkt (Hervorhebung von mir):

    Die Quintessenz ist, dass unsere intuitiven Annahmen darüber, was schnell ist und was nicht, oft falsch sein können, insbesondere wenn man bedenkt, wie stark sich die CPUs in den letzten Jahren verändert haben.
    – Theodore Ts'o

    Abschluss

    Es kann vorkommen, dass Sie die Leistung aus einer Schleife herausholen müssen, und wenn Sie genügend Elemente verarbeiten, könnte dies eine Möglichkeit sein, dies zu tun. Es ist gut, über diese Art von Optimierungen Bescheid zu wissen, aber für die meisten Arbeiten braucht man es nicht™.

    Dennoch hoffe ich, dass Ihnen mein Streifzug gefallen hat und dass Ihr Gedächtnis vielleicht in Zukunft über Überlegungen zur Leistungsoptimierung aufgefrischt wird.

    Danke fürs Lesen!

    Das obige ist der detaillierte Inhalt vonSchleifenabrollen in JavaScript?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
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