Domanda

Ok, ho un pezzo di codice Python che in realtà ha bisogno di ottimizzazione.

  • Si tratta di una ripetizione del gioco della vita umana sopra l'immagine di un piccolo (80x60 pixel) ed estrae i valori RGB da esso.
  • utilizzando un annidati per-loop; Preferisco scambiare quelli per cicli per la funzione più veloce map() c, ma se lo faccio io non riesco a capire come posso ottenere il valori X, Y, nè i valori locali definiti fuori della portata delle funzioni I 'd bisogno di definire.
  • sarebbe utilizzando map() essere qualsiasi più veloce di questa corrente serie di cicli for? Come ho potuto usarlo e ancora ottenere x, y?
  • Attualmente uso Superfici pygame, e ho provato i moduli surfarray/pixelarray, ma dal momento che sto cambiando / ricevendo ogni pixel, è molto più lento di Surface.get_at()/set_at().
  • Inoltre, un po 'irrilevante ... pensi che questo potrebbe essere fatto più velocemente se Python non stava attraversando un elenco di numeri, ma solo incrementare un numero, come in altre lingue? Perché python non include un normale (), così come la loro foreach ()?
  • La quantità di condizionali probabilmente ci rende le cose più lento troppo, giusto? La parte più lenta sta verificando per i vicini (in cui si costruisce la lista n) ... Ho sostituito che tutta bit con accesso fetta su una matrice 2D, ma non funziona correttamente.

Versione Redacted di codice:

xr = xrange(80)
yr = xrange(60)
# surface is an instance of pygame.Surface
get_at = surface.get_at()
set_at = surface.set_at()

for x in xr:
    # ....
    for y in yr:
        # ...
        pixelR = get_at((x,y))[0]
        pixelG = get_at((x,y))[1]
        pixelB = get_at((x,y))[2]
        # ... more complex stuff here which changes R,G,B values independently of each other
        set_at((x,y),(pixelR,pixelG,pixelB))

La versione completa della funzione:

# xr, yr = xrange(80), xrange(60)
def live(surface,xr,yr):
    randint = random.randint
    set_at = surface.set_at
    get_at = surface.get_at
    perfect = perfectNeighbours #
    minN = minNeighbours        # All global variables that're defined in a config file.
    maxN = maxNeighbours        #
    pos = actual                # actual = (80,60)
    n = []
    append = n.append
    NEIGHBOURS = 0

    for y in yr: # going height-first for aesthetic reasons.
        decay = randint(1,maxDecay)
        growth = randint(1,maxGrowth)

        for x in xr:
            r, g, b, a = get_at((x,y))

            del n[:]
            NEIGHBOURS = 0

            if x>0 and y>0 and x<pos[0]-1 and y<pos[1]-1:
                append(get_at((x-1,y-1))[1])
                append(get_at((x+1,y-1))[1])
                append(get_at((x,y-1))[1])
                append(get_at((x-1,y))[1])
                append(get_at((x+1,y))[1])
                append(get_at((x-1,y+1))[1])
                append(get_at((x+1,y+1))[1])
                append(get_at((x,y+1))[1])
                for a in n:
                    if a > 63:
                        NEIGHBOURS += 1

            if NEIGHBOURS == 0 and (r,g,b) == (0,0,0): pass
            else:

                if NEIGHBOURS < minN or NEIGHBOURS > maxN:
                    g = 0
                    b = 0
                elif NEIGHBOURS==perfect:
                    g += growth
                    if g > 255:
                        g = 255
                        b += growth
                        if b > growth: b = growth
                else:
                    if g > 10: r = g-10
                    if g > 200: b = g-100
                    if r > growth: g = r
                    g -= decay
                    if g < 0:
                        g = 0
                        b = 0
                r -= 1
                if r < 0:
                    r = 0
                set_at((x,y),(r,g,b))
È stato utile?

Soluzione

Dal momento che si sta leggendo e riscrittura tutti di pixel, penso che si può ottenere il miglior miglioramento della velocità non usando un Surface.

Suggerisco prima di prendere la vostra immagine 80x60 e convertirlo in un file bitmap pianura con i pixel a 32 bit. Poi leggi i dati dei pixel in un array oggetto Python. Ora si può camminare sopra l'oggetto array, la lettura dei valori, calcolare nuovi valori, e colpendo i nuovi valori in posizione con la massima velocità. Al termine, salvare la nuova immagine bitmap e quindi convertirlo in un Surface.

Si potrebbe anche usare pixel a 24 bit, ma che dovrebbe essere più lento. pixel a 32 bit significa un pixel è un valore intero di 32 bit, che rende la matrice di pixel molto più facile da indice. 24-bit pixel confezionati significa che ogni pixel è di 3 byte, che è molto più fastidioso per indicizzare.

Credo che si otterrà molto di più la velocità di questo approccio che, cercando di evitare l'uso di for. Se si prova, si prega di inviare qualcosa qui per farci sapere quanto bene ha funzionato o non ha fatto. Buona fortuna.

EDIT: ho pensato che un array ha solo un unico indice. Non sono sicuro di come sei riuscito a ottenere due indici di lavorare. Mi aspettavo di fare qualcosa di simile:

def __i(x, y):
    assert(0 <= x < 80)
    assert(0 <= y < 60)
    i = (y*80 + x) * 4
    return i
def red(x, y):
    return __a[__i(x, y)]
def green(x, y):
    return __a[__i(x, y) + 1]
def blue(x, y):
    return __a[__i(x, y) + 2]
def rgb(x, y):
    i = __i(x, y)
    return __a[i], __a[i + 1], __a[i + 2]
def set_rgb(x, y, r, g, b):
    i = __i(x, y)
    _a[i] = r
    _a[i + 1] = g
    _a[i + 2] = b

# example:
r, g, b = rgb(23, 33)

Dal momento che un array Python può contenere solo un unico tipo, si vuole impostare il tipo di "byte senza segno" e quindi indice come ho mostrato.

Dove ovviamente __a è la variabile array reale.

Se tutto questo non è utile, provare a convertire il bitmap in un elenco, o forse tre liste. È possibile utilizzare gli elenchi nidificati per ottenere 2D affrontare.

Spero che questo aiuta. Se non è utile, allora io non sto capendo quello che stai facendo; se spiegate più Cercherò di migliorare la risposta.

Altri suggerimenti

Che cosa sta facendo il vostro codice lenta è probabilmente non loop, sono incredibilmente veloci.

Cosa rallenta fatto il codice sono il numero di chiamate di funzione. Ad esempio

pixelR = get_at((x,y))[0]
pixelG = get_at((x,y))[1]
pixelB = get_at((x,y))[2]

molto più lento rispetto (circa 3 volte immagino)

r, g, b, a = get_at((x,y))

Ogni get_at, serrature di chiamata set_at la superficie, quindi è più veloce per accedere direttamente ai pixel utilizzando i metodi disponibili. Quello che sembra più ragionevole è Surface.get_buffer .

Utilizzando map non funziona nel tuo esempio, perché avete bisogno gli indici. Con appena 80 e 60 numeri potrebbe anche essere più veloce da usare al posto di range() xrange().

map(do_stuff, ((x, y) for x in xrange(80) for y in xrange(60)))

dove do_stuff dovrebbe presumibilmente essere definito in questo modo:

def do_stuff(coords):
    r, g, b, a = get_at(coords)
    # ... whatever you need to do with those ...
    set_at(coords, (r, g, b))

Si può anche utilizzare una lista comprensione invece di un generatore di espressione come il secondo parametro di map (sostituire ((x, y) ...) con [(x, y) ...]) e usare range anziché xrange. Direi che non è molto probabile avere un impatto significativo sulle prestazioni, però.

Modifica Si noti che GS è certamente ragione circa la for loop non essere la cosa principale ha bisogno di ottimizzazione nel codice ... Riducendo le chiamate superfluo get_at è più importante. In realtà, io non sono sicuro se la sostituzione delle anse con map sarà effettivamente migliorare le prestazioni qui a tutti ... Detto questo, trovo la versione map più leggibile (forse a causa della mia FP fondo ...), ecco che si andare comunque. ; -)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top