2011-02-05 11 views
12

私はPython 3でSugarscapeエージェントシミュレーションモデルの小さな部分を複製しています。私のコードのパフォーマンスはNetLogoの性能よりも3倍遅いことがわかりました。それはおそらく私のコードに問題があるのでしょうか、それともPythonの本質的な制限ですか?エージェントベースのシミュレーション:パフォーマンスの問題:PythonとNetLogo&Repast

明らかに、これはコードの単なる断片ですが、Pythonが実行時の3分の2を費やしているところです。私はそれがこのフラグメントに表示される場合があります本当に非効率的な何かを書いた場合、私は願っています:

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

私のコンピュータの場合:

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) 

ほぼ同等のcode in NetLogoを(このフラグメントは、上記のPythonの関数よりも少しありません) 、Pythonコードは15.5秒で1000ステップ実行されます。同じラップトップ上で、ブラウザ内でJavaで実行されているNetLogoシミュレーションは、6秒未満で1000ステップを終了します。

EDIT:Java実装を使用してRepastをチェックしました。 5.4秒でNetLogoとほぼ同じです。 JavaとPythonの間のRecent comparisonsはJavaに利点がないことを示唆しているので、それは自分の責任であると思いますか?

編集:MASONはRepastよりも高速であると思われますが、それでもまだJavaを実行します。

答えて

11

おそらく劇的な高速化はありませんが、ローカル変数は、グローバルや属性へのアクセスに比べてPythonではかなり高速です。だから、このように、地元の人々に内部ループで使用されるいくつかの値を割り当てる試みることができる:

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) 

機能はPythonで呼び出しても、(Javaのに比べて)比較的高いオーバーヘッドを持っている、あなたはさらに最適化しようとすることができますので、 occupied関数をダイレクト・ディクショナリ・ルックアップに置き換えます。

psycoもご覧ください。これはPythonのジャストインタイムコンパイラで、場合によっては速度を大幅に向上させることができます。しかし、まだPython 3.xをサポートしていないので、古いバージョンのPythonを使う必要があります。

+1

ニース。実行時間は15.5秒から11.4秒(〜26%)に減少しました。なぜ私は少なくともインタプリタに-Oオプションを指定すると、Pythonがそのようなものを最適化しないのでしょうか? – max

+1

「占有」機能を取り除くとさらに10.4秒になりました。 – max

+1

@max:あなたが経験したスピードアップで書き返してくれてありがとう。 – Curious2learn

4

私は、neighborhoodがNetLogoに実装されている方法が、あなたが持っているダブルループとは異なることを推測します。具体的には、私は彼らが

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

のような近所のベクトルを事前に計算すると思います(あなたは...、ビジョン= 1,2のために別のものを必要とする)と、代わりに、ネストされたループのnの上にただ一つのループを使用しますあなたがやっているように。これにより、乗算が不要になります。

私はこれで3倍のスピードアップが得られるとは思わない。

+0

最高の糖点の中にランダムな点が必要です。私のアプローチでは、ループの前の方向に1つのシャッフルを行うだけです。 NetLogoでは、すべてのベストポイントを追跡し、リストからランダムに1つ選択することで完了しました(私はPythonで試しましたが、少し遅くなりました)。あなたのアプローチでは、複数のベストポイントを再度追跡したり、各近傍ベクトルをシャッフルする必要があります(1つではなく「シャッフル」)。私が試してみます。 – max

+0

また、私はトーラスで折り返しをチェックする必要があるので、各点の近傍を事前に計算するか、または '% '操作を使用し続ける必要があります。前者は数パーセント速く、後者はずっと遅い。 – max

+0

netLogoのラップアラウンドワールド動作をより速く取得するには、x方向とy方向の両端に余分なインデックスを付けてnumpy配列内でモデル化するのがよいでしょう。近傍距離が1の場合、配列インデックスは0から幅+1まで、0から高さ+1まで実行されます。計算は、世界を表すインデックスのサブセット内でのみ行う必要があり、各次元の両端の余分なインデックスのパッチ値は、他の端の世界からコピーすることができます。これにより、世界の内部のnetLogoパッチに相当する各pythonの%計算が回避されます。 –

3

これは古い質問ですが、操作のスピードアップにNumPyを使用することを検討することをおすすめします。論理的に整理された(1次元、2次元、3次元、またはN次元のグリッド)均質なデータオブジェクト(すべての整数またはすべての浮動小数点数など)を使用する場所は、Numpyアレイ。ここで

http://numpy.org

3

比較的日付NetLogoの比較とRepastの1つのバージョンにアップしています。私は必ずしもRepastがより速いとは思わないでしょう。NetLogoには、何らかのコストを補うことができる非常にスマートなアルゴリズムが含まれているようです。 http://condor.depaul.edu/slytinen/abm/Lytinen-Railsback-EMCSR_2012-02-17.pdf

関連する問題