1

さて、本当に最適化が必要な Python コードを取得しました。

  • これは、小さな (80x60 ピクセル) 画像に対する Game-of-Life 反復であり、そこから RGB 値を抽出します。
  • 現在、ネストされた for ループを使用しています。これらの for ループをより高速な c 関数に交換したいのですmap()が、そうすると、x、y 値を取得する方法や、関数のスコープ外で定義されたローカル値を取得する方法がわかりません。定義する必要があります。
  • map()この for ループの現在のセットよりも速く使用できますか? どうすればそれを使用してもx、yを取得できますか?
  • 私は現在pygame Surfacesを使用しており、モジュールを試しましたsurfarray/pixelarrayが、すべてのピクセルを変更/取得しているため、Surface.get_at()/set_at().
  • また、少し無関係です... Pythonが数字のリストをトラバースせず、他の言語のように数字をインクリメントするだけの場合、これはより速くできると思いますか? Python に通常の for() と foreach() が含まれていないのはなぜですか?
  • そこにある条件文の量もおそらく物事を遅くしますよね?最も遅い部分は、隣人のチェックです (リスト n を構築する場所)... そのビット全体を 2D 配列のスライス アクセスに置き換えましたが、正しく動作しません。

コードの編集済みバージョン:

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

関数の完全版:

# 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))
4

3 に答える 3

3

コードを遅くしているのはおそらくループではなく、信じられないほど高速です。

コードを遅くするのは、関数呼び出しの数です。例えば

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

(私が推測する約3倍)よりもはるかに遅いです

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

すべてget_atの ,set_at呼び出しはサーフェスをロックするため、利用可能なメソッドを使用してピクセルに直接アクセスする方が高速です。最も合理的と思われるのはSurface.get_buffer.

mapインデックスが必要なため、この例では使用できません。数が 80 と 60 と少ない場合range()は、xrange().

于 2010-01-08T22:37:27.817 に答える
2
map(do_stuff, ((x, y) for x in xrange(80) for y in xrange(60)))

wheredo_stuffはおそらく次のように定義されます。

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

または、ジェネレーター式の代わりにリスト内包表記をmap( に置き換え((x, y) ...)[(x, y) ...])の 2 番目の引数として使用し、 のrange代わりに使用することもできxrangeます。ただし、パフォーマンスに大きな影響を与える可能性はあまりないと思います。

編集: gs は、forループがコードの最適化を必要とする主なものではないことについて確かに正しいことに注意してください...への余分な呼び出しを削減することget_atがより重要です。実際、ループを置き換えることでmap実際にパフォーマンスが向上するかどうかはわかりません...そうは言っても、mapバージョンの方が読みやすいと思います(おそらく私のFPのバックグラウンドのため...)ので、とにかくここに行きます. ;-)

于 2010-01-08T23:00:48.873 に答える
1

すべてのピクセルを読み込んで書き換えているので、 を使用しないことで最高の速度向上が得られると思いSurfaceます

最初に 80x60 の画像を取得し、それを 32 ビット ピクセルのプレーンなビットマップ ファイルに変換することをお勧めします。次に、ピクセル データを pythonarrayオブジェクトに読み取ります。これで、オブジェクトの上を歩いて、array値を読み取り、新しい値を計算し、新しい値を最大速度で所定の位置に突き刺すことができます。完了したら、新しいビットマップ イメージを保存し、Surface.

24 ビット ピクセルを使用することもできますが、これは遅くなるはずです。32 ビット ピクセルとは、1 つのピクセルが 1 つの 32 ビット整数値であることを意味します。これにより、ピクセルの配列のインデックスがはるかに簡単になります。24 ビットのパックされたピクセルは、各ピクセルが 3 バイトであることを意味し、インデックスを作成するのがはるかに面倒です。

の使用を避けようとするよりも、このアプローチの方がはるかに高速になると思いますfor。これを試した場合は、ここに投稿して、うまく機能したかどうかをお知らせください。幸運を。

array編集: anにはインデックスが1つしかないと思いました。どうやって 2 つのインデックスを機能させることができたのかわかりません。私はあなたがこのようなことをすることを期待していました:

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)

Pythonarrayは単一の型しか保持できないため、型を「符号なしバイト」に設定してから、私が示したようにインデックスを作成する必要があります。

__aもちろん、実際のarray変数はどこにありますか。

これが役に立たない場合は、ビットマップを 1 つまたは 3 つのリストに変換してみてください。ネストされたリストを使用して、2D アドレス指定を取得できます。

これが役立つことを願っています。役に立たない場合は、あなたが何をしているのか理解できません。もっと説明してくれたら、答えを改善しようとします。

于 2010-01-08T23:52:30.270 に答える