Das Arbeiten mit unveränderlichen Daten mit einzelnen Zuweisungen hat den offensichtlichen Effekt, dass mehr Speicher benötigt wird, da Sie ständig neue Werte erstellen (obwohl Compiler unter der Decke Zeigertricks ausführen, um dies weniger problematisch zu machen).
Aber ich habe jetzt ein paar Mal gehört, dass die Performance-Verluste durch die Gewinne in der Art aufgewogen werden, wie die CPU (speziell ihr Speichercontroller) die Tatsache ausnutzen kann, dass der Speicher nicht (so sehr) mutiert.
Ich hatte gehofft, jemand könnte etwas Licht ins Dunkel bringen, wie das wahr ist (oder wenn es nicht so ist?).
In einem Kommentar zu einem anderen Beitrag wurde erwähnt, dass abstrakte Datentypen (ADTs) damit zu tun haben, was mich weiter neugierig machte, wie sich ADTs speziell auf die Art und Weise auswirken, wie die CPU mit Speicher umgeht. Dies ist jedoch eine Randbemerkung. Hauptsächlich interessiert mich, wie sich die Reinheit der Sprache notwendigerweise auf die Leistung der CPU und ihrer Caches usw. auswirkt.
quelle
let a = [1,2,3] in let b = 0:a in (a, b, (-1):c)
Teilung verringert den Speicherbedarf, sondern hängt von der Definition(:)
und[]
und nicht der Compiler. Ich glaube? Ich bin mir nicht sicher.Antworten:
Dies erspart dem Compiler die Verwendung von Membar- Anweisungen, wenn auf Daten zugegriffen wird.
Sie sehen, wenn auf Daten von verschiedenen Threads aus zugegriffen wird, geschieht dies auf einer Multi-Core-CPU wie folgt: Verschiedene Threads werden auf verschiedenen Kernen ausgeführt, wobei jeder seinen eigenen Cache (lokal zu ihrem Kern) verwendet - eine Kopie eines globalen Caches.
Wenn die Daten veränderlich sind und der Programmierer sie zwischen verschiedenen Threads konsistent haben muss, müssen Maßnahmen ergriffen werden, um die Konsistenz zu gewährleisten. Für Programmierer bedeutet dies, Synchronisationskonstrukte zu verwenden, wenn sie auf Daten in einem bestimmten Thread zugreifen (z. B. diese lesen).
Für den Compiler bedeutet das Synchronisationskonstrukt im Code, dass ein Membar-Befehl eingefügt werden muss, um sicherzustellen, dass Änderungen an der Kopie der Daten auf einem der Kerne ordnungsgemäß weitergegeben ("veröffentlicht") werden, um sicherzustellen, dass die Caches auf anderen Kernen gespeichert werden habe die gleiche (aktuelle) Kopie.
Etwas vereinfachend siehe Hinweis unten , hier ist, was bei Multi-Core-Prozessor für Membar passiert:
Sie sehen, alle Kerne tun nichts, während Daten zwischen globalen und lokalen Caches hin und her kopiert werden . Dies ist erforderlich, um sicherzustellen, dass veränderbare Daten ordnungsgemäß synchronisiert sind (threadsicher). Wenn 4 Kerne vorhanden sind, werden alle 4 angehalten und warten, während die Caches synchronisiert werden. Wenn es 8 gibt, stoppen alle 8. Wenn es 16 gibt ... nun, Sie haben 15 Kerne, die genau nichts tun, während Sie auf das warten, was an einem dieser Kerne erledigt werden muss.
Nun wollen wir sehen, was passiert, wenn Daten unveränderlich sind. Egal welcher Thread darauf zugreift, es ist garantiert derselbe. Für Programmierer bedeutet dies keine Notwendigkeit zum Einfügen von Synchronisationskonstrukten , wenn sie Zugriff (Lesen) von Daten in bestimmten Thread.
Für Compiler bedeutet dies wiederum keine Notwendigkeit , eine einfügen Membar Anweisung .
Infolgedessen muss der Zugriff auf Daten nicht die Prozessorkerne anhalten und warten, während Daten zwischen globalen und lokalen Caches hin und her geschrieben werden. Das ist ein Vorteil der Tatsache, dass der Speicher nicht mutiert ist .
Beachten Sie die etwas vereinfachende Erklärung oben, die einige kompliziertere negative Auswirkungen von Daten, die veränderlich sind, zum Beispiel auf das Pipelining, fallen lässt . Um die erforderliche Bestellung zu gewährleisten, muss die CPU von Datenänderungen betroffene Pilotlinien ungültig machen - das ist ein weiterer Leistungsnachteil. Wenn dies durch eine einfache (und damit zuverlässige) Ungültigmachung aller Pipelines implementiert wird, wird der negative Effekt weiter verstärkt.
quelle