short, int und long

Schnelle objektorientierte, kompilierende Programmiersprache.
Nemo
Beiträge: 37
Registriert: Sa Mär 02, 2013 3:18 pm

Re: short, int und long

Beitrag von Nemo » Di Mär 26, 2013 4:47 pm

OK, danke für die schnelle Antwort.

Weil es gerade dazu passt:
Hier steht, dass man jedem eigenständigen Datensatz auch einen eigenen Typ geben soll.
z.B.

Code: Alles auswählen

struct Firstname
{
  char value[64];
};
Werden solche Hüllstrukturen auch vom Optimierer entfernt, oder sollte man die, wenn es auf Geschwindigkeit ankommt, zum Testen verwenden und für die endgültige Version wieder entfernen?

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: short, int und long

Beitrag von Xin » Di Mär 26, 2013 5:39 pm

Nemo hat geschrieben:Weil es gerade dazu passt:
Hier steht, dass man jedem eigenständigen Datensatz auch einen eigenen Typ geben soll.
z.B.

Code: Alles auswählen

struct Firstname
{
  char value[64];
};
Werden solche Hüllstrukturen auch vom Optimierer entfernt
Ich kann keine Garantie für alle Compiler übernehmen, aber Du kannst Dir die Wahrscheinlichkeit selbst ausdenken:

Code: Alles auswählen

struct Firstname fname;

char * result = fname.value;
Es geht ja hierbei um den Teil fname.value. Was passiert da, result soll also mit der Adresse von fname.value belegt werden. Wäre da keine Hülle drumrum, dann wäre fname die Adresse des 64 Byte großen Arrays und man könnte sich den Zugriff auf "value" sparen.

Nehmen wir an, fname liegt an Adresse 1000 im Speicher. Um nun vom Beginn der Struktur bis zu "value" zu gelangen muss man alle vorherigen Elemente überspringen. Es gibt 0 Elemente, die zu überspringen sind, also wird 0 addiert und man hat die Adresse gefunden, an der "value" liegt: 1000. Dass Du auf "value" zugreifen möchtest ist ja nicht variabel, da steht ja "value" (in C++ kann man für auch für Struktur-Member Variablen beschreiben). Hier hast du aber ganz statisch gesagt, dass Du "value" auswählst, also 0 Bytes auf die Start-Adresse der Struktur addieren möchtest.

Dass der Compiler eine Addition mit dem statischen Wert 0 nicht heraus optimiert, kann ich Dir nicht garantieren. Ich kann Dir aber garantieren, dass es nicht viel Aufwand ist, das zu prüfen.
Nemo hat geschrieben:, oder sollte man die, wenn es auf Geschwindigkeit ankommt, zum Testen verwenden und für die endgültige Version wieder entfernen?
Wenn es auf Geschwindigkeit ankommt, arbeitet man nicht mehr mit Strings. ;)
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Nemo
Beiträge: 37
Registriert: Sa Mär 02, 2013 3:18 pm

Re: short, int und long

Beitrag von Nemo » Mi Mär 27, 2013 1:20 pm

Xin hat geschrieben:Nehmen wir an, fname liegt an Adresse 1000 im Speicher. Um nun vom Beginn der Struktur bis zu "value" zu gelangen muss man alle vorherigen Elemente überspringen. Es gibt 0 Elemente, die zu überspringen sind, also wird 0 addiert und man hat die Adresse gefunden, an der "value" liegt: 1000.
Aha, vielleicht könntest du das in das Tutorial einbauen. Ich kann mir das ganze durch diese Erklärung viel besser vorstellen.
Xin hat geschrieben:Dass der Compiler eine Addition mit dem statischen Wert 0 nicht heraus optimiert, kann ich Dir nicht garantieren. Ich kann Dir aber garantieren, dass es nicht viel Aufwand ist, das zu prüfen.
Und wie genau prüft man das? Einmal mit und einmal ohne Hüllstruktur compilieren und dann überprüfen ob der gleiche Maschinencode dabei rauskommt?
Xin hat geschrieben:
Nemo hat geschrieben:, oder sollte man die, wenn es auf Geschwindigkeit ankommt, zum Testen verwenden und für die endgültige Version wieder entfernen?
Wenn es auf Geschwindigkeit ankommt, arbeitet man nicht mehr mit Strings. ;)
Das ist schon klar, war ja nur ein Beispiel. ;)

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: short, int und long

Beitrag von Xin » Mi Mär 27, 2013 2:14 pm

Nemo hat geschrieben:
Xin hat geschrieben:Nehmen wir an, fname liegt an Adresse 1000 im Speicher. Um nun vom Beginn der Struktur bis zu "value" zu gelangen muss man alle vorherigen Elemente überspringen. Es gibt 0 Elemente, die zu überspringen sind, also wird 0 addiert und man hat die Adresse gefunden, an der "value" liegt: 1000.
Aha, vielleicht könntest du das in das Tutorial einbauen. Ich kann mir das ganze durch diese Erklärung viel besser vorstellen.
Ähh... ich versuche es mir zu merken. ^^ (bzw. auf die Todoliste zu setzen ^^)
Nemo hat geschrieben:
Xin hat geschrieben:Dass der Compiler eine Addition mit dem statischen Wert 0 nicht heraus optimiert, kann ich Dir nicht garantieren. Ich kann Dir aber garantieren, dass es nicht viel Aufwand ist, das zu prüfen.
Und wie genau prüft man das? Einmal mit und einmal ohne Hüllstruktur compilieren und dann überprüfen ob der gleiche Maschinencode dabei rauskommt?
Genau.
Wobei Du den Unterschied - falls 'add register, 0' mit einkompiliert wurde - in der Ausführung eventuell schon nicht mehr mitbekommst, weil ein Prozessorkern heute aus einer Pipeline von Prozessoren besteht. Das Addieren einer konstanten Zahl ist eine häufige und vergleichweise einfache Aufgabe, die ein eigens dafür vorgesehener Prozessor bereits erledigen kann, bevor der wirkliche Prozessorkern überhaupt an die Aufgabe kommt. Die Anweisung kann also genauso gut von einem halbwegs modernen Prozessor zur Laufzeit aus der Pipeline herausoptimiert werden, so dass diese Additionen sowieso niemals einen Takt fressen.

Die Existenz solcher Vorab-Prozessoren sorgen auch dafür, dass die Taktfrequenz nicht mehr unbedingt eine gute Aussage darüber trifft, ob und wann ein Prozessor schnell ist oder wann nicht.

Ich programmiere seit 20 Jahren in C/C++, aber vieles macht man einfach nach Gefühl: Probieren wir es mal aus!

Folgendes Programm, es macht ganz einfach pro Aufruf von swap...() aus großen Buchstaben Kleine und aus kleinen Buchstaben Große. Einmal direkt mit char * und einmal mit der struct FirstName.

Code: Alles auswählen

xin@prgn:~$ cat dotvalue.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct FirstName
{
  char Value[64];
};

void swapChar( char * value )
{
  unsigned int i=0;
  while( value[i] )
  {
    if( value[i] >= 'a' && value[i] <= 'z' )
      value[i] -= 32;
    else if( value[i] >= 'A' && value[i] <= 'Z' )
      value[i] += 32;

    i++;
  }
}

void swapFirstName( struct FirstName * value )
{
  unsigned int i=0;
  while( value->Value[i] )
  {
    if( value->Value[i] >= 'a' && value->Value[i] <= 'z' )
      value->Value[i] -= 32;
    else if( value->Value[i] >= 'A' && value->Value[i] <= 'Z' )
      value->Value[i] += 32;

    i++;
  }
}

int main( int argc, char ** argv )
{
  if( argc != 4 )
  {
    printf( "%s <count> <method> <string>\nmethod: 0 for char *; 1 for struct FirstName *\n\nArgCount: %d - must be 3\n", argv[0], argc );
    return EXIT_FAILURE;
  }

  unsigned int count = strtoul( argv[1], NULL, 10 );
  printf( "Count: %u\n", count );

  unsigned int method = strtoul( argv[2], NULL, 10 );

  switch( method )
  {
    case 0:
    {
      printf( "Method: char *\n" );
      char buffer[64];
      strcpy( buffer, argv[3] );
      printf( "Start : %s\n", buffer );

      while( count-- )
        swapChar( buffer );

      printf( "Result: %s\n", buffer );
      return EXIT_SUCCESS;
    }
    case 1:
    {
      printf( "Method: struct FirstName *\n" );
      struct FirstName fname;
      strcpy( fname.Value, argv[3] );
      printf( "Start: %s\n", fname.Value );

      while( count-- )
        swapFirstName( &fname );

      printf( "Result: %s\n", fname.Value );
      return EXIT_SUCCESS;
    }
    default:
    {
      printf( "unknown method\n" );
      return EXIT_FAILURE;
    }
  };
}
xin@prgn:~$
Ergebnis:

Code: Alles auswählen

xin@prgn:~$ gcc dotvalue.c
xin@prgn:~$ time ./a.out 30000001 0 "Hello proggen.org"
Count: 30000001
Method: char *
Start : Hello proggen.org
Result: hELLO PROGGEN.ORG

real   0m11.220s
user   0m9.385s
sys    0m0.012s
xin@prgn:~$ time ./a.out 30000001 0 "Hello proggen.org"
Count: 30000001
Method: char *
Start : Hello proggen.org
Result: hELLO PROGGEN.ORG

real   0m9.463s
user   0m9.413s
sys    0m0.004s
xin@prgn:~$ time ./a.out 30000001 1 "Hello proggen.org"
Count: 30000001
Method: struct FirstName *
Start: Hello proggen.org
Result: hELLO PROGGEN.ORG

real   0m8.286s
user   0m8.289s
sys    0m0.000s
xin@prgn:~$ time ./a.out 30000001 1 "Hello proggen.org"
Count: 30000001
Method: struct FirstName *
Start: Hello proggen.org
Result: hELLO PROGGEN.ORG

real   0m11.017s
user   0m8.285s
sys    0m0.000s
xin@prgn:~$ gcc -O9 dotvalue.c
xin@prgn:~$ time ./a.out 30000001 0 "Hello proggen.org"
Count: 30000001
Method: char *
Start : Hello proggen.org
Result: hELLO PROGGEN.ORG

real   0m3.556s
user   0m3.276s
sys    0m0.008s
xin@prgn:~$ time ./a.out 30000001 0 "Hello proggen.org"
Count: 30000001
Method: char *
Start : Hello proggen.org
Result: hELLO PROGGEN.ORG

real   0m3.447s
user   0m3.284s
sys    0m0.000s
xin@prgn:~$ time ./a.out 30000001 1 "Hello proggen.org"
Count: 30000001
Method: struct FirstName *
Start: Hello proggen.org
Result: hELLO PROGGEN.ORG

real   0m3.691s
user   0m3.324s
sys    0m0.000s
xin@prgn:~$ time ./a.out 30000001 1 "Hello proggen.org"
Count: 30000001
Method: struct FirstName *
Start: Hello proggen.org
Result: hELLO PROGGEN.ORG

real   0m3.742s
user   0m3.324s
sys    0m0.000s
xin@prgn:~$
Wir sehen, dass naums viel größeren Einfluss auf die Ergebnisse hat, als die Struktur... das ganze sollte man also nicht auf dem Webserver testen, wenn da grade einer im Wiki hantiert... Bei der optimiert kompilierten Variante, kam Naums wohl nicht rechtzeitig zum Reload-Button :D

Probiers nochmal an deinem Rechner aus - die Dienste auf proggen.org bringen hier 20% Ungenauigkeit beim gleichen Programmablauf rein, das macht diese Aussage nicht sonderlich stichhaltig.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Nemo
Beiträge: 37
Registriert: Sa Mär 02, 2013 3:18 pm

Re: short, int und long

Beitrag von Nemo » Do Mär 28, 2013 4:02 pm

Ich hab das Programm etwas abgeändert, um die Messung genauer zu machen:

Code: Alles auswählen

  if( method )
  {
    struct FirstName fname;
    strcpy( fname.Value, argv[3] );
    printf( "Start: %s\n", fname.Value );
    printf( "stru\n" );

    while( count-- )
      swapFirstName( &fname );

    printf( "Result: %s\n", fname.Value );
    return EXIT_SUCCESS;
  }
  else
  {
    char buffer[64];
    strcpy( buffer, argv[3] );
    printf( "Start : %s\n", buffer );
    printf( "Char\n" );

    while( count-- )
      swapChar( buffer );

    printf( "Result: %s\n", buffer );
    return EXIT_SUCCESS;
  }
Dann hab ich es mit -O3 compiliert und mit größeren Werten gefüttert. Ich hab das ganze dann 15 mal mit beiden Methoden durchlaufen lassen und war vom Ergebnis ziemlich überrascht:
Die Methode mit Hüllstruktur ist durchschnittlich um 9% schneller!

Ich habe auch dein Originalprogramm compiliert und ausgeführt und die Methode mit der Hüllstruktur war schon wieder schneller.
Das konnte ich mir nicht erklären also habe ich beide Programme ohne optimierung compiliert, wodurch wie erwartet die Methode mit Hüllstruktur deutlich langsamer war.

Ich habe keine Ahnung was der Compiler da macht aber durch das optimieren wird die Funktion mit der Hüllstruktur schneller als die Funktion mit dem Char-Array. Hat irgendwer eine Ahnung warum :?:

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: short, int und long

Beitrag von Xin » Do Mär 28, 2013 4:23 pm

Nemo hat geschrieben:Ich hab das Programm etwas abgeändert, um die Messung genauer zu machen:
Das Switch tut bei Millionen swap-Aufrufen nicht weh ;-)
Nemo hat geschrieben:Dann hab ich es mit -O3 compiliert und mit größeren Werten gefüttert. Ich hab das ganze dann 15 mal mit beiden Methoden durchlaufen lassen und war vom Ergebnis ziemlich überrascht:
Die Methode mit Hüllstruktur ist durchschnittlich um 9% schneller!

Ich habe auch dein Originalprogramm compiliert und ausgeführt und die Methode mit der Hüllstruktur war schon wieder schneller.
Das konnte ich mir nicht erklären also habe ich beide Programme ohne optimierung compiliert, wodurch wie erwartet die Methode mit Hüllstruktur deutlich langsamer war.

Ich habe keine Ahnung was der Compiler da macht aber durch das optimieren wird die Funktion mit der Hüllstruktur schneller als die Funktion mit dem Char-Array. Hat irgendwer eine Ahnung warum :?:
time ist nur eine Näherung, keine wirklich gute Messung!
Wenn bei 15 Messungen sich allerdings so deutlich eine Tendenz zeigt, dann müsste man sich wirklich den Assemblercode ansehen.

Code: Alles auswählen

gcc dotvalue.c -S -fverbose-asm
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Antworten