Pregunta

Estoy replicando una pequeña parte del modelo de simulación del agente Sugarscape en Python 3. Encontré que el rendimiento de mi código es ~ 3 veces más lento que el de NetLogo. ¿Es probable que sea el problema con mi código o puede ser la limitación inherente de Python?

Obviamente, esto es solo un fragmento del código, pero ahí es donde Python pasa dos tercios del tiempo de ejecución. Espero que si escribiera algo realmente ineficaz, podría aparecer en este fragmento:

UP = (0, -1)
RIGHT = (1, 0)
DOWN = (0, 1)
LEFT = (-1, 0)
all_directions = [UP, DOWN, RIGHT, LEFT]
# point is just a tuple (x, y)
def look_around(self):
    max_sugar_point = self.point
    max_sugar = self.world.sugar_map[self.point].level
    min_range = 0

    random.shuffle(self.all_directions)
    for r in range(1, self.vision+1):
        for d in self.all_directions:
            p = ((self.point[0] + r * d[0]) % self.world.surface.length,
                (self.point[1] + r * d[1]) % self.world.surface.height)
            if self.world.occupied(p): # checks if p is in a lookup table (dict)
                continue
            if self.world.sugar_map[p].level > max_sugar:
                max_sugar = self.world.sugar_map[p].level
                max_sugar_point = p
    if max_sugar_point is not self.point:
        self.move(max_sugar_point)

Código aproximadamente equivalente en NetLogo (este fragmento hace un poco más que la función de Python anterior) :

; -- The SugarScape growth and motion procedures. --
to M    ; Motion rule (page 25)
    locals [ps p v d]
    set ps (patches at-points neighborhood) with [count turtles-here = 0]
    if (count ps > 0) [
        set v psugar-of max-one-of ps [psugar]              ; v is max sugar w/in vision
        set ps ps with [psugar = v]                         ; ps is legal sites w/ v sugar
        set d distance min-one-of ps [distance myself]      ; d is min dist from me to ps agents
        set p random-one-of ps with [distance myself = d]   ; p is one of the min dist patches
        if (psugar >= v and includeMyPatch?) [set p patch-here]
        setxy pxcor-of p pycor-of p                         ; jump to p
        set sugar sugar + psugar-of p                       ; consume its sugar
        ask p [setpsugar 0]                                 ; .. setting its sugar to 0
    ]
    set sugar sugar - metabolism    ; eat sugar (metabolism)
    set age age + 1
end

En mi computadora, el código Python tarda 15,5 segundos en ejecutar 1000 pasos; en la misma computadora portátil, la simulación de NetLogo que se ejecuta en Java dentro del navegador finaliza 1000 pasos en menos de 6 segundos.

EDITAR: Acabo de marcar Repast, usando la implementación de Java. Y también es aproximadamente lo mismo que NetLogo en 5,4 segundos. Comparaciones recientes entre Java y Python no sugieren ninguna ventaja para Java , así que supongo que es solo mi código el culpable.

EDITAR: Entiendo que MASON se supone que es incluso más rápido que Repast y, sin embargo, todavía ejecuta Java al final.

¿Fue útil?

Solución

Esto probablemente no dará lugar a una aceleración espectacular, pero debe tener en cuenta que las variables locales son bastante más rápidas en Python en comparación con el acceso a globales o atributos.Entonces, podría intentar asignar algunos valores que se usan en el bucle interno en locales, como este:

def look_around(self):
    max_sugar_point = self.point
    max_sugar = self.world.sugar_map[self.point].level
    min_range = 0

    selfx = self.point[0]
    selfy = self.point[1]
    wlength = self.world.surface.length
    wheight = self.world.surface.height
    occupied = self.world.occupied
    sugar_map = self.world.sugar_map
    all_directions = self.all_directions

    random.shuffle(all_directions)
    for r in range(1, self.vision+1):
        for dx,dy in all_directions:
            p = ((selfx + r * dx) % wlength,
                (selfy + r * dy) % wheight)
            if occupied(p): # checks if p is in a lookup table (dict)
                continue
            if sugar_map[p].level > max_sugar:
                max_sugar = sugar_map[p].level
                max_sugar_point = p
    if max_sugar_point is not self.point:
        self.move(max_sugar_point)

Las llamadas a funciones en Python también tienen una sobrecarga relativamente alta (en comparación con Java), por lo que puede intentar optimizar aún más reemplazando la función occupied con una búsqueda de diccionario directa.

También debería echar un vistazo a psyco .Es un compilador justo a tiempo para Python que puede brindar mejoras de velocidad dramáticas en algunos casos.Sin embargo, todavía no es compatible con Python 3.x, por lo que deberá utilizar una versión anterior de Python.

Otros consejos

Voy a suponer que la forma en que neighborhood se implementa en NetLogo es diferente del doble bucle que tiene.Específicamente, creo que precalculan un vector de vecindario como

n = [ [0,1],[0,-1],[1,0],[-1,0]....]

(necesitaría uno diferente para vision= 1,2, ...) y luego use solo un bucle sobre n en lugar de un bucle anidado como lo está haciendo.Esto elimina la necesidad de multiplicar.

No creo que esto le ayude a acelerar el triple.

Esta es una pregunta antigua, pero le sugiero que considere usar NumPy para acelerar sus operaciones.Los lugares donde usa dictados y listas que están organizados lógicamente (cuadrícula de 1, 2, 3 o N-dimensión) objetos de datos homogéneos (todos los enteros o todos los flotantes, etc.) tendrán menos gastos generales cuando se representan y se accede a ellos como Numpymatrices.

http://numpy.org

Aquí hay una comparación relativamente actualizada de NetLogo y una versión de Repast.No asumiría necesariamente que Repast sea más rápido.NetLogo parece contener algunos algoritmos muy inteligentes que pueden compensar cualquier costo que tenga. http://condor.depaul.edu/slytinen/abm/Lytinen-Railsback-EMCSR_2012-02-17.pdf

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