Hallo Oscar,
zunächst: [Blockierte Grafik: http://smilie-land.de/t/t-v/verneigen/verneigen0010.gif] TOP(!!) Leistung.
Ggf solltest du in deinem Post die Version posten, welche kein AssembleIt() benötigt, für diejenigen die "nur" deinen Code ausprobieren wollen benötigt man ausschliesslich AutoIt!
Das _DllStructCreate64() habe ich auch rausgenommen, ist nur für die 64-Bit-Kompatibilität (also 64-Bit-ASM-Code) nötig.
#AutoIt3Wrapper_UseX64=n ; 32Bit-Modus
#include <GDIPlus.au3>
#include <Memory.au3>
;#include "assembleit2_64.au3"
#Region ASM-Code
#cs _CountUniqueColors ;
Use32 ; 32Bit Modus!
;~ org $PTR_SOURCE_ASMCODE ; only needed for assembleit debugger
mov esi,dword[esp+4] ; esi = Pixelstruct-Pointer
mov edx,dword[esp+8] ; edx = right & Pixelcounter
dec edx ; um eins verringern, weil die Pixelstruct bei 0 beginnt
push edx ; edx fuer die Zaehlschleife sichern
xor ecx,ecx ; ecx = left (auf 0 setzen)
push edx ; right auf den Stack (fuer Quicksort)
push ecx ; left auf den Stack (fuer Quicksort)
call quicksort ; Quicksort aufrufen (die Pixelstruct sortieren)
pop edx ; edx wiederherstellen
mov eax,1 ; eax auf 1 setzen (Farbzaehler)
mov ebx,dword[esi+edx*4] ; ebx = Farbwert des letzten Pixels (zum Farbvergleich)
@count: ; Zaehlschleife fuer die Farben
dec edx ; Pixelcounter runterzaehlen
mov ecx,dword[esi+edx*4] ; ecx = Farbwert des vorletzten Pixels (zum Farbvergleich)
cmp ecx,ebx ; ecx mit ebx vergleichen
jae @next ; wenn groesser/gleich, dann mit naechsten Pixel weitermachen
inc eax ; wenn kleiner, dann Farbzaehler um eins erhoehen
mov ebx,ecx ; und die dazugehoerige Farbe merken
@next:
cmp edx,0
jnz @count ; wenn noch nicht 0, dann Schleife @count
ret ; Pixelcounter an AutoIt zurueckgeben (eax)
quicksort: ; die Quicksort-Funktion
mov ecx,dword[esp+4] ; ecx = left
mov edx,dword[esp+8] ; edx = right
cmp ecx,edx ; left und right vergleichen
jae @end ; wenn groesser/gleich, dann Funktion beenden
;~ _ASMDBG_() ; debug-gui anzeigen
push edx ; edx sichern
push ecx ; ecx sichern
push edx ; edx auf den Stack (fuer Partition)
push ecx ; ecx auf den Stack (fuer Partition)
call partition ; Partition aufrufen
pop ecx ; ecx wiederherstellen
pop edx ; edx wiederherstellen
push ebx ; ebx sichern
push edx ; edx sichern
push ecx ; ecx sichern
push ebx ; eax auf den Stack (right fuer Quicksort)
push ecx ; ecx auf den Stack (left fuer Quicksort)
call quicksort ; Quicksort aufrufen (rekursiv)
pop ecx ; ecx wiederherstellen
pop edx ; edx wiederherstellen
pop ebx ; ebx wiederherstellen
inc ebx ; ebx++
push edx ; edx auf den Stack (right fuer Quicksort)
push ebx ; ebx auf den Stack (left fuer Quicksort)
call quicksort ; Quicksort aufrufen (rekursiv)
@end:
ret 8
partition:
mov ecx,dword[esp+4] ; ecx = left
mov edx,dword[esp+8] ; edx = right
mov edi,dword[esi+ecx*4] ; edi = Pivotwert = Pixel[left]
mov eax,ecx
dec eax ; eax = left - 1
mov ebx,edx
inc ebx ; ebx = right + 1
;~ _ASMDBG_() ; debug-gui anzeigen
@loop: ; Hauptschleife
@left: ; Schleife fuer die linke Seite
inc eax ; left++
cmp dword[esi+eax*4],edi ; Vergleich mit Pivotwert
jb @left ; wenn kleiner, dann Schleife @left
@right: ; Schleife fuer die rechte Seite
dec ebx ; right--
cmp dword[esi+ebx*4],edi ; Vergleich mit Pivotwert
ja @right ; wenn groesser, dann Schleife @right
cmp eax,ebx ; Vergleich left und right
jae @return ; wenn groesser/gleich, dann @return
mov ecx,dword[esi+eax*4] ; Pixel[left] gegen Pixel[right] austauschen
mov edx,dword[esi+ebx*4]
mov dword[esi+eax*4],edx
mov dword[esi+ebx*4],ecx
jmp @loop ; und mit @loop fortfahren
@return:
ret 8 ; right zurueckgeben (ebx)
#ce
#EndRegion ASM-Code
;~ $binarycode = _AssembleIt2("retbinary", "_CountUniqueColors") ;gibt nur den assemblierten code zurück
;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & ($binarycode) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
;~ Exit
Global $g_pMem, $g_iMemSize, $bCode, $tCodeBuffer, $hImage, $iW, $iH, $iTimer, $tBitmapData, $pScan0, $tPixel, $pPixel, $tColors, $pColors, $ret
;~ $bCode = "0x8B7424048B5424084A5231C95251E81D0000005A31C08B1C964A8B0C9639D97201408B0C9639D973034089CB4A75F3C38B4C24048B54240839D1732052515251E81A000000595A5352515351E8DFFFFFFF595A5B435253E8D4FFFFFFC208008B4C24048B5424088B3C8E89C84889D34340393C8672FA4B393C9E77FA39D8730E8B0C868B149E891486890C9EEBE2C20800"
;~ $tCodeBuffer = _dllstructcreate64_("byte[" & StringLen($bCode) / 2 - 1 & "]") ;reserve Memory for opcodes
;~ DllStructSetData($tCodeBuffer, 1, $bCode)
_GDIPlus_Startup()
$file = FileOpenDialog("Select 32 Bpp image!", @ScriptDir, "Image (*.jpg;*.bmp;*.png)", 1 + 2)
;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test_g.jpg')
$hImage = _GDIPlus_BitmapCreateFromFile($file)
;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test.png')
$aDim = _GDIPlus_ImageGetDimension($hImage)
ConsoleWrite(StringFormat('_GetUniqueColors (ASM)\n%d x %d = %s px\n', $aDim[0], $aDim[1], $aDim[0] * $aDim[1]))
$iTimer = TimerInit()
$tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $aDim[0], $aDim[1], BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32ARGB)
ConsoleWrite('Zeit (BitmapLockBits): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
$tPixel = DllStructCreate('dword[' & $tBitmapData.Width * $tBitmapData.Height & '];', $tBitmapData.Scan0) ; erstelle Pixelstruct (dword = 32 Bit pro Pixel)
$pPixel = DllStructGetPtr($tPixel)
ConsoleWrite('Zeit (Pixelstruct): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
;$binarycode = _AssembleIt2("retbinary", "_CountUniqueColors") ;assembles the code into binary
$binarycode="0x8B7424048B5424084A5231C95251E81A0000005AB8010000008B1C964A8B0C9639D973034089CB83FA0075F0C38B4C24048B54240839D1732052515251E81A000000595A5352515351E8DFFFFFFF595A5B435253E8D4FFFFFFC208008B4C24048B5424088B3C8E89C84889D34340393C8672FA4B393C9E77FA39D8730E8B0C868B149E891486890C9EEBE2C20800"
;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & $binarycode & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
Global $tCodeBuffer = DllStructCreate("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes
DllStructSetData($tCodeBuffer, 1, $binarycode)
$ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
$ret = $ret[0]
;$ret = _AssembleIt2("dword", "_CountUniqueColors", "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
ConsoleWrite('Anzahl der Farben = ' & $ret & @CR)
ConsoleWrite('Zeit: ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
_GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
$tPixel = 0
$tColors = 0
$tBitmapData = 0
_GDIPlus_BitmapDispose($hImage)
_GDIPlus_Shutdown()
;~ _MemVirtualFree($g_pMem, $g_iMemSize, $MEM_DECOMMIT)
Exit
Alles anzeigen
Jetzt noch ein ganz privates Statement meinerseits....DANKE! Ich war all die Jahre zu faul, einen Quicksort in ASM zu erstellen, ich werde mich in Zukunft an deinem Code bedienen.
Mein Quicksort ist eine rekursive Variante, die den Stack ausgiebig nutzt, aber dafür ansonsten nur den Speicher der Pixelstruct (für das Bild) benutzt.
Das ist auch der Hauptgrund für die vergleichbare "Langsamkeit". Stack heißt Speicherzugriffe, und die kosten Zeit. Schlimmstenfalls liegen die rechten/linken Pixeladressen nicht im Cache und dann tritt der Supergau auf: Cachemiss. Das kostet je nach Prozessor massig Takte!
Daran ist aber nicht deine Umsetzung schuld, sondern der für die landläufige PC-Architektur "langsame" (sprich Speicherzugriffstechnisch aufwendige) Quicksort-Algorithmus.
Mir würde jetzt auf Anhieb nicht einfallen, wie man vorliegenden Code stark beschleunigen könnte.
//EDIT
Habe mal per RDTSC die Prozessor-Takte pro Pixel ausgeben lassen, bei kleinen Bildern ist Quicksort algorithmusbedingt "langsam" mit ca. 200-300 Takten/Pixel.
Bei deinem 7680 x 4320 Testbild gibt der Algorithmus richtig Gas und kommt bei mir auf 15-20 Takte/Pixel!!!
//EDIT2
Nachdem nochmals verifiziert, komme ich mit diversen 12MP-Bildern auf ca. 150 Takte/Pixel