Pergunta

Estou replicando um pequeno pedaço do modelo de simulação do agente Sugarscape no Python 3. Eu descobri que o desempenho do meu código é ~ 3 vezes mais lento do que o do NetLogo. É provável que seja o problema com meu código ou pode ser a limitação inerente do Python?

Obviamente, este é apenas um fragmento do código, mas é onde o Python gasta dois terços do tempo de execução. Espero que, se escrevi algo realmente ineficiente, isso apareça neste 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 código aproximadamente equivalente no NetLogo (este fragmento faz um pouco mais do que a função Python acima) :

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

No meu computador, o código Python leva 15,5 segundos para executar 1000 etapas; no mesmo laptop, a simulação NetLogo em execução em Java dentro do navegador termina 1000 etapas em menos de 6 segundos.

EDITAR: Apenas verifiquei Repast, usando implementação Java. E também é quase o mesmo que NetLogo em 5,4 seg. Comparações recentes entre Java e Python não sugerem nenhuma vantagem para Java , então acho que a culpa é apenas do meu código?

EDITAR: Entendo que MASON deve ser ainda mais rápido do que Repast, mas ainda executa Java no final.

Foi útil?

Solução

Isso provavelmente não proporcionará acelerações dramáticas, mas você deve estar ciente de que as variáveis locais são um pouco mais rápidas em Python em comparação com o acesso a globais ou atributos.Portanto, você pode tentar atribuir alguns valores que são usados no loop interno para locais, 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)

As chamadas de função em Python também têm uma sobrecarga relativamente alta (em comparação com Java), então você pode tentar otimizar ainda mais substituindo a função occupied por uma pesquisa direta no dicionário.

Você também deve dar uma olhada em psyco .É um compilador just-in-time para Python que pode oferecer melhorias dramáticas de velocidade em alguns casos.No entanto, ele ainda não oferece suporte ao Python 3.x, então você precisaria usar uma versão mais antiga do Python.

Outras dicas

Imagino que a maneira como o neighborhood é implementado no NetLogo é diferente do loop duplo que você tem.Especificamente, acho que eles pré-calculam um vetor de vizinhança como

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

(você precisaria de um diferente para vision= 1,2, ...) e então use apenas um loop sobre n em vez de um loop aninhado como você está fazendo.Isso elimina a necessidade de multiplicações.

Não acho que isso vá aumentar a velocidade de 3 vezes.

Esta é uma pergunta antiga, mas sugiro que você use o NumPy para acelerar suas operações.Locais onde você usa dicts e listas que são organizados logicamente (grade de 1, 2, 3 ou N-dimensional) objetos de dados homogêneos (todos os inteiros ou todos os flutuadores, etc) terão menos sobrecarga quando representados e acessados como Numpymatrizes.

http://numpy.org

Aqui está uma comparação relativamente atualizada do NetLogo e uma versão do Repast.Eu não presumia necessariamente que Repast é mais rápido.O NetLogo parece conter alguns algoritmos muito inteligentes que podem compensar quaisquer custos que ele acarrete. http://condor.depaul.edu/slytinen/abm/Lytinen-Railsback-EMCSR_2012-02-17.pdf

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top