Forum: Compiler & IDEs Geschwindigkeit Array vs Mehrdimensionaler Array


von Mehr ist mehr (Gast)


Lesenswert?

Guten Abend,

Ich bin auf ein Problem gestoßen. Mein Atmega8 ist mir zu lahm. Ich 
lasse den auf 8 MHz laufen und scheinbar ist mein Programmierstil so 
unglaublich schlecht, dass das nicht ausreicht. ;-)

Das Problem:
Ich habe ein doppeltes Array[6][3], dass ich gegen einen Wert i prüfen 
möchte. Wenn ich irgendwann fertig bin, soll das eine 18 Kanal PWM mit 
Schieberegister werden. g_byte wird dann per SPI losgeschickt. Sieht 
ungefähr so aus:
1
uint8_t array[6][3];
2
uint8_t g_byte[6];
3
.
4
.
5
.
6
void PWM(void){
7
uint8_t static i= 0;
8
9
  if (i < tabelle[array[0][0]]) {
10
    g_byte[0] |= (1 << 0);
11
  } else   g_byte[0] &= ~(1 << 0);
12
13
  if (i < tabelle[array[0][1]]) {
14
    g_byte[0] |= (1 << 1);
15
  } else   g_byte[0] &= ~(1 << 1);
16
.
17
.
18
.
19
  if (i < tabelle[array[5][2]]) {
20
    g_byte[5] |= (1 << 2);
21
  } else   g_byte[5] &= ~(1 << 2);
22
23
i++;
24
25
}

Das sind eine ganze Menge if-Abfragen und das dauert. Wäre es schneller, 
wenn ich da ein eindimensionales Array mache?
Oder kann ich da mit Pointern irgendwas zaubern?
Eine komplette Lösung meines Problems brauch ich jetzt nicht, sondern 
nur einen kleinen Anstoß, in welche Richtung ich gucken sollte, denn 
scheinbar ist meine Lösung nicht sonderlich flott...

Grüße

von Noname (Gast)


Lesenswert?

>Eine komplette Lösung meines Problems brauch ich jetzt nicht, sondern
>nur einen kleinen Anstoß

Nimm eine Schleife, mit for oder while.

von Peter II (Gast)


Lesenswert?

ich glaube mit dieser variante kommst du nicht weiter.

du musst ein Array vorberechnen damit du in diesem Teil vom Programm nur 
noch ein ein array zukreifen musst.
1
g_byte[0] = daten[x];
2
g_byte[1] = daten[x];
3
g_byte[5] = daten[x];

beim berechnen von daten kommt es ja nicht auf die zeit an. Das braucht 
dann zwar mehr speicher ist aber schneller.

Aber PWM und schieberegister passt eh nicht zusammen, hast du überhaupt 
mal gerechnet welcher wiederholfreqenz möglich ist?

von (prx) A. K. (prx)


Lesenswert?

Bei Indizierung mit Konstanten ist es irrelevant ob ein- oder 
mehrdimensional, da bereits der Compiler die Adresse berechnet.

von Peter II (Gast)


Lesenswert?

A. K. schrieb:
> Bei Indizierung mit Konstanten ist es irrelevant ob ein- oder
> mehrdimensional, da bereits der Compiler die Adresse berechnet.

aber nicht die zusätzliche Rechnerrei.

Die Frage ist auch vielmehr wozu er das array g_byte erst befüllt. Er 
muss die byte ja später sowiso seriell versenden, dann könnte er auch 
gleich an dieser stelle die aktuell pwm berechnen.

von Noname (Gast)


Lesenswert?

Oh Mann. Ich habs nicht kapiert. :-{

OK. Mit einer Schleife kriegst Du das Ganze nicht schneller.
Höchstens den Programmtext kürzer.

Falls nicht einer ohnehin raten kann, was der TO da macht, wäre es 
vielleicht hilfreich mal zu erklären was der Algorithmus ist und wie die 
Daten organisiert sind.
Möglicherweise kann man da was ändern und schneller machen.

von W.S. (Gast)


Lesenswert?

A. K. schrieb:
> Bei Indizierung mit Konstanten ist es irrelevant ob ein- oder
> mehrdimensional, da bereits der Compiler die Adresse berechnet.

Das ist bei C leider dummes Zeug, was du da geschrieben hast.

Bei Pascal könnte man das so machen, aber C kennt keine mehrdimensinalen 
Felder. Stattdessen besteht ein scheinbar mehrdimensionales Feld aus 
einem Feld von Zeigern, die jeweils auf ein anderes Feld zeigen. Also 
ist bei jedem Zugriff ein mehrfaches Dereferenzieren angesagt.

Unser Freund "Weniger ist manchmal mehr" schrieb:
> Ich habe ein doppeltes Array[6][3]

Ja, eben. Schreib deine Quelle um - und zwar so, daß du nicht ständig 
den Berg kreißen und ne Maus gebären lassen mußt.
Als erstes schmeiß deine Variablen
  uint8_t array[6][3];
  uint8_t g_byte[6];
raus und ersetze sie durch simple Variablen.
Und dann arbeite besser mit temporären Variablen, die bei Atmels noch in 
Registern gehalten wrden können. Sowas kannst du 1x am Schleifenanfang 
auf 0 setzen und sparst dir dann sämtliche else-Zweige ein.

W.S.

von Peter II (Gast)


Lesenswert?

W.S. schrieb:
> Bei Pascal könnte man das so machen, aber C kennt keine mehrdimensinalen
> Felder.

mein C schon.


int array[3][4];

und dort kommen keine zeiger vor.

von (prx) A. K. (prx)


Lesenswert?

W.S. schrieb:

> Das ist bei C leider dummes Zeug, was du da geschrieben hast.
>
> Bei Pascal könnte man das so machen, aber C kennt keine mehrdimensinalen
> Felder. Stattdessen besteht ein scheinbar mehrdimensionales Feld aus
> einem Feld von Zeigern, die jeweils auf ein anderes Feld zeigen.

Was soll das denn nun? C ist schon schlimm genug, da muss man die Leute 
nicht noch mehr verwirren als nötig.

Arrays von Arrays sind keine Arrays von Zeigern auf Arrays. Auch wenn 
array[2] bei ihm auf einen Zeiger hinausläuft ist dieser Zeiger dennoch 
nirgends gespeichert, sondern berechnet.

Wenn man die Dinger vollständig durchindiziert besteht zwischen Arrays 
von Arrays in C und mehrdimensionalen Arrays in Pascal nur ein 
syntaktischer Unterschied, nicht aber im Code. In einem solchen 
Zusammenhang tauchen Zeiger nur auf, wenn man sich die Mühe macht, die 
Adressarithmetik des Compilers nachzuvollziehen. Ich habe folglich kein 
Problem damit, diese Dinger auch in C als mehrdimensionale Arrays zu 
bezeichnen.

Und egal wie man das nennt, seine Zugriffe wie array[5][2] werden vom 
Compiler auf konstante Adressen fertig berechnet (weil statisch). Zeiger 
tauchen da nirgends real auf und werden schon garnicht irgendwo 
gespeichert.

von Mehr ist mehr (Gast)


Lesenswert?

Guten Abend,

Ich hatte hier gerade doch noch einen ziemlich großen Denkfehler drin.

Die Funktion PWM ist zur Berechnung von g_byte da. g_byte wird nach 
jedem Durchlauf der Funktion per SPI an 3 Schieberegister gesendet. 
Somit ist g_byte die Darstellung meiner PWM auf dem Schieberegister.

Allerdings dauert die Berechnung ewig. Mein Gedankengang hinter der 
Funktion PWM ist:

Ich lasse eine Variable i von 0 bis 255 hochzählen. Wenn ich kleiner als 
ein Wert in einer PWM-Tabelle bin, dann wird ein Bit in g_byte auf 1 
gesetzt, andernfalls auf 0.

Da ich allerdings 18 PWM-Kanäle habe, muss ich jedes mal alle 18 Werte 
in der Tabelle nachgucken und dementsprechend meine Bits in g_byte 
setzen. Und das dauert ewig lange.

Vorher hab ich mich echt mistig ausgedrückt, ich hoffe, es ist jetzt 
klarer, was ich eigentlich versuche.

Gibts da also einen besseren Weg als der, den ich versuche zu gehen?

von Peter II (Gast)


Lesenswert?

Mehr ist mehr schrieb:
> Da ich allerdings 18 PWM-Kanäle habe

lass uns doch erstmal Rechnen wieviel zeit du hast (oder auch nicht).

Welche PWM Frequenz braucht du?
Müssen immer 18 (oder sogar mehr) daten per SPI rausgeschoben werden und 
wie schnell
  -> wie lange dauert alleine das senden?
Welche Auflösung willst du haben (8bit?)

ich kann mir vorstellen das es sehr knapp wenn nicht sogar unmöglich 
ist.

von Mehr ist mehr (Gast)


Lesenswert?

Ok, überschlagsrechnung:

120 Hz, damits flimmerfrei ist. 8 Bit PWM, weils dadrunter keinen Sinn 
macht.

Also müsste ich 120 mal bis 256 zählen. Das heißt, die Funktion PWM 
müsste 30600 mal pro Sekunde durchlaufen, richtig? Bei einem Takt von 8 
MHz / 30600 = 261 Takte, die die Funktion dauern darf, wenn ich sonst 
nichts nebenher tue.

Klingt ziemlich wenig... Was braucht eine if-Abfrage denn so?

von Peter II (Gast)


Lesenswert?

du hast das SPI vergessen, kannst du denn überhaupt so schnell die daten 
senden?

du muss ja 120 mal Pro sekunde alle 18bits raussenden? Ich weiss ja 
nicht was du da noch an overhead hast.

von Mehr ist mehr (Gast)


Lesenswert?

SPI sendet ja immer ein ganzes Byte und ich brauche alle 3. Ausserdem 
lass ich das schon auf schnellster Geschwindigkeit, also 4 Mhz laufen. 
Da ist nichts mehr zu holen.

Gerechnet habe ich 24 Bit * 120 = 2880 Bit, die ich bei 4 Mhz sende. Das 
sollte eigentlich kein Problem sein, oder?

Und, lang ists her, so ein alter Röhrenfernseher hat mit 50 Hz 
Bildwiederholung gearbeitet, oder nicht? Dann wäre ich nämlich bei 625 
Takten, die ich Zeit hätte. Und das ist wieder ein ganz anderer Schnack.

von Peter II (Gast)


Lesenswert?

Mehr ist mehr schrieb:
> Gerechnet habe ich 24 Bit * 120 = 2880 Bit, die ich bei 4 Mhz sende. Das
> sollte eigentlich kein Problem sein, oder?

jetzt musst du aber noch * 256 rechnen weil du ja eine PWM machen 
willst.


also 24bit  120  256 = 737280bit/s  (müsste noch nachbar sein, wird 
aber eng) Immerhin muss du ja auch jedes byte in der ISR senden.

ich würde die berechnung der zu senden byte gleich an der stelle machen 
wo du sie senden willst. Also erst ein byte senden und dann das nächste 
byte berechnen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wozu dient tabelle? Könnte der Wert, der von tabelle ausgelesen
wird, nicht direkt schon in array stehen? Dann würde man sich den
Array-Zugriff sparen. Da die Zugriffe auf array und g_byte aus den
Gründen, die A. K. oben genannt hat, de facto gar keine Array-Zugriffe
sind, werden damit sämtliche Array-Zugriffe eliminiert.

Warum hat g_byte 6 Elemente, von denen jeweils nur 3 Bits genutzt
werden? Ich dachte, du wolltest per SPI 24 Bits hinausschicken, von
denen 18 belegt sind. Da würde es sich doch eher anbieten, g_byte mit
3 Elementen zu dimensionieren und in jedem Element 6 Bits zu nutzen.

Da in jedem Aufruf von PWM sämtliche relevanten Bits der Elemente von
g_byte aktualisiert werden, kannst du erst alle Bits löschen und dann
diejenigen setzen, bei denen die Bedingung erfüllt ist. Dadurch ersparst
du dir die viele Else-Zweige.

Das Ganze würde dann etwas so aussehen:
1
uint8_t array[6][3];
2
uint8_t g_byte[3];
3
4
void PWM(void) {
5
  uint8_t static i;
6
  uint8_t byte;
7
8
  byte = 0;
9
  if (i < array[0][0])
10
    byte |= 1 << 0;
11
  if (i < array[1][0])
12
    byte |= 1 << 1;
13
  if (i < array[2][0])
14
    byte |= 1 << 2;
15
  if (i < array[3][0])
16
    byte |= 1 << 3;
17
  if (i < array[4][0])
18
    byte |= 1 << 4;
19
  if (i < array[5][0])
20
    byte |= 1 << 5;
21
  g_byte[0] = byte;
22
23
  byte = 0;
24
  if (i < array[0][1])
25
    byte |= 1 << 0;
26
  if (i < array[1][1])
27
    byte |= 1 << 1;
28
  if (i < array[2][1])
29
    byte |= 1 << 2;
30
  if (i < array[3][1])
31
    byte |= 1 << 3;
32
  if (i < array[4][1])
33
    byte |= 1 << 4;
34
  if (i < array[5][1])
35
    byte |= 1 << 5;
36
  g_byte[1] = byte;
37
38
  byte = 0;
39
  if (i < array[0][2])
40
    byte |= 1 << 0;
41
  if (i < array[1][2])
42
    byte |= 1 << 1;
43
  if (i < array[2][2])
44
    byte |= 1 << 2;
45
  if (i < array[3][2])
46
    byte |= 1 << 3;
47
  if (i < array[4][2])
48
    byte |= 1 << 4;
49
  if (i < array[5][2])
50
    byte |= 1 << 5;
51
  g_byte[2] = byte;
52
53
  i++;
54
}

Ohne den CALL und RET-Befehl braucht die Funktion 104 Taktzyklen.

Statt die 3 Bytes erst in g_byte zu schreiben, kannst du sie auch
gleich an das SPI schicken. Damit sparst du noch ein paar Zyklen.

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.