staging.inyokaproject.org

Einfuss der Länge einer Variablen auf die Geschwindigkeit (C)

Status: Ungelöst | Ubuntu-Version: Ubuntu MATE 18.04 (Bionic Beaver)
Antworten |

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Ich frage mich gerade welchen Einfluss die Länge einer Variablen auf die Ausführungsgeschwindigkeit hat.

Ich suche in einer Datei nach einem 16 Byte langen Muster (GUID). Darin kommt folgende Codesequenz vor:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    static const char guid[16]
            = { 0x00, 0x57, 0xfb, 0x20, 0x55, 0x5b, 0xcf, 0x11,
                0xa8, 0xfd, 0x00, 0x80, 0x5f, 0x5c, 0x44, 0x2b };
    char    cbuf[16]; // Ringpuffer
    ...

        ri = wi; // Leseindex
        for( i = 0; i < 16; i++ ) {
            if( (res = guid[i] - cbuf[ri++]) != 0 )
                break;
            if( ri == 16 )
                ri = 0;         // wrap around
        }
    ...

cbuf ist dabei ein Ringpuffer, der die letzten 16 gelesenen Bytes enthält.

Die Variable res war ursprünglich ein char. Probeweise habe ich das mal auf int geändert und es scheint, dass dadurch die Ausführungsgeschwindigkeit minimal schneller ist. Gibt es dafür eine logische Erklärung?

Ich dachte bisher, wenn man nur Bytes benötigt, ist es schneller wenn man auch nur Bytes benutzt.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13242

Dakuan schrieb:

Die Variable res war ursprünglich ein char. Probeweise habe ich das mal auf int geändert und es scheint, dass dadurch die Ausführungsgeschwindigkeit minimal schneller ist. Gibt es dafür eine logische Erklärung?

Was bedeutet denn "minimal" und wie hast Du das ermittelt? Benchmarking ist so eine Kunst für sich...

Ich dachte bisher, wenn man nur Bytes benötigt, ist es schneller wenn man auch nur Bytes benutzt.

Die Register der CPU haben ja sowieso eine feste Breite (heutzutage dürften das üblicherweise 64 Bit sein). Ggf. wird für den Bytevergleich ein anderer Befehl benutzt, der langsamer ist. Du kannst ja mal beide Varianten mit "-S" übersetzen und Dir den Assemblercode anschauen.

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Ich war da etwas bequem und habe die Zeiten einfach mit dem Befehl time ermittelt, wobei das Programm den Suchvorgang 10000 Mal auf die selbe Datei angewandt hatte. Die Datei wurde zwischen den Suchvorgängen nicht geschlossen (rewind()). Die Zeitdifferenz dabei war etwa 50ms.

Der Assemblercode sagt mir jetzt nicht wirklich viel. Hier mal die langsame char Version:

.L157:
	.loc 1 730 0			; ri = wi;
	movl	-56(%rbp), %eax
	movl	%eax, -60(%rbp)
	.loc 1 731 0			; for( i = 0; i < 16; i++ ) {
	movl	$0, -52(%rbp)
	jmp	.L151
.L155:
	.loc 1 732 0			; if( (res = guid[i] - cbuf[ri++]) != 0 )
	movl	-52(%rbp), %eax
	movslq	%eax, %rdx
	leaq	guid.3222(%rip), %rax
	movzbl	(%rdx,%rax), %eax
	movl	%eax, %ecx
	movl	-60(%rbp), %eax
	leal	1(%rax), %edx
	movl	%edx, -60(%rbp)
	cltq
	movzbl	-48(%rbp,%rax), %eax
	subl	%eax, %ecx
	movl	%ecx, %eax
	movb	%al, -61(%rbp)
	cmpb	$0, -61(%rbp)
	jne	.L160
	.loc 1 734 0			; if( ri == 16 )
	cmpl	$16, -60(%rbp)
	jne	.L154
	.loc 1 735 0			; ri = 0;
	movl	$0, -60(%rbp)
.L154:
	.loc 1 731 0 discriminator 2		; for( i = 0; i < 16; i++ ) {
	addl	$1, -52(%rbp)
.L151:
	.loc 1 731 0 is_stmt 0 discriminator 1	;for( i = 0; i < 16; i++ ) {
	cmpl	$15, -52(%rbp)
	jle	.L155
	jmp	.L153
.L160:
	.loc 1 733 0 is_stmt 1
	nop
.L153:
	.loc 1 737 0			; if( res == 0 ) 
	cmpb	$0, -61(%rbp)
	jne	.L156

und hier die Abweichung bei der int Version:

.L155:
	.loc 1 732 0			; if( (res = guid[i] - cbuf[ri++]) != 0 )
	movl	-56(%rbp), %eax
	movslq	%eax, %rdx
	leaq	guid.3222(%rip), %rax
	movzbl	(%rdx,%rax), %eax
	movsbl	%al, %ecx
	movl	-64(%rbp), %eax
	leal	1(%rax), %edx
	movl	%edx, -64(%rbp)
	cltq
	movzbl	-48(%rbp,%rax), %eax
	movsbl	%al, %eax
	subl	%eax, %ecx
	movl	%ecx, %eax
	movl	%eax, -52(%rbp)
	cmpl	$0, -52(%rbp)
	jne	.L160
	.loc 1 734 0
	cmpl	$16, -64(%rbp)
	jne	.L154
	.loc 1 735 0
	movl	$0, -64(%rbp)
.L154:

Die einzige Merkwürdigkeit, die ich da entdecken kann ist dass die Variable res in der ersten Version offenbar eine ungerade Adresse (-61) bekommen hat.

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2136

Aus welchem Grund nutzt du nicht einfach die strstr() Funktion...?

ChickenLipsRfun2eat Team-Icon

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

Vielleicht optimiert der Compiler auch den int einfach besser als einzelne bytes. Hast du beim kompilieren mal die Optimierung ausgeschaltet? (gcc war imho -O0)

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

  1. Weil es keine Strings sind. Da tauchen immer wieder Nullbytes auf.

  2. Wegen der Pufferung. Ich müsste dann auf Verdacht einen großen Teil der (Multimedia-) Datei lesen.

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

@ChickenLipsRfun2eat Mit der Optimierung habe ich bisher noch nicht experimentiert. Ich war noch mit dem Debugging der anderen Funktionen beschäftigt.

Aber wahrscheinlich hast du recht. Ich hatte allerdings gedacht, das man das irgendwie an den Befehlen sehen kann.

In einem anderen Fall, wo es um double oder float ging, konnte man das sehen. Da wurde dann immer ein zusätzlicher FPU-Befehl eingefügt.

ChickenLipsRfun2eat Team-Icon

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

Naja, ich bin mir da auch immer unsicher. Ein Pointer hat 8 Bytes, ein int 4, ushort 2 und char 1. ( zumindest auf 64bit ). Da hoffe ich eigentlich, dass der Compiler das entsprechend richtig macht. Der Platz ist ja heute nicht mehr so relevant, wie der schnelle Zugriff. Ich hatte letztens ein Beispiel gesehen, in dem der Compiler selbständig eine komplette Kopie einer Klasseninstanz durch eine Referenz ersetzt hat. Deswegen kam ich da drauf. Aber ich möchte nicht parallel auch noch Assembler lernen ^^

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Ein Pointer hat 8 Bytes, ein int 4, ushort 2 und char 1.

Vorsicht. Wenn GCC C spricht, hat ein int 4 Bytes aber wenn er C++ spricht 8 Bytes. Deshalb habe ich immer wenn es um die Abbildung einer Dateistruktur geht, umgestellt auf int32_t oder int16_t u.s.w.

Der Platz ist ja heute nicht mehr so relevant, wie der schnelle Zugriff.

Das kann aber zusammenhängen. z.B. bei der Bildmanipulation, wo dann das alles noch im Speicher bewegt werden muss.

Das hatte ich übrigens im oben genannten Beispiel auch ausprobiert, also einen größeren Puffer zu nehmen um dann 16 Bytes vor dem Ende den Rest nach vorne zu kopieren um daran anschließend die nächste Portion der Datei zu lesen. Da war ich aber wohl mit dem Speicherplatz noch zu geizig.

Aber ich möchte nicht parallel auch noch Assembler lernen ^^

Ich auch nicht. Wer einmal MC68000 Assembler gemacht hat, tut sich mit der Intel Schreibweise schwer. Ich tippe die Befehle dann in die Suchmaschine ein und gucke ob das irgendwie interessant klingt 😉

ChickenLipsRfun2eat Team-Icon

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

Dakuan schrieb:

Vorsicht. Wenn GCC C spricht…

Ja, sorry, ich bin gerade voll auf C ☺ Bei mir ist es übrigens gleich - also bei beiden 4 Bytes.

Das hatte ich übrigens im oben genannten Beispiel auch ausprobiert, also einen größeren Puffer zu nehmen um dann 16 Bytes vor dem Ende den Rest nach vorne zu kopieren um daran anschließend die nächste Portion der Datei zu lesen. Da war ich aber wohl mit dem Speicherplatz noch zu geizig.

Da bist du wohl fitter als ich. Meine Puffer haben eher so 1000 bytes oder 4096… Bin da etwas gröber unterwegs 😉

Ich auch nicht. Wer einmal MC68000 Assembler gemacht hat, tut sich mit der Intel Schreibweise schwer. Ich tippe die Befehle dann in die Suchmaschine ein und gucke ob das irgendwie interessant klingt 😉

Hab ich noch nie… möchte ich trotzdem nicht. So gut ist mein Gehirn nicht, dass es das gut lernen kann 😀

Neral

Anmeldungsdatum:
3. Oktober 2007

Beiträge: 230

@Dakuan: Kannst du ein konkretes Beispielprogramm mit einer konkreten GCC-Version auf einer konkreten Architektur nennen, in dem ein int 8 Byte groß ist?

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Ups, da muss ich mich korrigieren. Ich habe das gerade überprüft und es sind tatsächlich nur 4 Bytes.

Da habe ich dann wohl nach den Problemen bei der Umstellung von 32-Bit auf 64-Bit etwas verwechselt. Ich hatte damals mehrere Tage gebraucht, bis überhaupt wieder irgendetwas lief. Das Alignment war damals nicht mein Freund.

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2136

Dakuan schrieb:

  1. Weil es keine Strings sind. Da tauchen immer wieder Nullbytes auf.

  2. Wegen der Pufferung. Ich müsste dann auf Verdacht einen großen Teil der (Multimedia-) Datei lesen.

Dann geht ja immer noch memmem(), was mit \0 klar kommt und auf vielen Linux-Systemen (u.a. Ubuntu) verfügbar ist...

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Danke, die Funktion kannte ich noch nicht. Taucht in meinen Büchern nicht auf, wohl weil es eine Gnu Extension ist.

Vielleicht greife ich doch noch darauf zurück. Ich habe mich aber gerade entschlossen, die gewünschte Information erst zu suchen, wenn der User auf das entsprechende Icon klickt. Da spielt Zeit dann keine Rolle mehr. Ursprünglich wollte ich die Informationen schon zusammentragen, wenn das Verzeichnis eingescannt wird.

Antworten |