Variablenzugriffe mal langsam mal schnell.



  • Hallo Fachleute.

    Habe hier ein echt merkwürdiges Problem: Ich programmiere mit VS2005 und C++ eine App (Grafik) für ein CE-Gerät. Dazu befülle ich ein PixelArray, welches ich dann mit CopyMemory auf eine DDraw-Surface kopiere. Soweit so gut, das klappt alles...

    Nun folegndes problem: Mein Array hat UCHAR als Wert für die einzelnen Farben der Bytes. In einer Schleife durchlaufe ich jeden Pixel (480 * 272) und setze Farben.

    Setze ich ein feste Farbe

    BUFFER[_createmode][pixel].b=200; 
    BUFFER[_createmode][pixel].g=200; 
    BUFFER[_createmode][pixel].r=200;
    

    geht das in 50 ms

    Setze ich die Farben mit einer Variable

    BUFFER[_createmode][pixel].b=(UCHAR)b; 
    BUFFER[_createmode][pixel].g=(UCHAR)g; 
    BUFFER[_createmode][pixel].r=(UCHAR)r;
    

    (egal ob ich =r oder =(UCHAR)r nehme), dauert es fast eine Sekunde.
    auch egal, ob r und g und b int sind oder schon UCHAR)

    Hat jemand evtl eine schöne Idee, was das nu wieder sein könnte???

    Danke Euch schonmal im Voraus



  • Naja, die Zuweisung von konstanten macht es dem Compiler leichter, was zu optimieren. Allerdings kann ich mir nicht vorstellen, dass das in deinem Fall einen so großen Unterschied macht. Zeig doch mal die ganze Schleife.



  • Release-Mode ausgewählt?



  • Hi. Ja, der Code ist Releasemode

    Hier mal der Code:

    typedef struct rgbz{
    	UCHAR b;
    	UCHAR g;
    	UCHAR r;
    	UCHAR z;
    } rgbz;
    
    rgbz *BUFFER[200];
    
    //Zeichnet ein Rechteck ohne Verlauf oder mit Verlauf, dann aber mit optisch optimiertem Verlauf.
    //LEFT RIGHT TOP BOTTOM Dimensionen
    //type uninterssant (Interpretation der Dimensionsangabe
    //Ro Go Bo Farbanteile des Rechtecks oben
    //Ru Gu Bu Farbanteile des Rechtecks unten (wenn -1, dann kein Verlauf, Rechteck hat eine Farbe)
    void PAINTRECT(int LEFT,int RIGHT,int TOP,int BOTTOM,int type,int Ro,int Go,int Bo,int Ru,int Gu,int Bu) {
    	int x,pixel,y,yy,ldiff,lmax=12,maxi=0,mit=1;
    	UCHAR r,g,b;
    	float proz,proz100;
    	float diffou;
    
    	//Hier wird der Farbunterschied zwischen oben und unten gemessen und die maximale "optische Anpassung" ermittelt
    	maxi=MaxDiff(maxi,Ro,Ru);
    	maxi=MaxDiff(maxi,Go,Gu);
    	maxi=MaxDiff(maxi,Bo,Bu);
    	if (maxi > 200) {
    		lmax=2;
    	}
    	if (maxi > 150) {
    		lmax=4;
    	}
    	if (maxi > 100) {
    		lmax=8;
    	}
    	if (Ru==-1 || Gu == -1 || Bu == -1) { //wenn es keinen Verlauf gibt, bekommt jeder Pixel die gleiche Farbe
    		r=Ro;						      //das dauert 35 ms
    		g=Go;
    		b=Bo;
    		for(x=LEFT;x<=RIGHT;++x){
    			for(y=TOP;y<=BOTTOM;++y){
    				pixel=x + y * 480; 
    				BUFFER[_createmode][pixel].b=b; 
    				BUFFER[_createmode][pixel].g=g; 
    				BUFFER[_createmode][pixel].r=r;
    			}
    		}
    	} else {
    		//hier wird der Verlauf berechnet.
    		//dazu wird zuerst, entsprechend der aktuellen Zeile der Prozentsatz der Farbanteile oben zu unten berechnet
    		//da es sich um ein Display mit nur 65535 Farben handelt, gibt es harte Breaks in den Farben (immer 6/7
    		//Zeilen eine Farbe, dann die nächste
    		//deshalb werden die Farben verwischt. Dazu wird nicht der Prozentsatz für die wirklich aktuelle Zeile 
    		//genommen, sondern es wird per "Zufall" eine gewisse Anzahl Zeilen (bis zu lmax) nach oben/unten gerutscht
    		//und der Prozentwert für diese Zeile berechnet. Dadurch gibt es auf dem Gerät einen tollen Verlauf
    		diffou=BOTTOM - TOP;
    		for(x=LEFT;x<=RIGHT;++x){
    			for(y=TOP;y<=BOTTOM;++y){
    				ldiff=(rand()%lmax) - (lmax/2); //Ermittlung der zufälligen Zeile
    				yy=y+ldiff;
    				if (yy<TOP) {
    					yy=TOP;
    				}
    				if (yy>BOTTOM) {
    					yy=BOTTOM;
    				}
    				if (diffou == 0) {
    					proz=100;
    				} else {
    					//proz=(float)(((float)yy-TOP)/((float)diffou));
    					proz=((yy-TOP)/(diffou));
    				}
    				proz100=1-proz;
    				r=(Ru * proz + Ro * proz100);
    				g=(Gu * proz + Go * proz100);
    				b=(Bu * proz + Bo * proz100);
    
    				pixel=x + y * 480; 
    				//Version 2 das hier dauert 840 ms
    				/*BUFFER[_createmode][pixel].b=b; 
    				BUFFER[_createmode][pixel].g=g; 
    				BUFFER[_createmode][pixel].r=r;*/
    
    				//Version 3 das hier dauert 420 ms
    				BUFFER[_createmode][pixel].b=b; 
    				BUFFER[_createmode][pixel].g=b; 
    				BUFFER[_createmode][pixel].r=b;
    
    				//Version 4 das hier dauert 840 ms
    				/*BUFFER[_createmode][pixel].b=(UCHAR)b; 
    				BUFFER[_createmode][pixel].g=(UCHAR)g; 
    				BUFFER[_createmode][pixel].r=(UCHAR)r;*/
    
    				//Version 5 das hier dauert 50 ms
    				/*BUFFER[_createmode][pixel].b=200; 
    				BUFFER[_createmode][pixel].g=200; 
    				BUFFER[_createmode][pixel].r=200;*/
    
    				//Version 6 das hier dauert 50 ms
    				/*int ttt=200;
    				BUFFER[_createmode][pixel].b=ttt; 
    				BUFFER[_createmode][pixel].g=ttt; 
    				BUFFER[_createmode][pixel].r=ttt;*/
    			}
    		}
    	}
    }
    

    Kann es evtl. sein, dass der Optimierer merkt, dass in Version 5 und 6 die Variablen r g und b gar nicht benutzt werden und somit auch der Weg zur Berechnung der Variablen gar nicht kompiliert wird? (In Version 3 brauchen wir die Hälfte der Zeit, weil wir nur b benutzen und nicht r und g. Hmmm...

    Auf jeden Fall mal vielen Dank für die Zeit, die Ihr Euch zum Lesen genommen habt...

    DANKÖÖÖ



  • Wie hast du die Zeiten gemessen?
    Da ist generell noch sehr viel Potenzial zum optimieren, weil vieles unnötig oft berechnet wird.
    Hier ist übrigens C++, C ist nebenan ontopic.



  • Hallo.

    In der DLL wird das Skript auf diese Weise aufgerufen:

    int start;
    
    	//Hintergrund
    	start=GetTickCount();
    	PAINTRECT(1,480,1,272,1,Backcolor.R,Backcolor.G,Backcolor.B,255,255,255);
    	_Out1=GetTickCount()-start;
    

    _Out1 wird dann an die aufrufende Funktion (das wäre dann CSharp) als Rückgabewert zurückgeliefert. Verfälschungen der Zeitmessung würde ich daher mal ausschließen.

    Eigentlich ist das ein C++ - Projekt, sieht der Code eher nach C aus? Warum?

    Wenn ma seinen eigenen Code sieht, fallen einem so welche Sachen nicht auf (unnötige Berechnungen). Was wäre das zum Beispiel?

    DANKÖÖÖ


  • Mod

    hvsecpp schrieb:

    Eigentlich ist das ein C++ - Projekt, sieht der Code eher nach C aus? Warum?

    Weil er sich mit einem reinen C Compiler übersetzen ließe.



  • hvsecpp schrieb:

    Eigentlich ist das ein C++ - Projekt, sieht der Code eher nach C aus? Warum?

    Das ist astreiner C-Code. typedef struct schreibt man in C++ nicht. Variablen alle im voraus am Anfang der Funktion deklarieren tut man auch nicht. Das ist zwar nicht falsch, aber unüblich.
    Deine Konventionen zur Benennung sind auch ein bisschen komisch; du scheinst eher nach Zufall zu bestimmen, was du GROSS schreibst? Naja, Geschmackssache.

    Wenn ma seinen eigenen Code sieht, fallen einem so welche Sachen nicht auf (unnötige Berechnungen). Was wäre das zum Beispiel?

    Ein einfaches Beispiel:

    for(x=LEFT;x<=RIGHT;++x){
                for(y=TOP;y<=BOTTOM;++y){
                    pixel=x + y * 480;
                    BUFFER[_createmode][pixel].b=b;
                    BUFFER[_createmode][pixel].g=g;
                    BUFFER[_createmode][pixel].r=r;
                }
            }
    

    ist so vermutlich viel schneller:

    for(y = TOP; y <= BOTTOM; ++y){ // zeilen nach außen wg. caching
                unsigned yoffset = y * 480; // muss nur für jede zeile berechnet werden
                rgbz* line = &BUFFER[_createmode][yoffset + LEFT]; // pointer auf aktuelle zeile holen             
                for(x = LEFT; x <= RIGHT; ++x){
                    line->r = r;
                    line->g = g;
                    line->b = b;
                    ++line;
                }
            }
    

    Schnell ohne zu testen hingeschrieben, evtl. noch kleine Fehler drin.

    Edit: generell kommt mir der Ansatz mit der Struktur für jeden Pixel etwas anrüchig vor. Da gibt es Padding zu beachten, etc. Ich weiß auch nicht wie gut der Compiler da optimieren kann. Wenn es wirklich schnell werden soll, würde ich ein rohes Array nehmen, bzw. einen vector, und da dann eher gleich u32 reinpacken und die Kanäle von Hand zusammen mischen. Das macht dann auch evtl. SIMD-Optimierungen einfacher.



  • ohh, das wusste ich noch net mal ;-((

    das kommt davon, wenn man C++ lernt anhand eines Programmes, welches ganz offensichtlich irgendwann mal so halb von C auf C++ portiert wurde ;-((

    Der Grund für das Pixel-Array in dieser Form ist, dass das genau die Struktur ist, wie eine DirectDraw-Surface aufgebaut ist. Wenn ich nämlich das Array per CopyMemory direkt auf das Surface-Handle kopiere, habe ich einen irre schnellen und flimmerfreien Bildaufbau OHNE Doublebuffering.(Das Programm ist nur für eine ganz bestimmte Hardware mit WinCE gedacht, und bei der ist das schnellste Weg zum Bildaufbau/Update. - Zumindest was ich bisher gefunden habe, bin da aber für ganz neue Ideen sehr aufgeschlossen...)

    Variablen-Benennungen ist ein Chaos - da gebe ich Dir zu 100% Recht ...

    Mit Sicherheit hast Du auch Recht, dass ich an der von Dir genannten Stelle noch ein bisschen optimieren könnte. Aber die Versionen, die 35 oder 50 ms brauchen, machen das ja genauso. Wenn ich hier nochmal 20 ms sparen könnte, wäre ich bei den Versionen, die 840 ms brauchen, aber immer noch bei 820 ms. Ich frage mich, wo diese fast 800 ms herkommen, die diese Versionen mehr Zeit brauchen.

    Hach ist das vertrackt ;-((



  • hvsecpp schrieb:

    Mit Sicherheit hast Du auch Recht, dass ich an der von Dir genannten Stelle noch ein bisschen optimieren könnte. Aber die Versionen, die 35 oder 50 ms brauchen, machen das ja genauso.

    Äh, nein, tun sie nicht. Alleine schon dass du spalten- statt zeilenweise über die Daten läufst, dürfte hier viel ausmachen.

    Wenn ich hier nochmal 20 ms sparen könnte, wäre ich bei den Versionen, die 840 ms brauchen, aber immer noch bei 820 ms.

    Falsch. Wenn die 35 ms-Version um 20ms schneller würde, wärst du bei 40% der ursprünglichen Laufzeit. Wenn die 840 ms im gleichen Maße schrumpfen, wären daraus 330 ms geworden.

    hvsecpp schrieb:

    Ich frage mich, wo diese fast 800 ms herkommen, die diese Versionen mehr Zeit brauchen.

    Schreib mal den Code nach dem aufgezeigten Schema um und miss dann nochmal.



  • Habe die Prozentberechnungen, die ich pro Pixel gemacht habe, als den Schuldigen für den Zeitverlust identifiziert.

    Habe das Ganze jetzt umgestellt auf Zeilenweise.
    Habe dadurch für jede ZEILE anstatt für jeden Pixel meine Prozentberechnungen gemacht. Um die Zufälligkeiten mit einbeziehen zu können, habe ich ein Array mit lmax verschiedenen Prozentwerten befüllt, und pro Pixel wähle ich jetzt nur noch einen Wert aus dem Array zufällig aus. Das Ergebnis (optisch) ist das gleiche, allerdings ohne weitere Optimiereungen (da werde ich mal weiter schauen) komme ich jetzt anstatt 840 ms auf 90. DAS ist doch schon mal gar nicht schlecht ... und da kommt noch bissi mehr, wenn ich weiter optimiere.

    War wie immer sehr erfrischend und hilfreich mit Euch zu diskutieren.

    Vielen Dank und schönes WE.

    Und so sieht mein Code im Moment aus:

    for(y=TOP;y<=BOTTOM;++y){
    			//Zeilenweise
    			for (int hh=0;hh<lmax*2;hh++){
    				yy=y+hh-lmax;
    				proz=((yy-TOP)/diffou);
    				if (proz < 0)  {
    					proz=0;
    				}
    				if (proz > 1)  {
    					proz=1;
    				}
    				proz100=1-proz;
    				r1[hh]=(Ru * proz + Ro * proz100);
    				g1[hh]=(Gu * proz + Go * proz100);
    				b1[hh]=(Bu * proz + Bo * proz100);
    			}
    			for(x=LEFT;x<=RIGHT;++x){
    				ldiff=(rand()%lmax);
    
    				pixel=x + y * 480; 
    				BUFFER[_createmode][pixel].b=b1[ldiff]; 
    				BUFFER[_createmode][pixel].g=g1[ldiff]; 
    				BUFFER[_createmode][pixel].r=r1[ldiff];
    
    			}
    		}
    

Anmelden zum Antworten