سؤال

حسنا، لذلك لدي قطعة من رمز بيثون الذي يحتاج حقا إلى تحسين.

  • إنها عملية تكرار في الحياة على صورة صغيرة (80x60 بكسل) واستخراج قيم RGB منه.
  • حاليا باستخدام متداخلة للحلقات؛ أنا أفضل مبادلة من تلك للحلقات بشكل أسرع map() وظيفة C، ولكن إذا قمت بذلك، فلا يمكنني معرفة كيف يمكنني الحصول على القيم x، y، ولا القيم المحلية المحددة من نطاق الوظائف التي أحتاج إلى تحديدها.
  • سوف تستخدم map() أن تكون أسرع من هذه المجموعة الحالية للحلقات؟ كيف يمكنني استخدامه وما زلت أحصل على X، Y؟
  • أستخدم حاليا أسطح Pygame، وقد جربت surfarray/pixelarray الوحدات، ولكن منذ أن أغير / الحصول على كل بكسل، فإنه أبطأ كثيرا من Surface.get_at()/set_at().
  • أيضا، غير ذي صلة قليلا ... هل تعتقد أن هذا يمكن أن يكون أسرع إذا لم يكن بيثون عبور قائمة بالأرقام ولكن فقط زيادة عدد، مثل بلغات أخرى؟ لماذا لا تشمل Python طبيعيا () بالإضافة إلى توقعاتهم ()؟
  • كمية الشرطية هناك ربما تجعل الأمور أبطأ أيضا، أليس كذلك؟ الجزء الأبطأ هو التحقق من الجيران (حيث يبني القائمة N) ... استبدلت هذا بت كامل مع شريحة وصول على صفيف ثنائي الأبعاد، لكنه لا يعمل بشكل صحيح.

نسخة منقوشة من التعليمات البرمجية:

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))
هل كانت مفيدة؟

المحلول

منذ أن تقرأ وإعادة كتابة كل بكسل، أعتقد أنه يمكنك الحصول على أفضل تحسن السرعة من خلال عدم استخدام Surface.

أقترح أولا أخذ صورتك 80x60 وتحويلها إلى ملف نقطي عادي مع بكسل 32 بت. ثم اقرأ بيانات البكسل في بيثون array هدف. الآن يمكنك المشي فوق array الكائن وقيم القراءة وحساب القيم الجديدة ومثبت القيم الجديدة في مكانها بأقصى سرعة. عند الانتهاء، احفظ صورتك النقطية الجديدة، ثم تحويلها إلى Surface.

يمكنك أيضا استخدام وحدات البكسل 24 بت، ولكن يجب أن تكون أبطأ. البكسل 32 بت يعني بكسل واحد هو قيمة عدد صحيح 32 بت، مما يجعل مجموعة من البكسل أسهل بكثير للفهرس. البكسلات المعبأة 24 بت يعني أن كل بكسل هو 3 بايت، وهو أكثر مزعجا إلى الفهرس.

أعتقد أنك ستكتسب أكثر سرعة من هذا النهج من خلال محاولة تجنب استخدام for. وبعد إذا جربت هذا، فيرجى نشر شيء ما هنا لإعلامنا مدى جودة عمله أو لم يفعله. حظ سعيد.

تحرير: اعتقدت أن array لديه فقط مؤشر واحد. لست متأكدا من كيفية تمكنك من الحصول على مؤشرين للعمل. كنت أتوقع منك أن تفعل شيئا مثل هذا:

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)

منذ بيثون array يمكن أن تعقد فقط نوع واحد، ستحتاج إلى تعيين النوع إلى "البايت غير الموقعة" ثم فهرس كما أظهرت.

حيث بالطبع __a هو الفعلي array عامل.

إذا لم يكن أي من هذا مفيدا، فحاول تحويل النقطات النقطية إلى قائمة، أو ربما ثلاث قوائم. يمكنك استخدام قوائم متداخلة للحصول على عنوان ثنائي الأبعاد.

آمل أن يساعد هذا. إذا لم يكن مفيدا، فأنا لا أفهم ما تفعله؛ إذا شرحت المزيد سأحاول تحسين الإجابة.

نصائح أخرى

ما الذي يجعل الرمز الخاص بك بطيء ربما لا الحلقات، فهي سريعة بشكل لا يصدق.

ما يطالب الكود الخاص بك هو عدد مكالمات الدالة. علي سبيل المثال

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 Call Locks السطح، وبالتالي فإنه أسرع الوصول مباشرة إلى البكسل باستخدام الأساليب المتاحة. الشخص الذي يبدو أكثر معقولة هو Surface.get_buffer.

استخدام map لا يعمل في مثالك، لأنك تحتاج إلى الفهارس. مع عدد قليل من 80 و 60 أرقاما قد تكون أسرع للاستخدام range() بدلا من xrange().

map(do_stuff, ((x, y) for x in xrange(80) for y in xrange(60)))

أين do_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) ...]) واستخدام range بدلا من xrange. وبعد أود أن أقول أنه ليس من المرجح أن يكون له تأثير كبير على الأداء، رغم ذلك.

يحرر: لاحظ أن GS هو بالتأكيد الحق في for لا يجري حلقات عدم وجود الشيء الرئيسي في الحاجة إلى التحسين في التعليمات البرمجية الخاصة بك ... قلل على المكالمات الزائدة get_at هو أكثر أهمية. في الواقع، لست متأكدا مما إذا استبدل الحلقات map سوف يحسن في الواقع الأداء هنا على الإطلاق ... بعد أن قال ذلك، أجد map الإصدار أكثر قراءة (ربما بسبب خلفية FP الخاصة بي ...)، لذلك هنا تذهب على أي حال. ؛-)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top