Buffer optimiert in C kopieren

#7967918
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?

#7967930
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.

#7967946
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

#7967976
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.

#7967990
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.

#7967994
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);
#7967998
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.

#7968000
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

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