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?
Dergute W. schrieb:
> memcpy()
Aber nur wenn der Bytesex richtig ist.
Danke, auf dem PC funktioniert memcpy(&buf_int16, &buf_uint8, 8) schon
mal.
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.
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.
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.
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
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.
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)
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.
Und den byte-order, in dem fall hier. Aber heute ist ja sowieso alles
little-endian.
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.
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);
|
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.
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.
|