Pregunta

Está bien, así que tengo una pieza de código Python, que realmente necesita la optimización.

  • Es una iteración del juego de la vida útil de una imagen pequeña (80x60 píxeles) una y extrae los valores RGB de ella.
  • Actualmente anidadas utilizando para-bucles; Yo prefiero intercambiar las de bucles para la función más rápido map() c, pero si hago lo que no puedo averiguar cómo puedo conseguir el x, los valores de y, ni los valores locales definidas fuera del alcance de las funciones que 'd que definir.
  • sería el uso de map() ser más rápido que el conjunto actual de los bucles? ¿Cómo podría usarlo y aún así obtener x, y?
  • I actualmente utilizan superficies de pygame, y he probado los módulos surfarray/pixelarray, pero ya que estoy cambiando / recibiendo cada píxel, que es mucho más lento que Surface.get_at()/set_at().
  • También, un poco irrelevante ... ¿cree que esto podría hacerse más rápido si Python no atravesaba una lista de números, sino simplemente incrementando un número, como en otros idiomas? ¿Por qué no incluir un pitón normal (), así como su foreach ()?
  • La cantidad de condicionales que probablemente hace las cosas más lentas también, ¿verdad? La parte más lenta es la comprobación de los vecinos (donde se construye la lista n) ... He sustituido poco que todo con acceso rebanada en una matriz 2D pero no funciona correctamente.

versión editada de código:

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 versión completa de la función:

# 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))
¿Fue útil?

Solución

Puesto que usted está leyendo y reescribiendo todos pixel, creo que se puede obtener la mejor mejora de la velocidad al no utilizar un Surface.

Sugiero primero tomando su imagen 80x60 y convertirlo en un archivo de mapa de bits llano con píxeles de 32 bits. A continuación, lea los datos de píxeles en una array objeto Python. Ahora se puede caminar sobre el objeto array, los valores de la lectura, el cálculo de nuevos valores, y meter los nuevos valores en su lugar con la máxima velocidad. Cuando haya terminado, guardar la imagen de mapa de bits nuevo y, a continuación, convertirlo en un Surface.

También es posible usar píxeles de 24 bits, pero que debe ser más lento. píxeles de 32 bits significa un píxel es un valor entero de 32 bits, que hace que la matriz de píxeles mucho más fácil de índice. 24-bit píxeles empaquetados significa que cada píxel es de 3 bytes, que es mucho más molesto para indexar en.

Creo que va a ganar mucha más velocidad de este enfoque de tratar de evitar el uso de for. Si intenta esto, por favor, puesto algo aquí para hacernos saber lo bien que funcionó o no. Buena suerte.

EDIT: He pensado que un array sólo tiene un único índice. No estoy seguro de cómo se las arregló para conseguir dos índices para trabajar. Yo estaba esperando que haga algo como esto:

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)

Desde un array Python sólo puede contener un solo tipo, tendrá que configurar el tipo de "byte sin signo" y luego índice como he mostrado.

Donde de __a curso es la variable array real.

Si nada de esto es útil, intentar convertir su mapa de bits en una lista, o tal vez tres listas. Puede utilizar listas anidadas para conseguir hacer frente a 2D.

Espero que esto ayude. Si no es útil, entonces yo no soy la comprensión de lo que está haciendo; si se le explica más voy a tratar de mejorar la respuesta.

Otros consejos

Lo que está haciendo su código lento es probable que no los bucles, que son increíblemente rápido.

Lo que retarda el hecho que su código es el número de llamadas a funciones. Por ejemplo

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

es mucho más lento que (alrededor de 3 veces, supongo)

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

Cada get_at, bloqueos de llamadas set_at la superficie, por lo tanto, es más rápido para acceder directamente a los píxeles utilizando los métodos disponibles. El que parece más razonable es Surface.get_buffer .

El uso de map no funciona en su ejemplo, porque necesita los índices. Con tan sólo 80 y 60 números que incluso podría ser más rápido usar range() en lugar de xrange().

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

donde do_stuff presumiblemente se definiría así:

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

Se puede usar alternativamente una lista por comprensión en lugar de un generador de expresión como el segundo argumento a map (sustituir con ((x, y) ...) [(x, y) ...]) y utilizar range en lugar de xrange. Yo diría que no es muy probable que tenga un efecto significativo en el rendimiento, sin embargo.

Editar Tenga en cuenta que gs está en lo cierto acerca de la for bucles no ser lo principal en la necesidad de optimización en el código ... Reducir el consumo de llamadas superfluas a get_at es más importante. De hecho, no estoy seguro de si la sustitución de los bucles con map realmente mejorar el rendimiento aquí en todo ... Una vez dicho esto, creo que la versión map más legible (tal vez debido a mi experiencia FP ...), por lo que aquí se ir de todos modos. ; -)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top