effiziente Initialisirung mit Nullen

Persönliche Seite #2743327
Lesenswert?

Grundlegende Frage: Wenn ich ein Array anlege, das aus later Nullen 
bestehen soll, muss die CPU nach der Speicherzuweisung jedes einzelne 
Arrayelement abfahren und auf Null setzen? Oder gibt es für diesen 
Spezialfall eine Abkürzung? Oder hängt das einfach nur von der Hardware 
ab und wird vom Compiler geregelt?

PS: Mir geht es eigentlich um GPUs, aber ich würde gerne wissen, wie es 
allgemein aussieht.
Persönliche Seite #2743338
Lesenswert?

Maxim S. schrieb:
> Wenn ich ein Array anlege, das aus later Nullen
> bestehen soll, muss die CPU nach der Speicherzuweisung jedes einzelne
> Arrayelement abfahren und auf Null setzen?

Ja. Wobei "Arrayelement" hier nichts mit den von Deinem Programm 
genutzten Elementen zu tun hat, sondern einzelne Bytes bzw. bei 
Prozessoren mit größerer Datenbusbreite die korrespondierende Datengröße 
beschreibt.

Ein 32-Bit-Prozessor wird also ein Array mit 32-Bit-Zugriffen 
beschreiben, auch wenn das Array im Programm aus einzelnen Bytes 
besteht.
(Firma: JDCC) #2743377
Lesenswert?

Peter II schrieb:
> da eine CPU nicht mal arrays kennt, denke ich nicht das es dafür befehle
> gibt. Es wird maximal eine möglichkeit haben einen Speicherbereich mit 0
> zu überschreiben, aber auch das muss vom Programieren umgesetzt werden.

Die x86er haben so etwas wie "Stringbefehle". Ob da ein
hardcodiertes memset dabei ist weiß ich jetzt gerade nicht.
Moderator Persönliche Seite #2743455
Lesenswert?

Peter II schrieb:
> Die Stringbefehle der CPU werden kaum
> von einem compiler umgesetzt.

Warum nicht?  Hängt vom Compiler und Target ab.  AVR-GCC beispiels-
weise expandiert memset() inline:
1
        ldi r24,lo8(256)
2
        ldi r25,hi8(256)
3
        ldi r30,lo8(array)
4
        ldi r31,hi8(array)
5
        mov r26,r30
6
        mov r27,r31
7
        mov r18,r24
8
        mov r19,r25
9
        st X+,__zero_reg__
10
        subi r18,1
11
        sbci r19,0
12
        brne .-8

GCC 4.6 auf FreeBSD/i386 ebenfalls:
1
        movl    $array, %edx
2
        movl    $64, %ecx
3
        movl    $0, %eax
4
        movl    %edx, %edi
5
        rep stosl

GCC 4.1 auf Linux/x86_64 dagegen nicht:
1
        movl    $256, %edx
2
        xorl    %esi, %esi
3
        movl    $array, %edi
4
        jmp     memset
Gast #2743507
Lesenswert?

Jörg Wunsch schrieb:
> Was soll das "oder"?

es könnte ja sein das er die funktion erkennt das ein speicherberich 
genullt werden soll und dieses mit hilfe der STringfunktionen umsetzt.

for( ... ) {
  x[y] = 0;
}


oder er weiss was memset macht und ersetzt es durch eine eigene 
implementierung. Was aber nicht mehr geht wenn ich mir die memset 
funtion umbenenne.
Moderator Persönliche Seite #2743542
Lesenswert?

Peter II schrieb:

> es könnte ja sein das er die funktion erkennt das ein speicherberich
> genullt werden soll und dieses mit hilfe der STringfunktionen umsetzt.
>
> for( ... ) {
>   x[y] = 0;
> }

Auch das könnte er erkennen, aber in meinem Falle war es ein
memset().

(Gerade probiert, eine explizite Schleife optimiert er auch mit
GCC 4.6 nicht, nur memset()).
#2743978
Lesenswert?

Peter II schrieb:
> avion23 schrieb:
>> Der Compiler ist an dieser Steller gefragt.
>
> ich würde denke hier ist mehr der Entwickler der glibc gefragt. Er muss
> dann memset anders implemtieren.

memset ist im Falle von gcc im Compiler implementiert, wie ein Großteil 
der Standard-Blibliothek. Siehe 
http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/Other-Builtins.html#Other-Builtins

> Die Stringbefehle der CPU werden kaum von einem compiler umgesetzt.

Warum sollten sie nicht? Bei mir tut er genau das, wenn ich einen 
memset-Aufruf mache. Allerdings hängt das davon ab, für welchen 
Prozessor man optimiert und wie groß der zu kopierende Block ist. Wenn 
ich z.B. die SIMD-Instruktionen freischalte (-march=core2), nutzt er 
die, aber nicht bei allen Blockgrößen. Und wenn die Blockgröße keine 
Konstante ist, sieht's dann nochmal ganz anders aus.

Jörg Wunsch schrieb:
> (Gerade probiert, eine explizite Schleife optimiert er auch mit
> GCC 4.6 nicht, nur memset()).

Sie sehen bei mir unterschiedlich aus, aber optimieren tut er die 
Schleife auch. Er erkent zumindest, daß er nicht jedes Byte einzeln 
schreiben muß, sondern das auch in 32-Bit-Blöcken tun kann.
Gast #2743994
Lesenswert?

Rolf Magnus schrieb:
>> Die Stringbefehle der CPU werden kaum von einem compiler umgesetzt.
> Warum sollten sie nicht?

ich hatte mal gelesen das sie zu "kompliziert" für dien Compiler sind. 
Sie sollen auch nicht wirklich geschwindigkeitsvorteile bringen. Die 
Befehle waren zu einer Zeit eingaut wurden wo man noch von Hand 
optimiert hat.

Und wenn gcc hier diese befehle bei memeset verwendet, dann vermute ich 
ganz stark das jemand dieses expliziert von hand codiert hat. Der 
normale Optimizer wird also nicht auf die Idee kommen diese befehle zu 
verwenden. Sonst hätte er es auch bei einer schleife gemacht.
Moderator Persönliche Seite #2744440
Lesenswert?

Peter II schrieb:
> Und wenn gcc hier diese befehle bei memeset verwendet, dann vermute ich
> ganz stark das jemand dieses expliziert von hand codiert hat.

Alles im GCC hat jemand explizit von Hand codiert. ;-)  Oder was
dachtest du, wie der Compiler seinen Code generiert?

> Der
> normale Optimizer wird also nicht auf die Idee kommen diese befehle zu
> verwenden.

Deine Vorstellungen über die Art und Weise, wie ein Compiler optimiert,
dürften aus dem letzten Viertel des vorherigen Jahrhunderts stammen.

Heutzutage gibt's einfach nicht mehr "den Optimizer", erst recht nicht
einen "normalen". ;-)
Gast #2744448
Lesenswert?

Jörg Wunsch schrieb:

> Oder was
> dachtest du, wie der Compiler seinen Code generiert?
es ist doch wohl ein unterschied ob man eine complete funktion in ASM 
codiert wird (von hand) oder ob der optimierer anhand des C codes 
erkennen soll was gemeint ist und damit selber auf die Idee kommt die 
string befehle zu verwenden.

>> Der
>> normale Optimizer wird also nicht auf die Idee kommen diese befehle zu
>> verwenden.
> Deine Vorstellungen über die Art und Weise, wie ein Compiler optimiert,
> dürften aus dem letzten Viertel des vorherigen Jahrhunderts stammen.

du könntest mich davon überzeugen das es nicht stimmt, in dem du mir mal 
code zeigst wo der compiler die string-upcodes verwendet.
Gast #2744563
Lesenswert?

Jörg Wunsch schrieb:
> Peter II schrieb:
>> du könntest mich davon überzeugen das es nicht stimmt, in dem du mir mal
>> code zeigst wo der compiler die string-upcodes verwendet.
>
> Dazu müsstest du dir schon mal den Compiler-Sourcecode selbst
> auspacken und in der Datei i386.md nach beispielsweise stosq
> suchen.

ich meinte zwar eine beispiel C datei wo mit hilfe des compilers am ende 
ein stosq verwendet wird.
Das der compiler grunsätzlich den upcode kennt, ist ja noch kein hinweis 
darauf das er ihn auch in der praxis irgenwie verwendet. Außer man 
schreibt gleich ASM code.

Werde mir aber mal die quellen saugen.
Moderator Persönliche Seite #2744810
Lesenswert?

Peter II schrieb:
> ich meinte zwar eine beispiel C datei wo mit hilfe des compilers am ende
> ein stosq verwendet wird.
1
$ cat foo.c
2
char array[256];
3

4
#include <string.h>
5

6
void
7
init_array(void)
8
{
9
  int i;
10
  for (i = 0; i < 256; i++)
11
    array[i] = 0;
12
}
13
$ /junk/testroot/bin/gcc -O3 -S foo.c
14
$ cat foo.s
15
        .file   "foo.c"
16
        .text
17
        .p2align 4,,15
18
        .globl  init_array
19
        .type   init_array, @function
20
init_array:
21
.LFB0:
22
        .cfi_startproc
23
        movl    $array, %edx
24
        movl    $64, %ecx
25
        pushl   %edi
26
        .cfi_def_cfa_offset 8
27
        .cfi_offset 7, -8
28
        xorl    %eax, %eax
29
        movl    %edx, %edi
30
        rep stosl
31
        popl    %edi
32
        .cfi_restore 7
33
        .cfi_def_cfa_offset 4
34
        ret
35
        .cfi_endproc
36
.LFE0:
37
        .size   init_array, .-init_array
38
        .comm   array,256,32
39
        .ident  "GCC: (GNU) 4.8.0 20120530 (experimental)"
40
        .section        .note.GNU-stack,"",@progbits

Es wird stosl (und nicht stosq) benutzt, weil der Compiler im
32-bit-Modus arbeitet.

> Das der compiler grunsätzlich den upcode kennt, ist ja noch kein hinweis
> darauf das er ihn auch in der praxis irgenwie verwendet. Außer man
> schreibt gleich ASM code.

Dafür müsste der Compiler den Opcode nicht kennen, denn inline asm
reicht er ja nur an den Assembler durch.
Gast #2744818
Lesenswert?

@Jörg Wunsch

danke für die Mühe. Ist das etwas neu in

GCC: (GNU) 4.8.0 20120530 (experimental)

?. Weil mit GCC 4.6 scheint es ja nicht zu passieren

> Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
> Datum: 05.07.2012 15:32
>
> (Gerade probiert, eine explizite Schleife optimiert er auch mit
> GCC 4.6 nicht, nur memset()).

oder liegt das am experimental?
Gast #2744831
Lesenswert?

es scheint zumindest noch nicht lange so zu sein. Mein 4.6 macht es 
nicht-

        .file   "test.c"
        .text
        .p2align 4,,15
        .globl  init_array
        .type   init_array, @function
init_array:
.LFB12:
        .cfi_startproc
        movl    $array, %eax
        .p2align 4,,7
        .p2align 3
.L2:
        movl    $0, (%eax)
        addl    $4, %eax
        cmpl    $array+256, %eax
        jne     .L2
        rep
        ret
        .cfi_endproc
.LFE12:
        .size   init_array, .-init_array
        .comm   array,256,32
        .ident  "GCC: (Debian 4.6.3-1) 4.6.3"
        .section        .note.GNU-stack,"",@progbits
Moderator Persönliche Seite #2744855
Lesenswert?

Peter II schrieb:
> Mein 4.6 macht es
> nicht-

Aber du siehst, dass er auch jeweils 4 Bytes zu einem Schreibzugriff
zusammenfasst.

Ab welcher Version das konkret drin ist, kann ich dir auch nicht
sagen.  Sollte ja vor allem als Beispiel dienen, dass die Optimierung
moderner Compiler tatsächlich so feingliedrig ist, dass sie da
unwahrscheinlich viel aufdröseln kann.  Letztlich muss ihr das
natürlich ein Mensch beigebracht haben, welche Muster im Maschinencode
durch welche Befehle optimal abarbeitbar sind.
Gast #2744878
Lesenswert?

Jörg Wunsch schrieb:
> Aber du siehst, dass er auch jeweils 4 Bytes zu einem Schreibzugriff
> zusammenfasst.

ja das ist auch gut so. Mir ging es aber extra um die String Opcodes. Da 
hatte ich mal gelesen das sie vom compiler nicht genutzt werden. Dies 
schein bis jetzt auch so zu sein.

.ident  "GCC: (Debian 4.7.1-2) 4.7.1"

macht es auch nicht. Etweder ist das ganze experimental oder es ist nach 
20Jahren wirklich mal einer auf die Idee gekommen es umzusetzen.
Gast #2745232
Lesenswert?

Wenn bei Intel genau nachliest, dann findet man, daß es verschiede Arten 
von Opcodes gibt. Manche laufen optimal durch die Pipelines, andere, wie 
z.B. die Repeated Stringbefehle, passen nicht auf jeder CPU dazu und 
laufen dann effektiv langsamer als äquivalente Sequenzen "flinker" 
Befehle. Durchsatz und Latenz sind die Stichworte.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren