Laufzeitoptimierung
-
vielleicht so:
double HDRI::weight(int zi, int image) { if (image == 0) { int z = imageList[image+1].data[zi]-imageList[image].data[zi]; return z<0 ? 0 : z*(1./(RANGE-1)); // wenn byte vorzeichenlos ist, sonst müssen wir auch gegen RANGE prüfen } else if (image == (NUM_IMAGES-1)) { int z = imageList[image].data[zi]-imageList[image-1].data[zi]; return z<0 ? 0 : z*(1./(RANGE-1)); } else { int z = imageList[image+1].data[zi]-imageList[image-1].data[zi]; return z<0 ? 0 : z*(2./(RANGE-1)); } }
Sinnvoll dürfte auch sein, den Test von image aus der Schleife, die diese Funktion vermutlich aufruft, zu schieben.
-
Danke euch allen für die Beteiligung,
mit euer Hilfe wurde es schon ein Stückchen schneller, aber dennoch ist es noch zu langsam, ich probiere mal den Zugriff auf die imageList anders zu programmieren (direkt über pointer)
Es ist schon richtig so dass es an dieser Funktion liegt, wenn ich nämlich nur diesen Aufruf auskommentiere dann sinkt die Berechnungsdauer auf ein Bruchteil der vorherigen Dauer...
Ich werde dann noch meine Erkenntnis später posten
Gruß
Steffen
-
Steffen82 schrieb:
Es ist schon richtig so dass es an dieser Funktion liegt, wenn ich nämlich nur diesen Aufruf auskommentiere dann sinkt die Berechnungsdauer auf ein Bruchteil der vorherigen Dauer...
du meinst, wenn du die letzte zeile auskommentierst? dann wuerde der compiler viele dinge die davor gemacht wurden auch wegoptimieren.
-
nein ich meinte schon den aufruf...
die weight-Funktion wird in einer schleife ständig aufgerufen...void HDRI::generateOneChannel() { int zv; double weightsum, irad, iradsum, weightZv; for (int x=0; x<PIXELS; x++) { weightsum=0; iradsum=0; for (int i=0 ; i<NUM_IMAGES; i++) { zv = imageList[i].data[x]; weightZv = weight(x, i) * bellWeight(zv); //weightZv = 0.00001; iradsum += (curve[zv]-imageList[i].exposure) * weightZv; weightsum += weightZv; } if (weightsum>0) irad = iradsum / weightsum; else irad = -1000; radMap->data[x] = exp(irad); } }
-
Steffen82 schrieb:
nein ich meinte schon den aufruf...
die weight-Funktion wird in einer schleife ständig aufgerufen...void HDRI::generateOneChannel() { int zv; double weightsum, irad, iradsum, weightZv; for (int x=0; x<PIXELS; x++) { weightsum=0; iradsum=0; for (int i=0 ; i<NUM_IMAGES; i++) { zv = imageList[i].data[x]; weightZv = weight(x, i) * bellWeight(zv); //weightZv = 0.00001; iradsum += (curve[zv]-imageList[i].exposure) * weightZv; weightsum += weightZv; } if (weightsum>0) irad = iradsum / weightsum; else irad = -1000; radMap->data[x] = exp(irad); } }
Dieses Vorgehen dürfte schnell durch die Speicherbandbreite limitiert werden. Die innere Schleife iteriert über Images - das heißt vergleichsweise große Distanzen im Speicher. Du solltest also unbedingt versuchen, die Schleifen zu vertauschen.
-
Bei Bildern bietet sich oft an diese Zeilenweise zu integrieren.
Bildweise ist doof weil man dadurch fast immer das "Accumulator" Bild aus dem 1st Level Cache wirft, und Pixelweise ist doof weil man dadurch eben dauernd springen muss was die "load" Performance total kaputtmacht.Beim Zeilenweise Integrieren passt die Accumulator Zeile meist noch in den 1st Level Cache und sicher in den 2nd Level Cache, und man bearbeitet wenigstens eine Zeile "am Stück", was für wesentlich bessere Performance bei den "loads" führt (Cache, Prefetch).
-
die schleifen zu vertauschen würde wenig sinn ergeben...
es geht hier um die Generierung eines High Dynamic Range Image (HDRI)
die Anzahl der Bilder ist dabei variabel (meistens 3 Stück) und die Anzahl der Pixel ist auch je nach Auflösung (ich hab grad z.B. 1600 x 1200 pixel)
wie könnte es zeilenweise funktionieren?
Ich müsste dann wohl einen Zeiger auf die jeweilige Zeile setzen und denn dann inkrementieren?
Pseudo-code wäre mir hilfreichVielen Dank
-
Steffen82 schrieb:
die schleifen zu vertauschen würde wenig sinn ergeben...
wieso?
double HDRI::weight_first_image(int zi) { int z = imageList[1].data[zi]-imageList[0].data[zi]; return z<0 ? 0 : z*(1./(RANGE-1)); } double HDRI::weight_last_image(int zi) { int z = imageList[NUM_IMAGES-1].data[zi]-imageList[NUM_IMAGES-2].data[zi]; return z<0 ? 0 : z*(1./(RANGE-1)); } double HDRI::weight(int zi, int image) { assert( image != 0 && image != NUM_IMAGES-1 ); int z = imageList[image+1].data[zi]-imageList[image-1].data[zi]; return z<0 ? 0 : z*(2./(RANGE-1)); } void HDRI::generateOneChannel() { boost::scoped_array<double> iradsum(new double[PIXELS]); boost::scoped_array<double> weightsum(new double[PIXELS]); { for (int x=0; x<PIXELS; ++x) { int zv = imageList[0].data[x]; double weightZv = weight_first_image(x) * bellweight(zv); iradsum[x] = (curve[zv]-imageList[0].exposure) * weightZv; weightsum[x] = weightZv; } } for (int i=0; i<NUM_IMAGES; ++i) { for (int x=0; x<PIXELS; ++x) { int zv = imageList[i].data[x]; double weightZv = weight(x, i) * bellweight(zv); iradsum[x] += (curve[zv]-imageList[i].exposure) * weightZv; weightsum[x] += weightZv; } } { for (int x=0; x<PIXELS; ++x) { int zv = imageList[NUM_IMAGES-1].data[x]; double weightZv = weight_last_image(x) * bellweight(zv); iradsum[x] += (curve[zv]-imageList[NUM_IMAGES-1].exposure) * weightZv; weightsum[x] += weightZv; } } static const double eps = exp( -1000 ); for (int x=0; x<PIXELS; ++x) radMap->data[x] = weightsum[x]>0 ? exp(iradsum[x]/weightsum[x]) : eps; }
ungetest und kann noch verbessert werden. hoffe, es haben sich keine Fehler eingeschlichen.
Wie das Ganze für zeilenweise Verarbeitung aussehen kann (eine gute Idee-oder man ignoriert Bildzeilen und beschränkt sich auf Vielfache von Cachezeilen), müsste dann eigentlich auch klar sein.
-
wie könnte es zeilenweise funktionieren?
buf line_buffer = ... for (y = 0 to total_lines) { clear line_buffer; for (i = 0 to total_images) { for (x = 0 to pixels_per_line) { integrate image(i), pixel(x, y) into line_buffer(x) } } write line_buffer to target image; }
Mir ist erst nicht aufgefallen dass die Pixel ja alle hintereinander liegen (ist bei Bildern oft nicht so, da hat man oft "Leerräume" zwischen den Zeilen). Dadurch kannst du ruhig wie camper schreibt eine Fixe Anzahl von Pixeln nehmen statt dich an der Zeilenlänge zu orientieren.
Ich würde sagen alles was in 32KB passt sollte gut sein, das geht bei jeder modernen CPU noch in den 1st Level Cache (die neuen AMD und Intel haben beide 64KB 1st Level Cache), oder 24KB wenn du auf älteren CPUs auch noch gute Geschwindigkeit haben willst. Bei einzelnen double Werten wären das 4096 bzw. 3072 Pixel.
Oder noch besser: du machst es konfigurierbar (INI File/Settings Dialog, was auch immer).
-
danke ich werde es ausprobieren
Gruß
Steffen