Riesiger Leistungsunterschied (26x schneller) beim Kompilieren für 32 und 64 Bit

79

Ich habe versucht, den Unterschied zwischen a forund a foreachbeim Zugriff auf Listen mit Werttypen und Referenztypen zu messen .

Ich habe die folgende Klasse verwendet, um die Profilerstellung durchzuführen.

public static class Benchmarker
{
    public static void Profile(string description, int iterations, Action func)
    {
        Console.Write(description);

        // Warm up
        func();

        Stopwatch watch = new Stopwatch();

        // Clean up
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        watch.Start();
        for (int i = 0; i < iterations; i++)
        {
            func();
        }
        watch.Stop();

        Console.WriteLine(" average time: {0} ms", watch.Elapsed.TotalMilliseconds / iterations);
    }
}

Ich habe doublefür meinen Werttyp verwendet. Und ich habe diese 'gefälschte Klasse' erstellt, um Referenztypen zu testen:

class DoubleWrapper
{
    public double Value { get; set; }

    public DoubleWrapper(double value)
    {
        Value = value;
    }
}

Schließlich habe ich diesen Code ausgeführt und die Zeitunterschiede verglichen.

static void Main(string[] args)
{
    int size = 1000000;
    int iterationCount = 100;

    var valueList = new List<double>(size);
    for (int i = 0; i < size; i++) 
        valueList.Add(i);

    var refList = new List<DoubleWrapper>(size);
    for (int i = 0; i < size; i++) 
        refList.Add(new DoubleWrapper(i));

    double dummy;

    Benchmarker.Profile("valueList for: ", iterationCount, () =>
    {
        double result = 0;
        for (int i = 0; i < valueList.Count; i++)
        {
             unchecked
             {
                 var temp = valueList[i];
                 result *= temp;
                 result += temp;
                 result /= temp;
                 result -= temp;
             }
        }
        dummy = result;
    });

    Benchmarker.Profile("valueList foreach: ", iterationCount, () =>
    {
        double result = 0;
        foreach (var v in valueList)
        {
            var temp = v;
            result *= temp;
            result += temp;
            result /= temp;
            result -= temp;
        }
        dummy = result;
    });

    Benchmarker.Profile("refList for: ", iterationCount, () =>
    {
        double result = 0;
        for (int i = 0; i < refList.Count; i++)
        {
            unchecked
            {
                var temp = refList[i].Value;
                result *= temp;
                result += temp;
                result /= temp;
                result -= temp;
            }
        }
        dummy = result;
    });

    Benchmarker.Profile("refList foreach: ", iterationCount, () =>
    {
        double result = 0;
        foreach (var v in refList)
        {
            unchecked
            {
                var temp = v.Value;
                result *= temp;
                result += temp;
                result /= temp;
                result -= temp;
            }
        }

        dummy = result;
    });

    SafeExit();
}

Ich habe Releaseund Any CPUOptionen ausgewählt , das Programm ausgeführt und die folgenden Zeiten erhalten:

valueList for:  average time: 483,967938 ms
valueList foreach:  average time: 477,873079 ms
refList for:  average time: 490,524197 ms
refList foreach:  average time: 485,659557 ms
Done!

Dann habe ich die Optionen Release und x64 ausgewählt, das Programm ausgeführt und die folgenden Zeiten erhalten:

valueList for:  average time: 16,720209 ms
valueList foreach:  average time: 15,953483 ms
refList for:  average time: 19,381077 ms
refList foreach:  average time: 18,636781 ms
Done!

Warum ist die x64-Bit-Version so viel schneller? Ich habe einen Unterschied erwartet, aber nichts so Großes.

Ich habe keinen Zugriff auf andere Computer. Könnten Sie dies bitte auf Ihren Maschinen ausführen und mir die Ergebnisse mitteilen? Ich verwende Visual Studio 2015 und habe einen Intel Core i7 930.

Hier ist die SafeExit()Methode, damit Sie selbst kompilieren / ausführen können:

private static void SafeExit()
{
    Console.WriteLine("Done!");
    Console.ReadLine();
    System.Environment.Exit(1);
}

Wie gewünscht, double?anstelle von DoubleWrapper:

Beliebige CPU

valueList for:  average time: 482,98116 ms
valueList foreach:  average time: 478,837701 ms
refList for:  average time: 491,075915 ms
refList foreach:  average time: 483,206072 ms
Done!

x64

valueList for:  average time: 16,393947 ms
valueList foreach:  average time: 15,87007 ms
refList for:  average time: 18,267736 ms
refList foreach:  average time: 16,496038 ms
Done!

Last but not least: Wenn x86ich ein Profil erstelle, bekomme ich fast die gleichen Ergebnisse bei der VerwendungAny CPU .

Trauer
quelle
14
"Beliebige CPU"! = "32Bits"! Wenn "Jede CPU" kompiliert ist, sollte Ihre Anwendung als 64-Bit-Prozess auf Ihrem 64-Bit-System ausgeführt werden. Außerdem würde ich den Code entfernen, der mit dem GC in Konflikt gerät. Es hilft eigentlich nicht.
Thorsten Dittmar
9
@ThorstenDittmar Die GC-Aufrufe erfolgen vor der Messung und nicht im gemessenen Code. Dies ist vernünftig genug, um das Ausmaß zu verringern, in dem das Glück des GC-Timings eine solche Messung beeinflussen kann. Außerdem gibt es zwischen den Builds "32-Bit-Bevorzugung" und "64-Bit-Bevorzugung" als Faktor.
Jon Hanna
1
@ThorstenDittmar Aber ich führe die Release-Version (außerhalb von Visual Studio) aus und der Task-Manager sagt, dass es sich um eine 32-Bit-Anwendung handelt (wenn sie auf eine beliebige CPU kompiliert wurde). Ebenfalls. Wie Jon Hanna sagte, ist der GC-Anruf nützlich.
Trauer
2
Welche Laufzeitversion verwenden Sie? Das neue RyuJIT in 4.6 ist viel schneller, aber selbst für frühere Versionen waren der x64-Compiler und der JITer neuer und fortschrittlicher als die x32-Versionen. Sie können weitaus aggressivere Optimierungen durchführen als die x86-Versionen.
Panagiotis Kanavos
2
Ich würde bemerken, dass der betreffende Typ keine Wirkung zu haben scheint; ändern doublezu float, longoder intund Sie ähnliche Ergebnisse erhalten.
Jon Hanna

Antworten:

87

Ich kann dies am 4.5.2 reproduzieren. Kein RyuJIT hier. Sowohl x86- als auch x64-Demontagen sehen vernünftig aus. Bereichsprüfungen usw. sind gleich. Die gleiche Grundstruktur. Kein Abrollen der Schleife.

x86 verwendet einen anderen Satz von Float-Anweisungen. Die Leistung dieser Anweisungen scheint mit Ausnahme der Unterteilung mit den x64-Anweisungen vergleichbar zu sein :

  1. Die 32-Bit-x87-Float-Anweisungen verwenden intern eine Genauigkeit von 10 Byte.
  2. Die erweiterte Präzisionsteilung ist sehr langsam.

Die Teilungsoperation macht die 32-Bit-Version extrem langsam. Wenn Sie die Division nicht kommentieren, wird die Leistung in hohem Maße ausgeglichen (32 Bit von 430 ms auf 3,25 ms).

Peter Cordes weist darauf hin, dass die Befehlslatenzen der beiden Gleitkommaeinheiten nicht so unterschiedlich sind. Möglicherweise sind einige der Zwischenergebnisse denormalisierte Zahlen oder NaN. Diese können einen langsamen Pfad in einer der Einheiten auslösen. Oder möglicherweise weichen die Werte zwischen den beiden Implementierungen aufgrund der Float-Genauigkeit von 10 Byte gegenüber 8 Byte ab.

Peter Cordes weist auch darauf hin, dass alle Zwischenergebnisse NaN sind ... Das Entfernen dieses Problems ( valueList.Add(i + 1)so dass kein Divisor Null ist) gleicht die Ergebnisse größtenteils aus. Anscheinend mag der 32-Bit-Code überhaupt keine NaN-Operanden. Drucken wir einige Zwischenwerte aus : if (i % 1000 == 0) Console.WriteLine(result);. Dies bestätigt, dass die Daten jetzt gesund sind.

Beim Benchmarking müssen Sie eine realistische Arbeitsbelastung messen. Aber wer hätte gedacht, dass eine unschuldige Spaltung Ihren Benchmark durcheinander bringen kann?!

Summieren Sie einfach die Zahlen, um einen besseren Benchmark zu erhalten.

Division und Modulo sind immer sehr langsam. Wenn Sie den BCL- DictionaryCode so ändern, dass der Modulo-Operator nicht zur Berechnung der messbaren Leistung des Bucket-Index verwendet wird, verbessert sich die Leistung. So langsam ist die Teilung.

Hier ist der 32-Bit-Code:

Geben Sie hier die Bildbeschreibung ein

64-Bit-Code (gleiche Struktur, schnelle Teilung):

Geben Sie hier die Bildbeschreibung ein

Dies wird trotz Verwendung von SSE-Anweisungen nicht vektorisiert.

usr
quelle
11
"Wer hätte gedacht, dass eine unschuldige Abteilung Ihren Benchmark durcheinander bringen kann?" Ich tat es sofort, sobald ich eine Teilung in der inneren Schleife sah, insb. als Teil der Abhängigkeitskette. Division ist nur dann unschuldig, wenn es sich um eine ganzzahlige Division durch eine Potenz von 2 handelt. Aus agner.org/optimize insn-Tabellen: Nehalem fdivhat eine Latenz von 7 bis 27 Zyklen (und denselben wechselseitigen Durchsatz). divsdbeträgt 7-22 Zyklen. addsdbei 3c Latenz 1 / c Durchsatz. Division ist die einzige Nicht-Pipeline-Ausführungseinheit in Intel / AMD-CPUs. C # JIT vektorisiert die Schleife für x86-64 (mit divPd) nicht.
Peter Cordes
1
Ist es auch normal, dass 32b C # keine SSE-Mathematik verwendet? Ist es nicht möglich, die Funktionen des aktuellen Maschinenteils von JIT zu nutzen? Bei Haswell und höher könnten Integer-Schleifen mit 256b AVX2 anstelle von nur SSE automatisch vektorisiert werden. Um die Vektorisierung von FP-Schleifen zu erhalten, müssten Sie sie vermutlich mit 4 Akkumulatoren parallel schreiben, da die FP-Mathematik nicht assoziativ ist. Die Verwendung von SSE im 32-Bit-Modus ist jedoch schneller, da Sie weniger Anweisungen haben, um dieselbe Skalararbeit auszuführen, wenn Sie nicht mit dem x87-FP-Stack jonglieren müssen.
Peter Cordes
4
Wie auch immer, div ist sehr langsam, aber 10B x87 fdiv ist nicht viel langsamer als 8B SSE2, daher erklärt dies nicht den Unterschied zwischen x86 und x86-64. Was es erklären könnte, sind FPU-Ausnahmen oder Verlangsamungen mit Denormalen / Unendlichkeiten. Das x87-FPU-Steuerwort ist vom SSE-Rundungs- / Ausnahmesteuerungsregister ( MXCSR) getrennt. Ein anderer Umgang mit Denormalen oder NaNs könnte meiner Meinung nach den Faktor 26 perf diff erklären. C # kann im MXCSR Denormals-are-Zero setzen.
Peter Cordes
2
@Trauer und usr: Ich habe gerade bemerkt, dass die valueList[i] = i, beginnend mit i=0, also die erste Schleifeniteration tut 0.0 / 0.0. Jede Operation in Ihrem gesamten Benchmark wird also mit NaNs ausgeführt. Diese Abteilung sieht immer weniger unschuldig aus! Ich bin kein Experte für Leistung mit NaNs oder den Unterschied zwischen x87 und SSE, aber ich denke, dies erklärt den 26-fachen Perf-Unterschied. Ich wette, Ihre Ergebnisse werden zwischen 32 und 64 Bit viel näher liegen, wenn Sie initialisieren valueList[i] = i+1.
Peter Cordes
1
Was das Flush-to-Zero angeht, bin ich mit 64-Bit-Double nicht besonders begeistert, aber wenn 80-Bit-Extended und 64-Bit-Double zusammen verwendet werden, kann es vorkommen, dass ein 80-Bit-Wert unterläuft und dann ausreichend skaliert wird Es doublewäre ziemlich selten, einen Wert zu erhalten, der als 64-Bit darstellbar wäre. Eines der Hauptverwendungsmuster für den 80-Bit-Typ bestand darin, die Summierung mehrerer Zahlen zu ermöglichen, ohne die Ergebnisse bis zum Ende eng runden zu müssen. Unter diesem Muster sind Überläufe einfach kein Problem.
Supercat
31

valueList[i] = iAusgehend von i=0der ersten Schleifeniteration 0.0 / 0.0. Jede Operation in Ihrem gesamten Benchmark wird also mit NaNs ausgeführt.

Wie @usr in der Demontage-Ausgabe zeigte , verwendete die 32-Bit-Version x87-Gleitkomma, während 64-Bit-SSE-Gleitkomma verwendete.

Ich bin kein Experte für Leistung mit NaNs oder den Unterschied zwischen x87 und SSE, aber ich denke, dies erklärt den 26-fachen Perf-Unterschied. Ich wette, Ihre Ergebnisse werden zwischen 32 und 64 Bit viel näher liegen, wenn Sie initialisieren valueList[i] = i+1. (Update: usr hat bestätigt, dass dies die Leistung von 32 und 64 Bit ziemlich nahe gebracht hat.)

Die Teilung ist im Vergleich zu anderen Operationen sehr langsam. Siehe meine Kommentare zur Antwort von @ usr. Unter http://agner.org/optimize/ finden Sie auch eine Menge großartiger Informationen zur Hardware und zur Optimierung von ASM und C / C ++, von denen einige für C # relevant sind. Er verfügt über Anweisungstabellen für Latenz und Durchsatz für die meisten Anweisungen für alle neueren x86-CPUs.

10B x87 fdivist jedoch divsdfür normale Werte nicht viel langsamer als die doppelte 8B-Genauigkeit von SSE2 . IDK über Leistungsunterschiede mit NaNs, Unendlichkeiten oder Denormalen.

Sie haben jedoch unterschiedliche Steuerelemente für das, was mit NaNs und anderen FPU-Ausnahmen geschieht. Das x87-FPU- Steuerwort ist vom SSE-Rundungs- / Ausnahmesteuerregister (MXCSR) getrennt. Wenn x87 für jede Abteilung eine CPU-Ausnahme erhält, SSE jedoch nicht, erklärt dies leicht den Faktor 26. Oder es gibt nur einen so großen Leistungsunterschied beim Umgang mit NaNs. Die Hardware ist nicht für das Durchlaufen NaNnachher optimiert NaN.

IDK, wenn die SSE-Kontrollen zur Vermeidung von Verlangsamungen mit Denormalen hier ins Spiel kommen, da ich glaube, resultdass dies die NaNganze Zeit sein wird. IDK, wenn C # im MXCSR das Denormals-are-zero-Flag oder das Flush-to-Zero-Flag setzt (das zuerst Nullen schreibt, anstatt Denormals beim Zurücklesen als Null zu behandeln).

Ich habe einen Intel-Artikel über SSE-Gleitkomma-Steuerelemente gefunden, der dem x87-FPU-Steuerwort gegenübersteht. Es gibt jedoch nicht viel zu sagen NaN. Es endet damit:

Fazit

Um Serialisierungs- und Leistungsprobleme aufgrund von Denormals und Unterlaufnummern zu vermeiden, verwenden Sie die Anweisungen SSE und SSE2, um die Modi Flush-to-Zero und Denormals-Are-Zero innerhalb der Hardware festzulegen und die höchste Leistung für Gleitkommaanwendungen zu ermöglichen.

IDK, wenn dies bei der Division durch Null hilft.

für vs. foreach

Es könnte interessant sein, einen durch den Durchsatz begrenzten Schleifenkörper zu testen, anstatt nur eine einzige von Schleifen getragene Abhängigkeitskette zu sein. So wie es ist, hängt die gesamte Arbeit von früheren Ergebnissen ab; Die CPU kann nichts parallel tun (außer Grenzen - überprüfen Sie die nächste Array-Last, während die Mul / Div-Kette ausgeführt wird).

Möglicherweise sehen Sie einen größeren Unterschied zwischen den Methoden, wenn die "echte Arbeit" mehr Ausführungsressourcen der CPUs belegt. Außerdem gibt es bei Intel vor Sandybridge einen großen Unterschied zwischen einer Schleifenanpassung im 28uop-Schleifenpuffer oder nicht. Wenn nicht, erhalten Sie Engpässe beim Dekodieren von Anweisungen. wenn die durchschnittliche Befehlslänge länger ist (was bei SSE der Fall ist). Anweisungen, die auf mehr als ein UOP dekodieren, begrenzen auch den Decoderdurchsatz, es sei denn, sie haben ein für die Decoder geeignetes Muster (z. B. 2-1-1). Eine Schleife mit mehr Anweisungen zum Schleifen-Overhead kann also den Unterschied zwischen einer Schleifenanpassung im UOP-Cache mit 28 Einträgen oder nicht ausmachen, was für Nehalem eine große Sache ist und manchmal für Sandybridge und später hilfreich ist.

Peter Cordes
quelle
Ich hatte noch nie einen Fall, in dem ich einen Leistungsunterschied festgestellt habe, der davon abhängt, ob NaNs in meinem Datenstrom enthalten sind, aber das Vorhandensein denormalisierter Zahlen kann einen großen Leistungsunterschied bewirken . In diesem Beispiel scheint dies nicht der Fall zu sein, aber es ist etwas zu beachten.
Jason R
@ JasonR: Ist das nur so, weil NaNs in der Praxis wirklich selten sind? Ich habe all das Zeug über Denormals und den Link zu Intels Zeug hinterlassen, hauptsächlich zum Nutzen der Leser, nicht weil ich dachte, dass es wirklich viel Einfluss auf diesen speziellen Fall haben würde.
Peter Cordes
In den meisten Anwendungen sind sie selten. Bei der Entwicklung einer neuen Software, die Gleitkomma verwendet, kommt es jedoch nicht selten vor, dass Implementierungsfehler NaN-Ströme anstelle der gewünschten Ergebnisse liefern! Das ist mir schon oft in den Sinn gekommen und ich kann mich an keinen merklichen Leistungseinbruch erinnern, wenn NaNs auftauchen. Ich habe das Gegenteil beobachtet, wenn ich etwas tue, das dazu führt, dass Denormale auftreten. Dies führt normalerweise zu einem sofort spürbaren Leistungsabfall. Beachten Sie, dass diese nur auf meiner anekdotischen Erfahrung beruhen; Bei NaNs kann es zu Leistungseinbußen kommen, die ich einfach nicht bemerkt habe.
Jason R
@ JasonR: IDK, vielleicht sind NaNs mit SSE nicht viel langsamer. Offensichtlich sind sie ein großes Problem für x87. Die SSE FP-Semantik wurde in den PII / PIII-Tagen von Intel entwickelt. Diese CPUs haben die gleichen Maschinen außer Betrieb wie die aktuellen Designs, so dass sie vermutlich beim Entwurf von SSE eine hohe Leistung für P6 im Auge hatten. (Ja, Skylake basiert auf der P6-Mikroarchitektur. Einige Dinge haben sich geändert, aber es dekodiert immer noch in Uops und plant sie für Ausführungsports mit einem Nachbestellungspuffer.) Die x87-Semantik wurde für einen optionalen externen Co-Prozessor-Chip für entwickelt eine in der Reihenfolge skalare CPU.
Peter Cordes
@PeterCordes Skylake als P6-basierten Chip zu bezeichnen, ist zu weit. 1) Die FPU wurde während der Sandy Bridge-Ära (fast) komplett neu gestaltet, so dass die alte P6-FPU bis heute im Grunde genommen weg ist. 2) Die x86-zu-UOP-Dekodierung hatte während der Core2-Ära eine kritische Modifikation: Während frühere Entwürfe Rechen- und Speicherbefehle als separate Uops dekodierten, haben die Core2 + -Chips Uops, die aus einem Rechenbefehl und einem Speicheroperator bestehen. Dies führte zu einer erheblich höheren Leistung und Energieeffizienz auf Kosten eines komplexeren Designs und einer möglicherweise niedrigeren Spitzenfrequenz.
Shodanshok
1

Wir haben die Beobachtung, dass 99,9% aller Gleitkommaoperationen NaNs betreffen, was zumindest sehr ungewöhnlich ist (zuerst von Peter Cordes gefunden). Wir haben ein weiteres Experiment von usr, bei dem festgestellt wurde, dass durch Entfernen der Teilungsanweisungen der Zeitunterschied fast vollständig verschwindet.

Tatsache ist jedoch, dass die NaNs nur erzeugt werden, weil die allererste Division 0,0 / 0,0 berechnet, was das anfängliche NaN ergibt. Wenn die Teilungen nicht durchgeführt werden, ist das Ergebnis immer 0.0 und wir berechnen immer 0.0 * temp -> 0.0, 0.0 + temp -> temp, temp - temp = 0.0. Das Entfernen der Teilung entfernte also nicht nur die Teilungen, sondern auch die NaNs. Ich würde erwarten, dass die NaNs tatsächlich das Problem sind und dass eine Implementierung die NaNs sehr langsam handhabt, während die andere das Problem nicht hat.

Es lohnt sich, die Schleife bei i = 1 zu starten und erneut zu messen. Die vier Operationen ergeben * temp, + temp, / temp, - temp addieren effektiv (1 - temp), sodass wir für die meisten Operationen keine ungewöhnlichen Zahlen (0, unendlich, NaN) haben.

Das einzige Problem könnte sein, dass die Division immer ein ganzzahliges Ergebnis liefert und einige Divisionsimplementierungen Verknüpfungen haben, wenn das richtige Ergebnis nicht viele Bits verwendet. Zum Beispiel ergibt das Teilen von 310.0 / 31.0 10.0 als die ersten vier Bits mit einem Rest von 0.0, und einige Implementierungen können die Auswertung der verbleibenden etwa 50 Bits beenden, während andere dies nicht können. Wenn es einen signifikanten Unterschied gibt, würde das Starten der Schleife mit result = 1.0 / 3.0 einen Unterschied machen.

gnasher729
quelle
-2

Es kann mehrere Gründe geben, warum dies in 64-Bit auf Ihrem Computer schneller ausgeführt wird. Der Grund, warum ich gefragt habe, welche CPU Sie verwenden, war, dass AMD und Intel bei der ersten Veröffentlichung von 64-Bit-CPUs unterschiedliche Mechanismen für den Umgang mit 64-Bit-Code hatten.

Prozessorarchitektur:

Intels CPU-Architektur war rein 64-Bit. Um 32-Bit-Code auszuführen, mussten die 32-Bit-Befehle vor der Ausführung (innerhalb der CPU) in 64-Bit-Befehle konvertiert werden.

Die CPU-Architektur von AMD sollte 64-Bit direkt auf der 32-Bit-Architektur aufbauen. Das heißt, es war im Wesentlichen eine 32-Bit-Architektur mit 64-Bit-Erweiterungen - es gab keinen Codekonvertierungsprozess.

Dies war offensichtlich vor ein paar Jahren, daher habe ich keine Ahnung, ob / wie sich die Technologie geändert hat, aber im Wesentlichen würde man erwarten, dass 64-Bit-Code auf einem 64-Bit-Computer eine bessere Leistung erbringt, da die CPU mit der doppelten Menge an arbeiten kann Bits pro Befehl.

.NET JIT

Es wird argumentiert, dass .NET (und andere verwaltete Sprachen wie Java) Sprachen wie C ++ übertreffen können, da der JIT-Compiler Ihren Code entsprechend Ihrer Prozessorarchitektur optimieren kann. In dieser Hinsicht stellen Sie möglicherweise fest, dass der JIT-Compiler eine 64-Bit-Architektur verwendet, die möglicherweise nicht verfügbar war oder bei der Ausführung in 32-Bit eine Problemumgehung erfordert.

Hinweis:

Haben Sie überlegt, anstelle von DoubleWrapper die Nullable<double>Syntax oder die Kurzschrift zu verwenden: double?- Es würde mich interessieren, ob sich dies auf Ihre Tests auswirkt.

Anmerkung 2: Einige Leute scheinen meine Kommentare zur 64-Bit-Architektur mit IA-64 zu verbinden. Zur Verdeutlichung bezieht sich in meiner Antwort 64 Bit auf x86-64 und 32 Bit auf x86-32. Nichts hier verwies auf IA-64!

Matthew Layton
quelle
4
OK, warum ist es 26x schneller? Kann dies in der Antwort nicht finden.
usr
2
Ich vermute, es sind die Jitter-Unterschiede, aber nicht mehr als zu raten.
Jon Hanna
2
@seriesOne: Ich denke, MSalters versucht zu sagen, dass Sie IA-64 mit x86-64 verwechseln. (Intel verwendet in seinen Handbüchern auch IA-32e für x86-64). Alle Desktop-CPUs sind x86-64. Der Itanic ist vor ein paar Jahren gesunken, und ich glaube, er wurde hauptsächlich auf Servern und nicht auf Workstations verwendet. Core2 (die erste CPU der P6-Familie, die den x86-64-Langmodus unterstützt) weist im 64-Bit-Modus tatsächlich einige Einschränkungen auf. zB UOP-Makrofusion funktioniert nur im 32-Bit-Modus. Intel und AMD haben dasselbe getan: Sie haben ihre 32-Bit-Designs auf 64-Bit erweitert.
Peter Cordes
1
@ PeterCordes wo habe ich IA-64 erwähnt? Mir ist bewusst, dass Itanium-CPUs ein völlig anderes Design und einen anderen Befehlssatz hatten. frühe Modelle, die als EPIC oder Explicitly Parallel Instruction Computing gekennzeichnet sind. Ich denke, MSalters verschmilzt 64bit und IA-64. Meine Antwort gilt für die x86-64-Architektur - es gab nichts, was auf die Itanium-CPU-Familie Bezug nahm
Matthew Layton
2
@ series0ne: Ok, dann ist dein Absatz über Intel-CPUs, die "rein 64-Bit" sind, völliger Unsinn. Ich nahm an, Sie dachten an IA-64, weil Sie sich dann nicht völlig geirrt hätten. Es gab nie einen zusätzlichen Übersetzungsschritt zum Ausführen von 32-Bit-Code. Die x86-> uop-Decoder haben nur zwei ähnliche Modi: x86 und x86-64. Intel baute 64-Bit-P4 auf P4. 64-Bit-Core2 brachte viele andere architektonische Verbesserungen gegenüber Core und Pentium M mit sich, aber Dinge wie Makrofusion, die nur im 32-Bit-Modus funktionieren, zeigen, dass 64-Bit angeschraubt war. (ziemlich früh im Designprozess, aber immer noch.)
Peter Cordes