wake-up-neo.com

Ist die Inline-Assemblersprache langsamer als nativer C ++ - Code?

Ich habe versucht, die Leistung der Inline-Assemblersprache und des C++ - Codes zu vergleichen. Deshalb habe ich eine Funktion geschrieben, die 100000 Mal zwei Arrays der Größe 2000 hinzufügt. Hier ist der Code:

#define TIMES 100000
void calcuC(int *x,int *y,int length)
{
    for(int i = 0; i < TIMES; i++)
    {
        for(int j = 0; j < length; j++)
            x[j] += y[j];
    }
}


void calcuAsm(int *x,int *y,int lengthOfArray)
{
    __asm
    {
        mov edi,TIMES
        start:
        mov esi,0
        mov ecx,lengthOfArray
        label:
        mov edx,x
        Push edx
        mov eax,DWORD PTR [edx + esi*4]
        mov edx,y
        mov ebx,DWORD PTR [edx + esi*4]
        add eax,ebx
        pop edx
        mov [edx + esi*4],eax
        inc esi
        loop label
        dec edi
        cmp edi,0
        jnz start
    };
}

Hier ist main():

int main() {
    bool errorOccured = false;
    setbuf(stdout,NULL);
    int *xC,*xAsm,*yC,*yAsm;
    xC = new int[2000];
    xAsm = new int[2000];
    yC = new int[2000];
    yAsm = new int[2000];
    for(int i = 0; i < 2000; i++)
    {
        xC[i] = 0;
        xAsm[i] = 0;
        yC[i] = i;
        yAsm[i] = i;
    }
    time_t start = clock();
    calcuC(xC,yC,2000);

    //    calcuAsm(xAsm,yAsm,2000);
    //    for(int i = 0; i < 2000; i++)
    //    {
    //        if(xC[i] != xAsm[i])
    //        {
    //            cout<<"xC["<<i<<"]="<<xC[i]<<" "<<"xAsm["<<i<<"]="<<xAsm[i]<<endl;
    //            errorOccured = true;
    //            break;
    //        }
    //    }
    //    if(errorOccured)
    //        cout<<"Error occurs!"<<endl;
    //    else
    //        cout<<"Works fine!"<<endl;

    time_t end = clock();

    //    cout<<"time = "<<(float)(end - start) / CLOCKS_PER_SEC<<"\n";

    cout<<"time = "<<end - start<<endl;
    return 0;
}

Dann führe ich das Programm fünfmal aus, um die Zyklen des Prozessors zu erhalten, die als Zeit angesehen werden könnten. Jedes Mal rufe ich nur eine der oben genannten Funktionen auf.

Und hier kommt das Ergebnis.

Funktion von Assembly version:

Debug   Release
---------------
732        668
733        680
659        672
667        675
684        694
Average:   677

Funktion der C++ Version:

Debug     Release
-----------------
1068      168
 999      166
1072      231
1002      166
1114      183
Average:  182

Der C++ - Code im Release-Modus ist fast 3,7-mal schneller als der Assembly-Code. Warum?

Ich vermute, dass der von mir geschriebene Assembly-Code nicht so effektiv ist wie der von GCC generierte. Für einen gewöhnlichen Programmierer wie mich ist es schwierig, Code schneller zu schreiben als für seinen Gegner, der von einem Compiler generiert wird. Bedeutet das, dass ich der Leistung der von meinen Händen geschriebenen Assemblersprache nicht vertrauen sollte, mich auf C++ konzentrieren und die Assemblersprache vergessen sollte?

172
user957121

Ihr Assembly-Code ist nicht optimal und kann verbessert werden:

  • Sie verschieben und platzieren ein Register ( [~ # ~] edx [~ # ~] ) in Ihrer inneren Schleife. Dies sollte aus der Schleife verschoben werden.
  • Sie laden die Array-Zeiger in jeder Iteration der Schleife neu. Dies sollte aus der Schleife verschoben werden.
  • Sie verwenden die Anweisung loop, die auf den meisten modernen CPUs für ihre Langsamkeit bekannt ist (möglicherweise das Ergebnis der Verwendung eines alten Assembly-Buches *)
  • Sie können das manuelle Abrollen der Schleife nicht nutzen.
  • Sie verwenden keine verfügbaren [~ # ~] simd [~ # ~] Anweisungen.

Wenn Sie also Ihre Fähigkeiten in Bezug auf Assembler nicht erheblich verbessern, ist es nicht sinnvoll, Assembler-Code für die Leistung zu schreiben.

* Natürlich weiß ich nicht, ob Sie wirklich die Anweisung loop aus einem alten Versammlungsbuch erhalten haben. Aber Sie sehen es fast nie im Code der realen Welt, da jeder Compiler da draußen klug genug ist, um loop nicht auszugeben, Sie sehen es nur in IMHO schlechten und veralteten Büchern.

189
Gunther Piez

Noch vor dem Einstieg in Assembly gibt es Codetransformationen, die auf einer höheren Ebene vorhanden sind.

static int const TIMES = 100000;

void calcuC(int *x, int *y, int length) {
  for (int i = 0; i < TIMES; i++) {
    for (int j = 0; j < length; j++) {
      x[j] += y[j];
    }
  }
}

kann umgewandelt werden in via Loop Rotation :

static int const TIMES = 100000;

void calcuC(int *x, int *y, int length) {
    for (int j = 0; j < length; ++j) {
      for (int i = 0; i < TIMES; ++i) {
        x[j] += y[j];
      }
    }
}

das ist viel besser, was die Speicherlokalität angeht.

Dies könnte weiter optimiert werden, indem a += b X-mal ist gleichbedeutend mit a += X * b so bekommen wir:

static int const TIMES = 100000;

void calcuC(int *x, int *y, int length) {
    for (int j = 0; j < length; ++j) {
      x[j] += TIMES * y[j];
    }
}

mein bevorzugter Optimierer (LLVM) führt diese Transformation jedoch nicht durch.

[edit] Ich fand, dass die Transformation durchgeführt wird, wenn wir das restrict-Qualifikationsmerkmal zu x und y. In der Tat, ohne diese Einschränkung, x[j] und y[j] könnte ein Alias ​​an derselben Stelle sein, wodurch diese Umwandlung fehlerhaft wird. [Bearbeitung beenden]

Wie auch immer, das ist meiner Meinung nach die optimierte C-Version. Schon ist es viel einfacher. Hier ist mein Crack bei ASM (ich lasse Clang es generieren, ich bin unbrauchbar darin):

calcuAsm:                               # @calcuAsm
.Ltmp0:
    .cfi_startproc
# BB#0:
    testl   %edx, %edx
    jle .LBB0_2
    .align  16, 0x90
.LBB0_1:                                # %.lr.ph
                                        # =>This Inner Loop Header: Depth=1
    imull   $100000, (%rsi), %eax   # imm = 0x186A0
    addl    %eax, (%rdi)
    addq    $4, %rsi
    addq    $4, %rdi
    decl    %edx
    jne .LBB0_1
.LBB0_2:                                # %._crit_Edge
    ret
.Ltmp1:
    .size   calcuAsm, .Ltmp1-calcuAsm
.Ltmp2:
    .cfi_endproc

Ich fürchte, ich verstehe nicht, woher all diese Anweisungen kommen, aber Sie können immer Spaß haben und versuchen, zu sehen, wie sie verglichen werden ... aber ich würde trotzdem die optimierte C-Version anstelle der Assembly-Version im Code verwenden. viel tragbarer.

59
Matthieu M.

Kurze Antwort: ja.

Lange Antwort: ja, es sei denn, Sie wissen wirklich, was Sie tun und haben einen Grund, dies zu tun.

41

Ich habe meinen asm code gefixt:

  __asm
{   
    mov ebx,TIMES
 start:
    mov ecx,lengthOfArray
    mov esi,x
    shr ecx,1
    mov edi,y
label:
    movq mm0,QWORD PTR[esi]
    paddd mm0,QWORD PTR[edi]
    add edi,8
    movq QWORD PTR[esi],mm0
    add esi,8
    dec ecx 
    jnz label
    dec ebx
    jnz start
};

Ergebnisse für Release-Version:

 Function of Assembly version: 81
 Function of C++ version: 161

Der Assembly-Code im Release-Modus ist fast 2-mal schneller als der in C++.

33
sasha

Bedeutet das, dass ich der Leistung meiner Assemblersprache nicht trauen sollte?

Ja, genau das bedeutet es, und es gilt für jede Sprache. Wenn Sie nicht wissen, wie effizienter Code in der Sprache X geschrieben wird, sollten Sie Ihrer Fähigkeit, effizienten Code in X zu schreiben, nicht vertrauen. Wenn Sie also effizienten Code wünschen, sollten Sie eine andere Sprache verwenden.

Die Montage reagiert besonders empfindlich darauf, denn was Sie sehen, ist das, was Sie bekommen. Sie schreiben die spezifischen Anweisungen, die die CPU ausführen soll. Bei Hochsprachen gibt es einen Compiler, der Ihren Code transformieren und viele Ineffizienzen beseitigen kann. Mit Assembly sind Sie auf sich allein gestellt.

24
jalf

Der einzige Grund für die heutige Verwendung der Assemblersprache ist die Verwendung einiger Funktionen, auf die die Sprache keinen Zugriff hat.

Dies gilt für:

  • Kernel-Programmierung, die auf bestimmte Hardwarefunktionen wie die MMU zugreifen muss
  • Hochleistungsprogrammierung, die sehr spezifische Vektor- oder Multimedia-Anweisungen verwendet, die von Ihrem Compiler nicht unterstützt werden.

Aktuelle Compiler sind jedoch ziemlich schlau. Sie können sogar zwei separate Anweisungen wie d = a / b; r = a % b; Durch eine einzige Anweisung ersetzen, die die Division und den Rest in einem Zug berechnet, wenn sie verfügbar sind, auch wenn C keinen solchen Operator hat.

21
fortran

Es ist richtig, dass ein moderner Compiler bei der Codeoptimierung hervorragende Arbeit leistet, aber ich möchte Sie dennoch ermutigen, weiterhin Assembly zu lernen.

Zuallererst sind Sie klar nicht eingeschüchtert, das ist ein großartiges Plus - als nächstes sind Sie auf dem richtigen Weg durch Profiling, um Ihre Geschwindigkeitsannahmen zu validieren oder zu verwerfen, du fragst nach Input von erfahrenen Leuten und du hast das größte Optimierungswerkzeug, das der Menschheit bekannt ist: ein Gehirn.

Mit zunehmender Erfahrung lernen Sie, wann und wo Sie es verwenden müssen (normalerweise die engsten, innersten Schleifen in Ihrem Code, nachdem Sie auf algorithmischer Ebene gründlich optimiert haben).

Als Inspiration empfehle ich Ihnen, die Artikel von Michael Abrash nachzuschlagen (wenn Sie nichts von ihm gehört haben, ist er ein Optimierungsguru; er hat sogar mit John Carmack bei der Optimierung des Quake-Software-Renderers zusammengearbeitet!). )

"Den schnellsten Code gibt es nicht" - Michael Abrash

19
user1222021

Ich habe den asm code geändert:

 __asm
{ 
    mov ebx,TIMES
 start:
    mov ecx,lengthOfArray
    mov esi,x
    shr ecx,2
    mov edi,y
label:
    mov eax,DWORD PTR [esi]
    add eax,DWORD PTR [edi]
    add edi,4   
    dec ecx 
    mov DWORD PTR [esi],eax
    add esi,4
    test ecx,ecx
    jnz label
    dec ebx
    test ebx,ebx
    jnz start
};

Ergebnisse für Release-Version:

 Function of Assembly version: 41
 Function of C++ version: 161

Der Assembly-Code im Release-Modus ist fast viermal schneller als der in C++. IMHo, die Geschwindigkeit des Assembler-Codes hängt vom Programmer ab

14
sasha

es ist ein sehr interessantes Thema!
Ich habe die MMX um SSE in Sashas Code geändert
Hier sind meine Ergebnisse:

Function of C++ version:      315
Function of Assembly(simply): 312
Function of Assembly  (MMX):  136
Function of Assembly  (SSE):  62

Der Assembly-Code mit SSE ist fünfmal schneller als C++

12
salaoshi

Die meisten Hochsprachen-Compiler sind sehr optimiert und wissen, was sie tun. Sie können versuchen, den Disassemblierungscode zu sichern und ihn mit Ihrer nativen Assembly zu vergleichen. Ich glaube, Sie werden einige nette Tricks sehen, die Ihr Compiler verwendet.

Nur zum Beispiel, auch wenn ich nicht mehr sicher bin, ob es richtig ist :):

Tun:

mov eax,0

kosten mehr zyklen als

xor eax,eax

was das gleiche tut.

Der Compiler kennt alle diese Tricks und verwendet sie.

12
Nuno_147

Der Compiler hat dich geschlagen. Ich werde es versuchen, aber ich werde keine Garantien geben. Ich gehe davon aus, dass die "Multiplikation" mit TIMES einen relevanteren Leistungstest darstellen soll, dass y und x auf 16 ausgerichtet sind und dass length a ist Nicht-Null-Vielfaches von 4. Das ist wahrscheinlich sowieso alles wahr.

  mov ecx,length
  lea esi,[y+4*ecx]
  lea edi,[x+4*ecx]
  neg ecx
loop:
  movdqa xmm0,[esi+4*ecx]
  paddd xmm0,[edi+4*ecx]
  movdqa [edi+4*ecx],xmm0
  add ecx,4
  jnz loop

Wie gesagt, ich mache keine Garantien. Aber ich bin überrascht, ob es viel schneller geht - der Engpass hier ist der Speicherdurchsatz, auch wenn alles ein L1-Hit ist.

10
harold

Nur blind den exakt gleichen Algorithmus, Anweisung für Anweisung, in Assembly zu implementieren ist garantiert langsamer als das, was der Compiler tun kann.

Dies liegt daran, dass selbst die kleinste Optimierung, die der Compiler durchführt, besser ist als Ihr starrer Code, ohne dass eine Optimierung erfolgt.

Natürlich ist es möglich, den Compiler zu übertreffen, besonders wenn es sich um einen kleinen, lokalisierten Teil des Codes handelt. 4-fache Geschwindigkeit, aber in diesem Fall müssen wir uns stark auf gute Kenntnisse der Hardware und zahlreiche, scheinbar kontraintuitive Tricks verlassen.

6
vsz

Als Compiler würde ich eine Schleife mit einer festen Größe für viele Ausführungsaufgaben ersetzen.

int a = 10;
for (int i = 0; i < 3; i += 1) {
    a = a + i;
}

wird herstellen

int a = 10;
a = a + 0;
a = a + 1;
a = a + 2;

und irgendwann wird es wissen, dass "a = a + 0;" ist nutzlos, daher wird diese Zeile entfernt. Hoffentlich ist jetzt etwas in Ihrem Kopf, das bereit ist, einige Optimierungsoptionen als Kommentar hinzuzufügen. All diese sehr effektiven Optimierungen beschleunigen die kompilierte Sprache.

5
Miah

Es ist genau das, was es bedeutet. Überlassen Sie die Mikrooptimierungen dem Compiler.

4
Luchian Grigore

Ich liebe dieses Beispiel, weil es eine wichtige Lektion über Low-Level-Code zeigt. Ja, Sie können Assembly schreiben, die so schnell ist wie Ihr C-Code. Dies ist tautologisch wahr, aber nicht unbedingt gemein irgendetwas. Klar jemand kann, sonst würde der Assembler die entsprechenden Optimierungen nicht kennen.

Ebenso gilt das gleiche Prinzip, wenn Sie die Hierarchie der Sprachabstraktion nach oben gehen. Ja, Sie können schreiben einen Parser in C, der so schnell ist wie ein flottes Perl-Skript, und viele Leute tun es. Das bedeutet jedoch nicht, dass Ihr Code schnell sein wird, da Sie C verwendet haben. In vielen Fällen führen die übergeordneten Sprachen Optimierungen durch, die Sie möglicherweise noch nie in Betracht gezogen haben.

4
tylerl

In vielen Fällen hängt die optimale Ausführung einer Aufgabe vom Kontext ab, in dem die Aufgabe ausgeführt wird. Wenn eine Routine in Assemblersprache geschrieben ist, kann die Reihenfolge der Anweisungen im Allgemeinen nicht kontextabhängig geändert werden. Betrachten Sie als einfaches Beispiel die folgende einfache Methode:

inline void set_port_high(void)
{
  (*((volatile unsigned char*)0x40001204) = 0xFF);
}

Ein Compiler für 32-Bit-Code ARM unter Berücksichtigung der obigen Bedingungen würde den Code wahrscheinlich wie folgt rendern:

ldr  r0,=0x40001204
mov  r1,#0
strb r1,[r0]
[a fourth Word somewhere holding the constant 0x40001204]

oder vielleicht

ldr  r0,=0x40001000  ; Some assemblers like to round pointer loads to multiples of 4096
mov  r1,#0
strb r1,[r0+0x204]
[a fourth Word somewhere holding the constant 0x40001000]

Das könnte im hand-assemblierten Code leicht optimiert werden, als entweder:

ldr  r0,=0x400011FF
strb r0,[r0+5]
[a third Word somewhere holding the constant 0x400011FF]

oder

mvn  r0,#0xC0       ; Load with 0x3FFFFFFF
add  r0,r0,#0x1200  ; Add 0x1200, yielding 0x400011FF
strb r0,[r0+5]

Beide von Hand zusammengestellten Ansätze würden 12 Byte Code-Speicherplatz anstelle von 16 Byte erfordern. Letzteres würde eine "Last" durch eine "Add" ersetzen, die auf einem ARM7-TDMI zwei Zyklen schneller ausgeführt würde. Wenn der Code in einem Kontext ausgeführt werden würde, in dem r0 nicht bekannt/egal ist, wären die Assembler-Versionen daher etwas besser als die kompilierte Version. Andererseits wird angenommen, dass der Compiler wusste, dass ein Register [z. r5] würde einen Wert halten, der innerhalb von 2047 Bytes der gewünschten Adresse 0x40001204 [z. 0x40001000] und wussten ferner, dass ein anderes Register [z. r7] würde einen Wert halten, dessen niedrige Bits 0xFF waren. In diesem Fall könnte ein Compiler die C-Version des Codes optimieren, um einfach:

strb r7,[r5+0x204]

Viel kürzer und schneller als selbst der handoptimierte Assembler-Code. Angenommen, set_port_high ist im Kontext aufgetreten:

int temp = function1();
set_port_high();
function2(temp); // Assume temp is not used after this

Gar nicht unplausibel beim Codieren für ein Embedded-System. Wenn set_port_high In Assemblycode geschrieben ist, müsste der Compiler r0 (der den Rückgabewert von function1 Enthält) an eine andere Stelle verschieben, bevor er den Assemblycode aufruft, und diesen Wert dann zurück nach r0 verschieben danach (da function2 seinen ersten Parameter in r0 erwartet), würde der "optimierte" Assembly-Code fünf Anweisungen benötigen. Selbst wenn der Compiler keine Register kannte, die die Adresse oder den zu speichernden Wert enthalten, würde seine Version mit vier Befehlen (die er anpassen könnte, um alle verfügbaren Register zu verwenden - nicht notwendigerweise r0 und r1) die "optimierte" Assembly schlagen -sprachige Version. Wenn der Compiler die erforderlichen Adressen und Daten in r5 und r7 hätte, wie zuvor beschrieben, würde function1 Diese Register nicht ändern, und daher könnte er set_port_high Durch einen einzelnen Befehl strb ersetzen --vier Anweisungen kleiner und schneller als der "handoptimierte" Assembly-Code.

Beachten Sie, dass handoptimierter Assembly-Code häufig einen Compiler übertreffen kann, wenn der Programmierer den genauen Programmfluss kennt, Compiler jedoch in Fällen glänzen, in denen ein Codeteil geschrieben wird, bevor sein Kontext bekannt ist, oder wenn ein Quelltextteil bekannt ist aus mehreren Kontexten aufgerufen [wenn set_port_high an fünfzig verschiedenen Stellen im Code verwendet wird, könnte der Compiler für jede der Stellen unabhängig entscheiden, wie er am besten erweitert werden soll].

Im Allgemeinen würde ich vorschlagen, dass die Assemblersprache die größten Leistungsverbesserungen in den Fällen erzielt, in denen jeder Codeteil aus einer sehr begrenzten Anzahl von Kontexten angesprochen werden kann und die Leistung an Stellen beeinträchtigt, an denen ein Teil davon ausgeführt wird Code kann aus vielen verschiedenen Kontexten angesprochen werden. Interessanterweise (und bequemerweise) sind die Fälle, in denen Assembly für die Leistung am vorteilhaftesten ist, häufig die, in denen der Code am einfachsten und am einfachsten zu lesen ist. Die Stellen, an denen der Assembler-Code zu einem lästigen Durcheinander wird, sind häufig die Stellen, an denen das Schreiben in Assembler den geringsten Leistungsvorteil bietet.

[Kleinere Anmerkung: Es gibt einige Stellen, an denen Assembly-Code verwendet werden kann, um ein hyperoptimiertes Durcheinander zu erzielen. Zum Beispiel ein Stück Code, den ich für ARM benötigt habe, um ein Wort aus RAM abzurufen und eine von ungefähr zwölf Routinen basierend auf den oberen sechs Bits auszuführen des Wertes (viele Werte sind derselben Routine zugeordnet) Ich glaube, ich habe diesen Code auf etwas optimiert, wie:

ldrh  r0,[r1],#2! ; Fetch with post-increment
ldrb  r1,[r8,r0 asr #10]
sub   pc,r8,r1,asl #2

Das Register r8 enthielt immer die Adresse der Hauptversandtabelle (innerhalb der Schleife, in der der Code 98% seiner Zeit verbrachte, wurde er nie für andere Zwecke verwendet). Alle 64 Einträge bezogen sich auf Adressen in den 256 Bytes davor. Da die primäre Schleife in den meisten Fällen ein zeitlich begrenztes Ausführungsintervall von etwa 60 Zyklen aufwies, war das Abrufen und Versenden mit neun Zyklen sehr wichtig, um dieses Ziel zu erreichen. Die Verwendung einer Tabelle mit 256 32-Bit-Adressen wäre einen Zyklus schneller gewesen, hätte jedoch 1 KB sehr wertvollen Werts RAM [Flash hätte mehr als einen Wartezustand hinzugefügt] verschlungen. 64 32 -bit-Adressen hätten das Hinzufügen eines Befehls zum Maskieren einiger Bits aus dem abgerufenen Wort erforderlich gemacht und hätten immer noch 192 Bytes mehr verschlungen als die Tabelle, die ich tatsächlich verwendet habe. Unter Verwendung der Tabelle mit 8-Bit-Offsets ergab sich ein sehr kompakter und schneller Code. Ich würde jedoch nicht erwarten, dass sich ein Compiler jemals etwas einfallen lässt, und ich würde auch nicht erwarten, dass ein Compiler ein Register "Vollzeit" für die Speicherung der Tabellenadresse verwendet.

Der obige Code wurde für die Ausführung als eigenständiges System entwickelt. Es konnte C-Code periodisch aufrufen, aber nur zu bestimmten Zeiten, wenn die Hardware, mit der es kommunizierte, alle 16 ms sicher für zwei Intervalle von ungefähr einer Millisekunde in einen "Ruhezustand" versetzt werden konnte.

3
supercat

C++ ist schneller, es sei denn, Sie verwenden Assembler mit vertieften Kenntnissen auf die richtige Weise.

Wenn ich in ASM programmiere, reorganisiere ich die Anweisungen manuell, damit die CPU, wenn logisch möglich, mehrere parallel ausführen kann. Ich verwende kaum RAM, wenn ich in ASM codiere, zum Beispiel: In ASM können mehr als 20000 Codezeilen vorkommen, und ich habe Push/Pop noch nie verwendet.

Sie könnten möglicherweise in die Mitte des Opcodes springen, um den Code und das Verhalten selbst zu ändern, ohne die mögliche Strafe des sich selbst ändernden Codes. Der Zugriff auf Register benötigt 1 Tick (manchmal 0,25 Ticks) der CPU. Der Zugriff auf RAM) kann Hunderte dauern.

Für mein letztes ASM-Abenteuer habe ich das RAM) nie benutzt, um eine Variable zu speichern (für Tausende von Zeilen ASM). ASM könnte möglicherweise unvorstellbar schneller sein als C++. Aber es hängt von vielen ab variable Faktoren wie:

1. I was writing my apps to run on the bare metal.
2. I was writing my own boot loader that was starting my programs in ASM so there was no OS management in the middle.

Ich lerne jetzt C # und C++, weil ich erkannt habe, dass es auf die Produktivität ankommt !! Sie könnten versuchen, die am schnellsten vorstellbaren Programme in der Freizeit allein mit reinem ASM zu erstellen. Aber um etwas zu produzieren, benutze eine Hochsprache.

Das letzte Programm, das ich programmiert habe, war zum Beispiel die Verwendung von JS und GLSL, und ich habe nie Leistungsprobleme bemerkt, auch wenn ich über JS gesprochen habe, das langsam ist. Dies liegt daran, dass das bloße Konzept der Programmierung der GPU für 3D die Geschwindigkeit der Sprache, in der die Befehle an die GPU gesendet werden, nahezu irrelevant macht.

Die Geschwindigkeit des Monteurs allein auf dem bloßen Metall ist unwiderlegbar. Könnte es in C++ noch langsamer sein? - Dies kann daran liegen, dass Sie Assembly-Code mit einem Compiler schreiben, der zunächst keinen Assembler verwendet.

Mein persönlicher Rat ist, niemals Assembly-Code zu schreiben, wenn Sie ihn vermeiden können, obwohl ich Assembly liebe.

2
user5921954

In letzter Zeit haben alle Geschwindigkeitsoptimierungen, die ich durchgeführt habe, gehirngeschädigten langsamen Code durch nur vernünftigen Code ersetzt. Da jedoch die Geschwindigkeit ausschlaggebend war und ich mich ernsthaft darum bemühte, etwas schnell zu machen, war das Ergebnis immer ein iterativer Prozess, bei dem jede Iteration mehr Einblick in das Problem gewährte und Wege zur Lösung des Problems mit weniger Operationen ermittelte. Die Endgeschwindigkeit hing immer davon ab, wie viel Einsicht ich in das Problem hatte. Wenn ich zu irgendeinem Zeitpunkt Assembly-Code oder C-Code verwendet hätte, der überoptimiert war, hätte der Prozess der Suche nach einer besseren Lösung gelitten, und das Endergebnis wäre langsamer.

2
gnasher729

Alle Antworten hier scheinen einen Aspekt auszuschließen: Manchmal schreiben wir keinen Code, um ein bestimmtes Ziel zu erreichen, sondern nur aus purem Spaß . Es mag nicht wirtschaftlich sein, die Zeit dafür zu investieren, aber es gibt wohl keine größere Befriedigung, als das schnellste vom Compiler optimierte Code-Snippet mit einer manuell gerollten asm-Alternative in Geschwindigkeit zu schlagen.

0
madoki