Frage

Ich war Zeuge der folgende seltsame Verhalten. Ich habe zwei Funktionen, die gleich fast tun - sie messen die Anzahl der Zyklen es eine bestimmte Operation zu tun nimmt. In einer Funktion, die innerhalb der Schleife inkrementiert I eine Variable; in dem anderen nichts passiert. Die Variablen sind flüchtig, damit sie nicht weg optimiert werden. Dies sind die Funktionen:

unsigned int _osm_iterations=5000;

double osm_operation_time(){
    // volatile is used so that j will not be optimized, and ++ operation
    // will be done in each loop
    volatile unsigned int j=0;
    volatile unsigned int i;
    tsc_counter_t start_t, end_t;
    start_t = tsc_readCycles_C();
    for (i=0; i<_osm_iterations; i++){
       ++j;
    }
    end_t = tsc_readCycles_C();
    if (tsc_C2CI(start_t) ==0 || tsc_C2CI(end_t) ==0 || tsc_C2CI(start_t) >= tsc_C2CI(end_t))
         return -1;
    return (tsc_C2CI(end_t)-tsc_C2CI(start_t))/_osm_iterations;
}

double osm_empty_time(){
    volatile unsigned int i;
    volatile unsigned int j=0;
    tsc_counter_t start_t, end_t;
    start_t = tsc_readCycles_C();
    for (i=0; i<_osm_iterations; i++){
        ;
    }
    end_t = tsc_readCycles_C();
    if (tsc_C2CI(start_t) ==0 || tsc_C2CI(end_t) ==0 || tsc_C2CI(start_t) >= tsc_C2CI(end_t))
        return -1;
    return (tsc_C2CI(end_t)-tsc_C2CI(start_t))/_osm_iterations;
}

Es gibt einige Nicht-Standard-Funktionen gibt, aber ich bin sicher, dass Sie verwalten.

Die Sache ist, die erste Funktion zurück 4 , während die zweite Funktion (die angeblich tut weniger) kehrt 6 , obwohl der zweite offensichtlich tut kleiner als der erste ein.

Hat das irgendeinen Sinn zu jemand machen?

Eigentlich habe ich die erste Funktion, so konnte ich die Schleife Overhead für meine Messung der zweiten reduzieren. Sie haben keine Ahnung, wie das zu tun (da diese Methode nicht wirklich schneiden)?

Ich bin auf Ubuntu (64 bit glaube ich).

Vielen Dank.

War es hilfreich?

Lösung

kann ich ein paar Dinge hier sehen. Eine davon ist, dass der Code für die beiden Schleifen sieht identisch. Zweitens erkennen wird der Compiler wahrscheinlich, dass die Variable i und die Variable j immer den gleichen Wert haben und weg von ihnen zu optimieren. Sie sollten bei den generierten Assembly schauen und sehen, was wirklich los ist.

Eine andere Theorie ist, dass die Änderung auf den Innenkörper der Schleife die cachability des Codes betroffen hat - diese es über Cache-Zeilen verschoben haben könnte oder eine andere Sache.

Da der Code so trivial ist, Sie finden es schwierig, einen accuate Timing-Wert zu erhalten, auch wenn Sie 5000 Iterationen tun, können Sie feststellen, dass die Zeit in der Marge ist für Fehler für den Timing-Code Sie verwenden. Ein moderner Computer kann wahrscheinlich laufen, dass in weit weniger als eine Millisekunde - vielleicht sollten Sie die Anzahl der Iterationen erhöhen

Um die generierte Assembly in gcc zu sehen, geben Sie die -S-Compiler-Option :

  

F: Wie kann ich an dem Assembler-Code peek   erzeugt durch GCC?

     

F: Wie kann ich eine Datei erstellen, wo ich kann   siehe den C Code und seine Montage   Übersetzung zusammen?

     

A: Verwenden Sie die -S (Anmerkung: Kapital S) Schalter   zu GCC, und es wird die Montage emittieren   Code in eine Datei mit einer Erweiterung .s.   Zum Beispiel den folgenden Befehl ein:

     

gcc -O2 ES -c foo.c

     

wird die erzeugte Assembler-Code verlassen   auf die Datei foo.s.

     

Wenn Sie den C-Code sehen wollen zusammen   wobei die Anordnung es umgewandelt wurde,   Verwenden Sie eine Befehlszeile wie folgt:

     

gcc -c -g -Wa, -a, -ad [andere GCC   Optionen] foo.c> foo.lst

     

, die ausgegeben die kombinierte   C / Assembler-Liste in die Datei   foo.lst.

Andere Tipps

Diese Art der Sache hängt stark von Compiler-Optimierungen und auf Timer-Auflösung. Die Ergebnisse, die Sie zur Verfügung stellen (4 und 6) sind so eine Art niedrig, unabhängig von der Einheit. Um Benchmark richtig sollten Sie diese beiden Funktionen in einer Schleife wickeln, die sie ein paar tausend Mal ausführt.

Es ist manchmal schwierig, diese Art der Sache zu erraten, vor allem aufgrund der geringen Anzahl von Iterationen. Eine Sache, die, wenn auch passiert sein könnte, ist die Zunahme auf einer freie Integer-Ausführungseinheit wurde die Ausführung könnte, etwas geringen Grad an Parallelität zu gewinnen, da sie keine dep auf dem Wert von i haben.

Da Sie erwähnt war das 64-Bit-Betriebssystem, ist es fast sicher ist, alle diese Werte in den Registern sind, da es mehrere Register in der x86_64-Architektur ist. Other than that, ich würde viel mehr Iterationen durchführen sagen, und sehen, wie stabil die Ergebnisse sind.

Wenn Sie versuchen, wirklich den Betrieb eines Stück Code ("j++;" in diesem Fall) zu testen, sind Sie tatsächlich besser dran Sie folgendermaßen vorgehen:

1 / Do it in zwei separaten ausführbaren Dateien, da die Möglichkeit besteht, dass Position innerhalb der ausführbaren Datei den Code beeinflussen kann.

2 / Stellen Sie sicher, dass die CPU-Zeit verwenden, anstatt die verstrichene Zeit (ich bin nicht sicher, was "tsc_readCycles_C()" gibt Ihnen). Dies ist verirrten ergibt sich aus einer CPU zu vermeiden, mit anderen Aufgaben belastet.

3 / ausschalten Compiler-Optimierung (zum Beispiel "gcc -O0") gcc, um sicherzustellen, nicht in jedem fancy stuff setzen, die die Ergebnisse verfälschen wahrscheinlich ist.

4 / Sie müssen nicht über volatile kümmern, wenn Sie das tatsächliche Ergebnis, wie das Anbringen verwenden:

printf ("%d\n",j);

nach der Schleife oder:

FILE *fx = fopen ("/dev/null","w");
fprintf (fx, "%d\n", j);
fclose (fx);

, wenn Sie nicht überhaupt ausgegeben werden sollen. Ich kann mich nicht erinnern, ob flüchtig war ein Vorschlag für den Compiler oder erzwungen.

5 / Iterations von 5000 scheint ein wenig auf der unteren Seite, wo „Rauschen“ die Messwerte beeinflussen könnte. Vielleicht wäre ein höherer Wert besser sein. Dies kann kein Problem sein, wenn Sie ein größeres Stück Code sind Timing und Sie haben gerade "j++;" als Platzhalter enthalten.

Wenn ich Tests ähnlich wie diese ausgeführt wird, ich normalerweise:

  1. Sicherstellen, dass die Zeiten, in zumindest Sekunden gemessen werden, vorzugsweise (small) zig Sekunden.
  2. Haben Sie einen einzigen Durchlauf des Programms die erste Funktion aufrufen, dann die zweite, dann die erste wieder, dann die zweite wieder, und so weiter, nur um zu sehen, ob es seltsam Cache Warm-up Probleme sind.
  3. Führen Sie das Programm mehrmals, um zu sehen, wie stabil das Timing über Läufe ist.

ich ratlos bin immer noch Ihre beobachteten Ergebnisse zu erklären, aber wenn Sie sicher sind, Sie haben Ihre Funktionen korrekt identifiziert (nicht selbstverständlich der Fall gegeben, dass es copy'n'paste Fehler früher war, für Beispiel), dann an dem Assembler Ausgang sucht, ist die wichtigste Option links.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top