Forum: Mikrocontroller und Digitale Elektronik Buffer optimiert in C kopieren


von Wulf D. (holler)


Lesenswert?

Vermutlich simpel, aber komme gerade nicht drauf: es geht um 
rechenzeit-optimiertes Kopieren von Buffern.

Gegegeben ist ein größerer uint8_t-Buffer der prozesssicher in einem 
Mehrkern-uC als Ringbuffer mit 16 Bit-Wörter gefüllt wird. Zyklisch 
werden bei ausreichendem Füllstand wieder 16-Bit-Werte entnommen.

Stark vereinfacht und initial befüllt in etwa so:
1
uint8_t buf_uint8[8]={1,2,3,4,5,6,7,8};
2
int16_t buf_int16[4];
3
4
for  (uint16_t i=0; i<4; i++)
5
     {
6
     buf_int16[i]= (int16_t *)(buf_uint8[i*2] + (buf_uint8[i*2+1]<<8)); //?
7
     printf("buf_int16[%d]= %d \n",i, buf_int16[i]);
8
     }

So wie im Beispiel funktioniert die Sache, aber geht das nicht 
effizienter ohne schieben und addieren? Also direkt aus dem 8 Bit Buffer 
zwei Wörter zu einem 16 Bit Wort kopieren?

Muss man den Endian-Mode beachten, letztlich läuft das auf einem ARM.
Wie sähe der C-Syntax dafür aus?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

memcpy()

Gruss
WK

von G. K. (zumsel)


Lesenswert?

Dergute W. schrieb:

> memcpy()

Aber nur wenn der Bytesex richtig ist.

von Wulf D. (holler)


Lesenswert?

Danke, auf dem PC funktioniert memcpy(&buf_int16, &buf_uint8, 8) schon 
mal.

von Andras H. (andras_h)


Lesenswert?

Alignment aufpassen! Wenn man Zugriffe mit 16 oder 32 bit grösse macht, 
etwarten viele Architekturen auch addressen die 16 oder 32bit aligned 
sind. Aus einer 8bit char buffer kann man deswegen nicht immer gleich 32 
bit auskopieren. Klar wenn alignment stimmt geht.

Sonst ein memcpy müsste eigentlich das können. Gut implementationen 
sehen nach ob man am anfang oder ende noch 8bit weise kopiert. 
Dazwischen aber 32 bit weise. Teilweise gibt es architekturen wo der RAM 
eh 64bit breit ist (oder noch breiter) wegen ECC. Da kann man richtig 
gas geben, denn man kann 64bit weise kopieren.

von Daniel A. (daniel-a)


Lesenswert?

Andras H. schrieb:
> Teilweise gibt es architekturen wo der RAM
> eh 64bit breit ist (oder noch breiter) wegen ECC. Da kann man richtig
> gas geben, denn man kann 64bit weise kopieren.

Das landet doch sowieso zuerst in den CPU Caches, da wird der RAM 
vermutlich keinen unterschied sehen.

von Wulf D. (holler)


Lesenswert?

Danke für die Hinweise.
Hat auch auf dem ARM funktioniert. Das Alignment müsste man vermutlich 
beim Anlegen des Buffers prüfen?
Wäre jetzt davon Ausgegangen, dass der Compiler das nicht schräg über 
Wortgrenzen legt.

Geht um einen RP2350, also RPI Pico2. Ist 32Bit.

von Daniel A. (daniel-a)


Lesenswert?

Wulf D. schrieb:
> Das Alignment müsste man vermutlich beim Anlegen des Buffers prüfen?

Mei memcpy kannst du das alignment ignorieren, das braucht man nur bei 
hacks wie z.B. `uint8_t x[2]; *(uint16_t*)x = 123;`.

Aber ja, beim anlegen eines Arrays kann man das auch mitgeben, mittels 
alignas: https://en.cppreference.com/w/c/language/alignas.html

von Falk B. (falk)


Lesenswert?

Wulf D. schrieb:
> Gegegeben ist ein größerer uint8_t-Buffer der prozesssicher in einem
> Mehrkern-uC als Ringbuffer mit 16 Bit-Wörter gefüllt wird. Zyklisch
> werden bei ausreichendem Füllstand wieder 16-Bit-Werte entnommen.

Warum dann nicht gleich ein int16_t Array anlegen? Dann stimmt auch das 
Alignment.

von Matthias X. (current_user)


Lesenswert?

Brauchst du wirklich eine kopie, oder reicht dir der Zugriff auf 8Bit 
bzw. 16Bit Ebene? Wenn es Keine Kopie sein muss, dann kannst du auch ein 
Union anlegen.

Falls es doch eine Kopie sein soll:
- kopieren via Pointer (Dann gleich mit maximaler Bitbreite 
kopieren(32Bit?)
- DMA (Falls der Prozessor das kann)

von Klaus H. (klummel69)


Lesenswert?

Wulf D. schrieb:
> Danke, auf dem PC funktioniert memcpy schon mal.

Auch auf allen anderen Architekturen sollte man nichts eigenes machen.
Die Compiler optimieren meist besser als man selber.
Wenn du als Optimierung -O3 eingestellt hast, wird er vermutlich 
Intrinsic Funktionen nutzen.

Das einzige auf was man selber achten sollte ist das Alignment wie oben 
beschrieben.

von Daniel A. (daniel-a)


Lesenswert?

Und den byte-order, in dem fall hier. Aber heute ist ja sowieso alles 
little-endian.

von Norbert (der_norbert)


Lesenswert?

Wulf D. schrieb:
> Gegegeben ist ein größerer uint8_t-Buffer der prozesssicher in einem
> Mehrkern-uC als Ringbuffer mit 16 Bit-Wörter gefüllt wird. Zyklisch
> werden bei ausreichendem Füllstand wieder 16-Bit-Werte entnommen.

Wulf D. schrieb:
> Geht um einen RP2350, also RPI Pico2. Ist 32Bit.

DMA. Der Pico hat so viele Kanäle davon, dass man die sogar gefahr- und 
kostenlos nutzen kann.

von Wulf D. (holler)


Lesenswert?

Sorry für die verzögerte Reaktion, komme heute nur sehr sporadisch 
Zugriff zum Forum. Irgendwas hakelt.

Endian Mode dürfte in der Tat egal sein, da ja alles auf einem Prozessor 
läuft. Kann doch eigentlich nur beim Transport über Grenzen schief 
gehen. Denke, dass ARM big endian ist. Und x86 little endian. Jedenfalls 
sah die Byteanordnung im Testprogramm auf dem PC so aus.

Die Ringbuffer-Entnahmemethode ist so deklariert
1
RING_BUFFER_SIZE_TYPE ring_buffer_pop(ring_buffer_t *ring_buf, uint8_t* vals, RING_BUFFER_SIZE_TYPE maxvals);
RING_BUFFER_SIZE_TYPE ist uint16_t, uint8_t* vals mit dem eigentlichen 
Bufferspeicher verbunden. ring_buffer_t eine Struktur.

Letztlich gab es noch eine einfachere Variante die 16-Bit-Wörter aus dem 
8-Bit Bufferspeicher zu ziehen, aber das konntet ihr mangels Infos nicht 
ahnen und ich bin mir nicht sicher ob der (uint8_t *)-Cast auf ein 
int16_t-Array guter Stil ist. Funktionieren tut es, decodebuf enthält 
die gewünschten 16Bit-Werte.
1
int16_t decodebuf[1920];
2
ring_buffer_pop(&ring_buffer, (uint8_t *)decodebuf, 1920*2);

von Wulf D. (holler)


Lesenswert?

Norbert schrieb:
> DMA. Der Pico hat so viele Kanäle davon, dass man die sogar gefahr- und
> kostenlos nutzen kann.

eh, guter Tipp. Vier werden bereits benutzt, aber da ist noch Luft. 
Versuche ich beim Optimieren.
Leider ist das SDK qualitätsmäßig so mies, dass man den Debugger nicht 
nutzen kann.  stdio_init_all(); stürzt zuverlässig ab.

Normales Compilieren und Laden geht, aber ohne Debugger äußerst lästig.

von Rbx (rcx)


Lesenswert?

Wulf D. schrieb:
> So wie im Beispiel funktioniert die Sache, aber geht das nicht
> effizienter ohne schieben und addieren? Also direkt aus dem 8 Bit Buffer
> zwei Wörter zu einem 16 Bit Wort kopieren?

Müsste mit logischen Operationen gehen.
Das Hintergrundproblem ist hier die Umwandlung des Datenformats.
Schaut man sich ein wenig um, könnte man sich noch auf memcpy stürzen 
(schneller machen), bzw. sich auf das Allignment konzentrieren, und 
memcpy weitgehend weglassen.
Teilweise sind die angesprochenen Sachen hier sehr Hardware- bzw. 
Compilerabhängig.

https://stackoverflow.com/questions/17498743/how-does-the-internal-implementation-of-memcpy-work
hier verlinkt noch (was zum Downloaden):
http://www.danielvik.com/2010/02/fast-memcpy-in-c.html

https://stackoverflow.com/questions/6842363/why-are-structures-copied-via-memcpy-in-embedded-system-code

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.