Hallo Zusammen,
ich arbeite mit einem Cyclone V von Altera und Yocto Linux, dass auf dem
ARM Prozessor läuft.
Im FPGA habe ich ein RAM erzeugt, auf dass ich von Linux Seite her
beschreibe. Der Inhalt des RAMs wird dann in Form einer Grafik (256x128)
einem Video überlagert.
Das funktioniert auch tadellos, nur extrem langsam. Man kann sehen wie
sich die Grafik zeilenweise im Bild aufbaut.
Nach einiger Recherche bin ich zum dem Schluss gekommen, dass es an der
Software liegen muss, genauer gesagt an der Funktion, welche die Daten
ins RAM schreibt:
Kann man das als Funktion so schreiben? Oder muss man hier anders
(besser) vorgehen?
Gibt es Optionen für den GNU C++ Compiler um solche Schleifen zu
optimieren?
Ich bin für jeden Ratschlag dankbar! :-)
Michael schrieb:> Dahinter versteckt sich eine Funktion die einen Port zum virtuellen> Linux-Speicher öffnet und die Werte auf die physikalische Adresse> schreibt.
Und das für jedes Pixel.
Umbauen!
Vor Beginn der kompletten Pixel Operation die Sache mit dem File-Öffnen
und memory allokieren ereldigen.
Dann kommen deine Schleifen zum Zug, die die Pixel setzen. Und zwar in
die bereits vorhandene memory-map. Innerhjalb der Schfleife passiert im
wesentlichen nur noch ein
1
*((unsignedlong*)virt_addr)=writeval;
mit jeweils wechselenden Adressen. Aber mehr nicht.
und dann die Abschlussarbeiten, wie File schliessen.
So wie jetzt darfst du dich nicht wundern, wenn das alles saulangsam
ist. Du bürdest ja deinem ARM einen Haufen Sonderarbeit für nichts und
wieder nichts auf! Du schickst ihn bildlich gesprochen, für 50
Beilagscheiben 50 mal ins Lager um jeweils 1 Beilagscheibe zu holen. Das
das dann in Summe 2 Stunden dauert, darf doch nicht verblüffen, während
ein Kunde der mitdenkt, dich nur einmal ins Lager schickt um alle 50
Scheiben zu holen, was dann in 3 Minuten ereldigt ist. Das Problem ist
nicht das greifen der Beilagscheiben. Das Problem ist der Fussweg ins
Lager und wieder zurück, den du in dem einen Fall 50 mal machst und im
anderen Fall nur einmal.
Hallo,
zunächst einmal "Danke" für die Unterstützung!
Stimmt, das ist echt "Wahnsinn" was ich da geschrieben habe. Ich
schreibe erst seit rund einem Jahr C++, aber ich denke das sieht man :-D
Ich habe die Funktion jetzt mal wie folgt umgebaut:
vor der Schleife schreiben? Da ist "address" doch noch gar nicht
bekannt? Ich versuche es gleich mal mit einem Pointer. Aber wie gesagt,
ich schreibe noch nicht lange C++.
Gruß,
Michael
Michael schrieb:> Wie soll ich> map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE,> MAP_SHARED, fd, address & ~MAP_MASK);> vor der Schleife schreiben? Da ist "address" doch noch gar nicht> bekannt?
In der neuen Version beim ersten Schleifendurchlauf auch nicht. Zwischen
der Deklaration und der Verwendung von address wird kein Wert
zugewiesen.
Das mmap() machst du einfach vor den Schleifen vom ganzen Bereich des
Framebuffers. Es gibt doch keinen Grund den für jeden Zugriff zu ändern.
Michael schrieb:> nicht das was Karl Heinz meinte?>> Wie soll ichmap_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE,> MAP_SHARED, fd, address & ~MAP_MASK);> vor der Schleife schreiben? Da ist "address" doch noch gar nicht> bekannt?
Der Witz bei mmap ist, daß du einen ganzen Block mappen kannst und nicht
nur ein einzelnes Byte. Das mmap jedesmal neu zu machen, ist ungefähr
so, als ob du beim Einlesen einer Datei für jedes einzelne gelesene Byte
die Datei neu auf- und danach wieder zumachst. Klar, daß das wahnsinnig
Performance frisst.
PS: Warum machst du eigentlich dauernd ein fflush(stdout), obwohl du nie
was dorthin schreibst?
Rolf Magnus schrieb:> Das mmap jedesmal neu zu machen, ist ungefähr> so, als ob du beim Einlesen einer Datei für jedes einzelne gelesene Byte> die Datei neu auf- und danach wieder zumachst. Klar, daß das wahnsinnig> Performance frisst.
Ein mmap ist sogar noch viel schlimmer. Das muss das virtuelle Mapping
ändern was viel aufwendiger ist. Nicht unbedingt wegen der
Datenstrukturen die angelegt/modifiziert werden müssen, sondern wegen
der (je nach Prozessor-Architektur) nötigen Cache- und TLB-Flushes.
Wenn die CPU nur noch mit der Geschwindigkeit des Hauptspeichers
arbeiten kann wird es halt wirklich grottig lahm.
Michael Schäfer schrieb:> Gibt es denn eine schnellere Methode als mmap?
Das Problem ist nicht, daß du mmap benutzt, sondern daß du es für jeden
der 32768 zu schreibenden Werte extra aufrufst, statt nur genau einmal
am Anfang.
Mal ein anderer Vergleich: Stell dir vor, du mußt einen Laster mit
Kartons befüllen. Die sinnvolle Variante wäre, den Laster vom Parkplatz
vor die Tür zu fahren, zu öffnen, alle Kartons einzuladen, zumachen und
losfahren.
Was du stattdessen machst: Du fährst den Laster vom Parkplatz her,
machst ihn auf, lädst genau einen Karton ein, machst ihn wieder zu und
fährst zurück zum Parkplatz. Dann fährst du ihn wieder vor die Tür,
machst den Laster wieder auf und lädst den nächsten Karton ein, wieder
zu, zurück zum Parkplatz, u.s.w.
Die Aktion "Laster herfahren und aufmachen" ist aufwendig, wird aber nur
einmal benötigt, also macht man sie auch nur einmal. So ist das auch mit
mmap.
Das Problem hierbei ist:
Da "adress" vor der Schleife lediglich die Start-Adresse (HISTR) des RAM
enthält, wird in der Schleife die ganze Zeit nur an diese eine Adresse
geschrieben.
Irgendwie stehe ich gerade total auf dem Schlauch.
Was muss ich hier Wie ändern? Die Adressen müssen doch irgendwo hoch
gezählt werden?
Michael Schäfer schrieb:> //Datenwort wird an Adresse geschrieben> adress = ((HISTR+spalte)+(3*spalte)+(1024*zeile));> virt_addr = map_base + (address & MAP_MASK);> *((unsigned long *) virt_addr) = combined;>> c++;> d++;> e++;> }> }>> //Port zum Speicher wird wieder geschlossen> read_result = *((unsigned long *) virt_addr);> if(munmap(map_base, MAP_SIZE) == -1) FATAL;> close(fd);> [/c]> Das Problem hierbei ist:> Da "adress" vor der Schleife lediglich die Start-Adresse (HISTR) des RAM> enthält, wird in der Schleife die ganze Zeit nur an diese eine Adresse> geschrieben.
Echt?
Und was ist das hier?
1
//Datenwort wird an Adresse geschrieben
2
adress=((HISTR+spalte)+(3*spalte)+(1024*zeile));
3
virt_addr=map_base+(address&MAP_MASK);
Das Problem ist eher, dass du durch rumdoktern versuchst, den Code
irgendwie zum laufen zu bringen, anstatt durch systematisches nachdenken
und ergründen, wie dieses File-mapping eigentlich auf Codeebene
funktioniert bzw. was der Originalcode eigentlich gemacht hat und warum
er es genau so gemacht hat.
Gibt es einen Grund dafür, dass du hier
mittels Offset nur einen Teil des 'files' in den Speicher mappen lässt?
So groß ist deine Bitmap mit den paar Pixeln dann auch wieder nicht,
dass das ein enorme Speicherbelastung wäre, wenn da mal 32k kurzzeitig
benutzt werden.
Lass die das komplette File mappen und greif nicht segmentiert zu. (Es
sei denn es gibt einen technischen Grund dafür. Was ich allerdings nicht
weiß).
Dazu siehst du dir mal die Doku zu mmap an, was denn die einzelnen Teile
eigentlich bedeuten.
Wie groß ist bei dir eigentlich MAP_SIZE bei dir eingestellt?
Obwohl: Im Grunde wäre mir das wurscht, weil ich diesen ganzen
Segmentiermechanismus mit MAP_SIZE und MAP_MASK sowieso rauswerfen
würde. D.h. wenn möglich. Bei 32k Bitmaps, so wie hier, lohnt das alles
nicht. Wenn du Bitmaps mit 4096 mal 8192 Pixel, True Color, bearbeiten
musst, die die Megabytes nur so auffressen, dann sieht das anders aus.
Aber für die popel-Bitmap mit ihren 32k lohnt sich das doch nicht. Wenn
der Speicher so knapp ist, dass diese 32k ein Problem darstellen, dann
hast du ganz andere, dringendere Probleme.
Was mich auch allerdings auch Angst und Bang werden lässt, das ist dein
Umgang mit Datentypen in diesem Code.
Wieso wird hier eigentlich
1
*((unsignedlong*)virt_addr)=combined;
auf unsigned long gecastet?
combined ist ein int (sollte das nicht eigentlich ein unsigned int sein,
wenn ich mir so ansehe, wie du die Farbinformation zusammenstellst?).
Ist auf deinem System die sizeof(int) identisch zur sizeof(unsigned
long)? Wenn ja (was durchaus sein könnte), dann ist das zumindest
grenzwertig maximal-verwirrend.
Die Byteinformationen legst du in einen char, der eigentlich ein
unsigned char sein sollte. char ist für Textverarbeitung. Machst du hier
Textverarbeitung? Nein! Damit stellt sich die Frage nach einem char gar
nciht mehr und die Wahl kann nur noch zwischen 'signed char' und
'unsigned char' gefällt werden. Da reine Bytes, so wie hier,
normalerweise nicht als vorzeichenbehaftet angesehen werden, ist 'signed
char' auch aus dem Rennen und es bleibt ein 'unsigned char' übrig. So
wie eigentlich meistens, wenn man mit Bytes operiert.
Du gehst mir da überall viel zu leichtfertig mit den Datentypen um. Ein
void* Pointer ist schon mal extrem verdächtig.
Die Adressberechnung mit den 3*spalte ist auch merkwürdig, könnte aber
stimmen, je nachdem wie das FPGA das haben will. Auf jeden Fall ist es
nicht logisch, dass pro Pixel 3 Bytes benutzt werden, vor allen Dingen
mit dem Hintergrund, dass hier
1
intcombined=(MSB<<24)|(B<<16)|(G<<8)|R;
ja offenbar etwas mit 4 Bytes (die anscheinend ja alle wichtig sind)
zusammengebaut werden, und eine komplette Pixelzeile 1024 Bytes lang
ist. Dein Display hat pro Zeile 256 Pixel. Pro Pixel 3 Bytes würden 768
Bytes pro Zeile machen. Rechnet man aber mit 4 Bytes pro Pixel (was mit
dem restlichen Code logisch wäre), dann ergeben sich die 1024. Dann ist
allerdings aber die Adressberechnung mit 3*spalte wieder unlogisch.
Wie gesagt: es wäre unter umständlich möglich, dass das alles stimmt,
wenn das FPGA das alles wieder irgendwie seltsam auseinanderdröselt,
weil es auf Displays bis hinauf zu 341 Pixel pro Zeile ausgelegt ist
(was auch eine seltsame Zahl darstellen würde). Aber um ehrlich zu sein,
denke ich eher, dass du dich da komplett vertan hast und noch nicht
verstanden hast, wie die Adressierung eigentlich funktioniert.
Oops. das ist ja eine RGB Sache. Also braucht ein 256*128 Pixel Bild
nicht 32k, sondern das 3(4?)-fache davon. Aber selbst 128k sind bei den
Speichergrößen, die du brauchst um Linux fahren zu können, immer noch
kein Thema.
@Karl Heinz:
Der Tipp mit der MAP_SIZE war gold wert. Die war auf 4096UL definiert.
Ich hab Sie nun so groß definiert, wie die Grafik ist. Folgende Änderung
war notwendig:
1
#define HIST_SIZE 131072UL
2
#define HIST_MASK (HIST_SIZE - 1)
Der Geschwindigkeitsunterschied ist unglaublich!
Vielen Dank für die anregende Kritik!