2016-04-14 5 views
-1

Theoretisch werden die Kosten der Doppelwort-Addition/Subtraktion zweimal aus einem einzelnen Wort genommen. In ähnlicher Weise wird das Kostenverhältnis der Einwort-Multiplikation zur Addition als 3 angenommen. Ich habe das folgende C-Programm unter Verwendung von GCC unter Ubuntu LTS 14.04 geschrieben, um die Anzahl der Taktzyklen auf meinem Rechner, Intel Sandy Bridge Corei5-2410M, zu überprüfen. Obwohl das Programm die meiste Zeit 6 Taktzyklen für die 128-Bit-Addition zurückgibt, habe ich den besten Fall genommen. Ich kompiliert den Befehl (gcc -o ow O3 cost.c) und das Ergebnis unterDie Verwendung von rdtsc() in meinem Programm, um die Anzahl der Taktzyklen für Einzel- und Doppelwort-Operationen zu erhalten?

32-bit Add: Clock cycles = 1 64-bit Add: Clock cycles = 1 64-bit Mult: Clock cycles = 2 128-bit Add: Clock cycles = 5 

gegeben wird Das Programm ist wie folgt:

#define n 500 
#define counter 50000 

typedef uint64_t utype64; 
typedef int64_t type64; 
typedef __int128 type128; 

__inline__ utype64 rdtsc() { 
     uint32_t lo, hi; 
     __asm__ __volatile__ ("xorl %%eax,%%eax \n  cpuid"::: "%rax", "%rbx", "%rcx", "%rdx"); 
     __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); 
     return (utype64)hi << 32 | lo; 
} 

int main(){ 
    utype64 start, end; 
    type64 a[n], b[n], c[n]; 
    type128 d[n], e[n], f[n]; 
    int g[n], h[n]; 
    unsigned short i, j; 
    srand(time(NULL)); 
    for(i=0;i<n;i++){ g[i]=rand(); h[i]=rand(); b[i]=(rand()+2294967295); e[i]=(type128)(rand()+2294967295)*(rand()+2294967295);} 
    for(j=0;j<counter;j++){ 
     start=rdtsc(); 
     for(i=0;i<n;i++){ a[i]=(type64)g[i]+h[i]; } 
     end=rdtsc(); 
     if((j+1)%5000 == 0) 
      printf("%lu-bit Add: Clock cycles = %lu \t", sizeof(g[0])*8, (end-start)/n); 

     start=rdtsc(); 
     for(i=0;i<n;i++){ c[i]=a[i]+b[i]; } 
     end=rdtsc(); 
     if((j+1)%5000 == 0) 
      printf("%lu-bit Add: Clock cycles = %lu \t", sizeof(a[0])*8, (end-start)/n); 

     start=rdtsc(); 
     for(i=0;i<n;i++){ d[i]=(type128)c[i]*b[i]; } 
     end=rdtsc(); 
     if((j+1)%5000 == 0) 
      printf("%lu-bit Mult: Clock cycles = %lu \t", sizeof(c[0])*8, (end-start)/n); 

     start=rdtsc(); 
     for(i=0;i<n;i++){ f[i]=d[i]+e[i]; } 
     end=rdtsc(); 
     if((j+1)%5000 == 0){ 
      printf("%lu-bit Add: Clock cycles = %lu \n", sizeof(d[0])*8, (end-start)/n); 
     printf("f[%hu]= %ld %ld \n\n", i-7, (type64)(f[i-7]>>64), (type64)(f[i-7]));} 
    } 

return 0; 
} 

Es gibt zwei Dinge, in der Folge, dass belästigt mich.

1) Kann die Anzahl der Taktzyklen für (64-Bit-) Multiplikation 2 werden?

2) Warum beträgt die Anzahl der Taktzyklen für die Doppelwort-Addition mehr als das Zweifache der Einzelwort-Addition?

Ich bin hauptsächlich besorgt für den Fall (2). Jetzt stellt sich die Frage, dass es wegen meiner Programmlogik ist? Oder liegt es an der GCC-Compiler-Optimierung?

+1

** Wir ** wissen, dass Fragen beginnend mit "... wir wissen ..." meistens mit einer falschen Voraussetzung beginnen. Dein ist keine Ausnahme. – Olaf

+0

Sie sollten Pipelining in Betracht ziehen. Ein guter Ort, um über das Timing des x86-Befehls zu lesen, sind die Befehlstabellen von Agner Fog. – EOF

Antwort

2

In der Theorie wissen wir, dass die Doppelwort Addition/Subtraktion 2 mal eines einzelnen Wortes dauert.

Nein, tun wir nicht.

In ähnlicher Weise wird das Kostenverhältnis der Einwort-Multiplikation zur Addition als 3 angenommen wegen des schnellen ganzzahligen Multiplikators der CPU.

Nein, ist es nicht.

Sie messen keine Anweisungen. Sie messen Aussagen in Ihrem Programm. Welche Beziehung zu den Anweisungen, die Ihr Compiler ausgibt, haben kann oder nicht. Mein Compiler zum Beispiel, nach dem Fixieren des Codes, so dass es kompiliert, einige der Schleifen vektorisiert. Hinzufügen mehrerer Werte pro Anweisung Die erste Schleife selbst ist immer noch 23 Anweisungen lang und wird immer noch als 1 Zyklus von Ihrem Code gemeldet.

Moderne (wie in den letzten 25 Jahren) CPUs führen keine Anweisung gleichzeitig aus. Sie haben mehrere Anweisungen gleichzeitig im Flug und können sie außer Betrieb ausführen.

Dann haben Sie Speicherzugriffe. Auf Ihrer CPU gibt es keine Anweisungen, die einen Wert aus dem Speicher nehmen können, fügen Sie einen anderen Wert aus dem Speicher hinzu und speichern Sie ihn dann an einem dritten Speicherort. Daher müssen bereits mehrere Anweisungen ausgeführt werden. Darüber hinaus kosten Speicherzugriffe viel mehr als arithmetische Anweisungen, dass alles, was Speicher berührt (es sei denn, es trifft die ganze Zeit auf L1-Cache) von der Speicherzugriffszeit dominiert wird.

Darüber hinaus gibt RDTSC möglicherweise nicht einmal die tatsächliche Anzahl der Zyklen zurück. Einige CPUs haben variable Taktraten, halten aber trotzdem TSC in der gleichen Geschwindigkeit, unabhängig davon, wie schnell oder langsam die CPU tatsächlich läuft, da TSC vom Betriebssystem zur Zeitmessung verwendet wird. Andere nicht.

Sie messen also nicht, was Sie zu messen glauben und wer auch immer Ihnen diese Dinge gesagt hat, war entweder zu stark vereinfachend oder hat seit zwei Jahrzehnten keine CPU-Dokumentation mehr gesehen.

+0

In der Literatur/Theorie wird das Verhältnis von Multiplikation zu Addition im schlimmsten Fall 3 genommen. – user110219