Forum: Compiler & IDEs Array auf Stack oder ins Datensegment ablegen? (Zugriffszeit)


von David H. (david_h)


Lesenswert?

Hallo,

ich möchte ein Array an mehrere Funktionen übergeben, benötige aber 
keinen globalen Zugriff auf die Daten. Wann sollte man dann das Array im 
Stack, und wann im Datensegmen ablegen? Wie wird denn genau die Adresse 
eines Array-Elements berechnet, wenn ich den Array-Pointer als Parameter 
übergeben habe.

Ich stelle mir das in etwa so vor:

Methode 1
1
int myArray[] = {1, 2, 3};
2
3
void f2()
4
{
5
  myArray[2] = 13;
6
}
7
8
void f1() 
9
{ 
10
  myArray[0] = 42;
11
  f2(); 
12
}
13
14
main()
15
{
16
  while(1)
17
    f1();
18
};
Hier muss jedes Mal ein Zugriff auf die Daten im Datensegment erfolgen. 
Vorteil: Muss nur einmal angelegt & initialisiert werden.

Methode 2
1
void f2(int array[])
2
{
3
  array[2] = 13;
4
}
5
6
void f1() 
7
{ 
8
  int myArray[] = {1, 2, 3};
9
  myArray[0] = 42;
10
  f2(myArray); 
11
}
12
13
main()
14
{
15
  while(1)
16
    f1();
17
};
Hier muss bei jedem Funktionsaufruf von f1 myArray angelegt & 
initialisiert werden. Dafür verbraucht es keinen Speicher im 
Datensegment. Ich frage mich wie hier auf ein Array-Element zugefriffen 
wird. Ich nehme an f2 wird hier einen Zeiger auf Array auf dem Stack 
bekommen. Läuft das dann in etwa so (Pseudo-Code):
1
baseArray = FramePointer - 4; //hole das Argument aus dem Stack-Frame. Die Zahl 4 ist einfach ausgedacht.
2
wertBeiArray2 = *(baseArray + 2*Wortlaenge); //ist dieser Zugriff genau so langsam wie der Zugriff auf das Datensegment?
?


Oder was gibt es noch zu beachten? Was wäre ein "guter Stil"/sauberer? 
Mein Hauptaugenmerk liegt auf die Zugriffszeit. Die Frage stellt sich 
nur unter der Annahme, dass ich genug Platz im Stack und Datensegment 
habe...
Bin für Anmerkungen dankbar!

David

von Andreas F. (aferber)


Lesenswert?

Oder.

Ohne genauere Angaben, zumindest zur verwendeten Architektur, besser 
aber auch noch z.B. zur Größe des Arrays, Art der Zugriffe (mehr Lesen 
oder mehr Schreiben) etc., ist die Frage nicht beantwortbar.

Andreas

von David H. (david_h)


Lesenswert?

Hallo Andreas,

ich hatte gehofft es gäbe allgemeine Richtlinien an die man sich halten 
könnte. Ich interessiere mich auch eher für die Faktoren die man von 
Fall zu Fall in Betracht nehmen muss und wie diese zu interpretieren 
sind. Auch ob mein Gedankengang bzgl der Adressierung des Arrays 
überhaupt richtig ist.

In meinem Fall handelt es sich um einen Digital Signal Controler, dem 
Delphino TSM320F28335 von TI. Ist eine 32-bit CPU mit 256k*16 Flash und 
24k*16 SARAM Speicher.
In diesem konkreten Fall handelt es sich um ein Array mit 10 Elementen 
die aus Zeigern auf Strukturen bestehen. Es werden genau so viele Lese- 
wie Schreiboperationen vorgenommen. Schreiboperationen sind 
ausschließlich |=

Fehlt noch eine Angabe?

Grüße, David

von (prx) A. K. (prx)


Lesenswert?

David H. schrieb:

> ich hatte gehofft es gäbe allgemeine Richtlinien an die man sich halten
> könnte.

Problem dabei: Es gibt Prozessoren, die mit statisch adressierten Arrays 
besser umgehen können als mit Arrays auf dem Stack. Und umgekehrt. Davon 
hängt das durchaus ein bischen ab. Es hängt auch davon ab, ob der Index 
wie oben gezeigt überwiegend konstant ist oder variabel.

Was du selber schon angemerkt hast: Die Initialisierung lokaler Arrays 
der Speicherklasse "auto" kostet Code und Zeit - sofern der Compiler das 
überhaupt akzeptiert (C89 nicht). Sowas macht man bei grossen Arrays 
folglich eher selten.

> Auch ob mein Gedankengang bzgl der Adressierung des Arrays
> überhaupt richtig ist.

Ist er. Da liegt ja der Hase im Pfeffer.

von (prx) A. K. (prx)


Lesenswert?

David H. schrieb:

> In diesem konkreten Fall handelt es sich um ein Array mit 10 Elementen
> die aus Zeigern auf Strukturen bestehen. Es werden genau so viele Lese-
> wie Schreiboperationen vorgenommen. Schreiboperationen sind
> ausschließlich |=

Also ist array[i] ein Pointer und es sind Operationen der Art
1
array[i] |= value;
Muss ein anderes C sein als die mir bekannten Versionen.

von David H. (david_h)


Lesenswert?

A. K. schrieb:
> David H. schrieb:
>> Schreiboperationen sind ausschließlich |=
> Also ist array[i] ein Pointer und es sind Operationen der Art
>
1
array[i] |= value;
> Muss ein anderes C sein als die mir bekannten Versionen.

Ups, da habe ich den Code nicht ganz durchdacht! Also da fällt mir auf, 
dass ich eigentlich gar nicht in das Array schreibe! Die Operationen 
sind von der Art:
1
array[i]->element |= foo; //int element, foo; meinStructTyp * array[10];

Kann man nun, da es nur Lese-Operationen sind, ein Aussage zum 
geeigneteren Speicherplatz machen?

von (prx) A. K. (prx)


Lesenswert?

Wenn ins Array überhaupt nicht geschrieben wird, dann ist es konstant 
und kann derart deklariert werden. Dann kann das vom Compiler weit 
besser optimiert werden und landet, wenn statisch, je nach Architektur 
platzsparend im ROM statt RAM.

von David H. (david_h)


Lesenswert?

Ja richtig!
Die Deklaration würde dann so aussehen
1
static const meinStructTyp * array[] = { &struct1, &struct2, ... };

Das würde dann auch keinen Unterschied mehr machen ob ich das dann local 
oder global deklariere, stimmts!?

von (prx) A. K. (prx)


Lesenswert?

Mit "static" sowieso nicht. Das ist vom Code her identisch, egal ob in 
der Funktion oder draussen. Ob der Zugriff einen nennenswerten 
Unterschied macht lässt sich nicht verallgemeinert sagen, aber ich würde 
da nicht so drauf rumreiten. Es gibt in deinem Programm garantiert 
andere Stellen, die von weit grösserem Interesse sind.

Besser wär's aber mit
1
static meinStructTyp * const array[] = { &struct1, &struct2, ... };
denn es ist der Pointer konstant, nicht die struct.

von David H. (david_h)


Lesenswert?

Auf jeden Fall Danke schon mal für eure Anmerkungen! Damit hat sich mein 
Problem in Luft aufgelöst. Aber bzgl dem anfangs gestellten Problem bin 
ich nicht wirklich schlauer geworden. Aber wahrscheinlich hängt das zu 
sehr von der Architektur ab, als man das pauschal beantworten könnte. 
Man muss die Architektur dann auch gut kennen um das zu wissen.

A. K. schrieb:
> Es gibt in deinem Programm garantiert
> andere Stellen, die von weit grösserem Interesse sind.

Ja richtig. Aber mich interessiert ja auch nicht der konkrete Fall. Ich 
dachte man könnte pauschal sagen, Zugriffe auf das Datensegment bzw 
Stack sind idR schneller und sollten entsprechend genutzt werden.

Danke!

von (prx) A. K. (prx)


Lesenswert?

David H. schrieb:

> Man muss die Architektur dann auch gut kennen um das zu wissen.

So ist es. Drum habe ich nichts drüber gesagt. Ich kenne zwar ziemlich 
viele Architekturen, aber nicht diese DSPs.

von Andreas F. (aferber)


Lesenswert?

David H. schrieb:
> In diesem konkreten Fall handelt es sich um ein Array mit 10 Elementen
> die aus Zeigern auf Strukturen bestehen.

David H. schrieb:
> Hier muss bei jedem Funktionsaufruf von f1 myArray angelegt &
> initialisiert werden.

Damit ist die Frage doch im wesentlichen schon beantwortet. Im einen 
Fall müssen erst 10 Elemente aus dem Code- oder Datensegment kopiert 
werden, um dann eines am Ende zu lesen, im anderen Fall muss nur ein 
Element aus den Daten gelesen werden.

David H. schrieb:
> Die Deklaration würde dann so aussehen
> static const meinStructTyp * array[] = { &struct1, &struct2, ... };

Nur stellt sich dann die Frage, warum du nicht gleich die Structs selbst 
in das Array packst:
1
static meinStructTyp array[] = {
2
    { ... },
3
    { ... },
4
    ...
5
};

Langsamer als die Variante mit Pointern sollte das in keinem Fall sein, 
in diversen Fällen spart es aber eine Indirektionsebene ein. Pointer auf 
die einzelne Struct kannst du dann immer noch erzeugen, wenn du die z.B. 
an eine andere Funktion übergeben musst.

Andreas

von (prx) A. K. (prx)


Lesenswert?

Andreas Ferber schrieb:

> Langsamer als die Variante mit Pointern sollte das in keinem Fall sein,

Doch, weil dann abhängig von der Grösse der struct u.U. multipliziert 
werden muss. Dafür hat er sich mit einem DSP zwar eine ideale Plattform 
gesucht, denn niemand kann das so gut wie die, aber das wäre dann schon 
z.T. ein recht grosser Unterschied bei verschiedenen Architekturen, also 
kaum als architekturübergreifender Tipp geeignet.

von Andreas F. (aferber)


Lesenswert?

A. K. schrieb:
> Doch, weil dann abhängig von der Grösse der struct u.U. multipliziert
> werden muss.

Das muss abhängig von der Größe eines Pointers auch bei der indirekten 
Variante gemacht werden.

Andreas

PS: natürlich habe ich deinen Einwand schon verstanden, es läuft halt am 
Ende immer darauf hinaus, dass es auf die Spezialitäten der jeweiligen 
Architektur ankommt.

von (prx) A. K. (prx)


Lesenswert?

Andreas Ferber schrieb:

> Das muss abhängig von der Größe eines Pointers auch bei der indirekten
> Variante gemacht werden.

Im Prinzip ja, aber Pointer, deren Grösse im Compiler keine Zweierpotenz 
ist, sind mindestens extrem selten wenn es überhaupt welche gibt (mir 
fällt grad kein Fall ein). Und die Multiplikation mit einer Zweierpotenz 
ist in allen Architekturen erträglich.

Daher ist ein Pointer-Array architekturübergreifend betrachtet weniger 
riskant als ein struct-Array. Das kann man wohl schon verallgemeinern. 
Es sei denn das Array ist schweinemässig gross und es kommen 
Cache-Misses hinzu, die teurer sind als die Multiplikation ;-). Aber da 
sind wir etwas jenseits des hier im Forum üblichen Rahmens.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.