Domanda

Sto replicando una piccola parte del modello di simulazione dell'agente Sugarscape in Python 3. Ho scoperto che le prestazioni del mio codice sono ~ 3 volte più lente di quelle di NetLogo. È probabile il problema con il mio codice o può essere la limitazione intrinseca di Python?

Ovviamente, questo è solo un frammento del codice, ma è lì che Python trascorre i due terzi del tempo di esecuzione. Spero che se scrivessi qualcosa di veramente inefficiente potrebbe apparire in questo frammento:

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)

codice in NetLogo più o meno equivalente (questo frammento fa un po 'di più della funzione Python sopra) :

; -- 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

Sul mio computer, il codice Python impiega 15,5 secondi per eseguire 1000 passaggi; sullo stesso laptop, la simulazione NetLogo in esecuzione in Java all'interno del browser completa 1000 passaggi in meno di 6 secondi.

EDIT: ho appena controllato Repast, utilizzando l'implementazione Java. Ed è anche più o meno lo stesso di NetLogo a 5,4 secondi. Confronti recenti tra Java e Python non suggeriscono alcun vantaggio per Java , quindi immagino che la colpa sia solo del mio codice?

MODIFICA: capisco che MASON dovrebbe essere ancora più veloce di Repast, eppure alla fine esegue ancora Java.

È stato utile?

Soluzione

Questo probabilmente non darà una notevole accelerazione, ma dovresti essere consapevole che le variabili locali sono un po 'più veloci in Python rispetto all'accesso alle variabili globali o agli attributi.Quindi potresti provare ad assegnare alcuni valori che vengono utilizzati nel ciclo interno in locals, come questo:

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)

Le chiamate di funzione in Python hanno anche un overhead relativamente elevato (rispetto a Java), quindi puoi provare a ottimizzare ulteriormente sostituendo la funzione occupied con una ricerca diretta nel dizionario.

Dovresti anche dare un'occhiata a psyco .È un compilatore just-in-time per Python che può offrire notevoli miglioramenti della velocità in alcuni casi.Tuttavia, non supporta ancora Python 3.x, quindi dovresti usare una versione precedente di Python.

Altri suggerimenti

Immagino che il modo in cui neighborhood è implementato in NetLogo è diverso dal doppio loop che hai.Nello specifico, penso che pre-calcolino un vettore di vicinato come

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

(ne avresti bisogno uno diverso per vision= 1,2, ...) e quindi usa un solo loop su n invece di un loop annidato come stai facendo.Ciò elimina la necessità di moltiplicazioni.

Non credo che questo ti farà aumentare la velocità di 3 volte.

Questa è una vecchia domanda, ma ti suggerisco di considerare l'utilizzo di NumPy per velocizzare le tue operazioni.I luoghi in cui si utilizzano dict ed elenchi che sono organizzati logicamente (griglia a 1, 2, 3 o N dimensioni) oggetti dati omogenei (tutti i numeri interi o tutti i float, ecc.) Avranno meno overhead quando rappresentati e accessibili come Numpyarray.

http://numpy.org

Ecco un confronto relativamente aggiornato tra NetLogo e una versione di Repast.Non presumo necessariamente che Repast sia più veloce.NetLogo sembra contenere alcuni algoritmi molto intelligenti che possono compensare i costi che ha. http://condor.depaul.edu/slytinen/abm/Lytinen-Railsback-EMCSR_2012-02-17.pdf

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