كيفية تجنب الحساب في كل مرة يتم فيها إعادة تحميل وحدة بايثون

StackOverflow https://stackoverflow.com/questions/195626

  •  10-07-2019
  •  | 
  •  

سؤال

لدي وحدة بايثون تستخدم متغيرًا عالميًا ضخمًا للقاموس، حاليًا أضع رمز الحساب في القسم العلوي، في كل مرة يستغرق استيراد الوحدة أو إعادة تحميلها أكثر من دقيقة واحدة، وهو أمر غير مقبول على الإطلاق.كيف يمكنني حفظ نتيجة الحساب في مكان ما حتى لا تضطر عملية الاستيراد/إعادة التحميل التالية إلى حسابها؟لقد حاولت cPickle، لكن تحميل متغير القاموس من ملف (1.3M) يستغرق تقريبًا نفس وقت الحساب.

ولإعطاء المزيد من المعلومات حول مشكلتي،

FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min
هل كانت مفيدة؟

المحلول

فقط للتوضيح:الكود الموجود في نص الوحدة هو لا يتم تنفيذه في كل مرة يتم فيها استيراد الوحدة - يتم تشغيلها مرة واحدة فقط، وبعد ذلك تجد عمليات الاستيراد المستقبلية الوحدة التي تم إنشاؤها بالفعل، بدلاً من إعادة إنشائها.ألق نظرة على sys.modules لرؤية قائمة الوحدات المخزنة مؤقتًا.

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

للحصول على الحد الأدنى من التغيير في الواجهة الخاصة بك، قد تكون وحدة الرف هي الخيار الأفضل لك - وهذا يضع واجهة شفافة جدًا بين وحدات dbm مما يجعلها تعمل مثل إملاء بايثون التعسفي، مما يسمح بتخزين أي قيمة قابلة للاختيار.هنا مثال:

# Create dict with a million items:
import shelve
d = shelve.open('path/to/my_persistant_dict')
d.update(('key%d' % x, x) for x in xrange(1000000))
d.close()

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

>>> d = shelve.open('path/to/my_persistant_dict')
>>> print d['key99999']
99999

إنه أبطأ قليلاً من الإملاء الحقيقي، وهو كذلك سوف لا يزال التحميل يستغرق وقتًا طويلاً إذا قمت بشيء يتطلب جميع المفاتيح (على سبيل المثال.حاول طباعته)، ولكنه قد يحل مشكلتك.

نصائح أخرى

واحسب فار العمومي على أول استخدام.

class Proxy:
    @property
    def global_name(self):
        # calculate your global var here, enable cache if needed
        ...

_proxy_object = Proxy()
GLOBAL_NAME = _proxy_object.global_name

وأو الأفضل من ذلك، الوصول إلى البيانات necessery عبر كائن بيانات خاصة.

class Data:
    GLOBAL_NAME = property(...)

data = Data()

مثال:

from some_module import data

print(data.GLOBAL_NAME)

الإعدادات جانغو .

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

هل يمكن أن حاول استخدام مارشال حدة بدلا من ج ؟ مخلل واحد؛ يمكن أن يكون أسرع. وتستخدم هذه الوحدة التي كتبها الثعبان لتخزين القيم في شكل ثنائي. ملاحظة خاصة الفقرة التالية، لمعرفة ما إذا مارشال يناسب احتياجاتك:

<اقتباس فقرة>   

وليست كل أنواع الكائنات بيثون. بشكل عام، الكائنات فقط قيمتها مستقلة من الاحتجاج معين من بيثون يمكن كتابة وقراءة من خلال هذه الوحدة. يتم دعم الأنواع التالية: لا شيء، صحيحة، صحيحة طويلة، أرقام النقطة العائمة، سلاسل، والأشياء يونيكود، الصفوف، والقوائم، ومجموعات والقواميس، والأشياء الرمز، حيث يجب أن يكون مفهوما أن الصفوف، القوائم والقواميس معتمدة فقط طالما كما القيم الواردة فيه هي نفسها المدعومة؛ وقوائم متكررة والقواميس لا ينبغي أن تكون مكتوبة (أنها سوف تتسبب في حلقات لانهائية).

ولمجرد أن يكون على الجانب الآمن، قبل unmarshalling على ديكت، تأكد من أن النسخة بيثون أن unmarshals في ديكت هو نفسه ذلك الذي فعل المشير، حيث لا توجد ضمانات لالتوافق.

إذا تبين أن حل "الرف" بطيء للغاية أو تافه، فهناك احتمالات أخرى:

وshelve يحصل حقا بطيئة مع مجموعات كبيرة من البيانات. لقد تم استخدام رديس جدا بنجاح، وكتب FreqDist المجمع حوله. انها سريعة جدا، ويمكن الوصول إليها في وقت واحد.

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

ورفوف ورطة القيم ديكت جدا، ولكن لن تفعل (الامم المتحدة) المخلل ليس عند بدء التشغيل لجميع العناصر، ولكن فقط في وقت وصول كل بند نفسها.

بعض الأشياء التي ستساعد في تسريع الواردات:

  1. يمكنك محاولة تشغيل python باستخدام علامة -OO عند تشغيل python.سيؤدي هذا إلى إجراء بعض التحسينات التي من شأنها تقليل وقت استيراد الوحدات.
  2. هل هناك أي سبب يمنعك من تقسيم القاموس إلى قواميس أصغر في وحدات منفصلة يمكن تحميلها بسرعة أكبر؟
  3. كحل أخير، يمكنك إجراء الحسابات بشكل غير متزامن حتى لا تؤخر برنامجك حتى يحتاج إلى النتائج.أو ربما حتى وضع القاموس في عملية منفصلة وتمرير البيانات ذهابًا وإيابًا باستخدام IPC إذا كنت ترغب في الاستفادة من البنى متعددة النواة.

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

  1. هل تقوم باستيراد الوحدة داخل الوظيفة؟إذا كان الأمر كذلك، هذا يستطيع يؤدي إلى مشاكل في الأداء لأنه يتعين عليه التحقق ومعرفة ما إذا كان يتم تحميل الوحدة في كل مرة تصل فيها إلى بيان الاستيراد.
  2. هل برنامجك متعدد الخيوط؟لقد رأيت مناسبات حيث يمكن أن يؤدي تنفيذ التعليمات البرمجية عند استيراد الوحدة النمطية في تطبيق متعدد الخيوط إلى بعض عدم الاستقرار وعدم استقرار التطبيق (وعلى الأخص مع وحدة cgitb).
  3. إذا كان هذا متغيرًا عامًا، فكن على دراية بأن أوقات البحث عن المتغيرات العامة يمكن أن تكون أطول بكثير من أوقات البحث عن المتغيرات المحلية.في هذه الحالة، يمكنك تحقيق تحسين كبير في الأداء عن طريق ربط القاموس بمتغير محلي إذا كنت تستخدمه عدة مرات في نفس السياق.

ومع ذلك، فمن الصعب بعض الشيء أن نقدم لك أي نصيحة محددة دون مزيد من السياق.وبشكل أكثر تحديدًا، أين تستورده؟وما هي الحسابات؟

  1. عامل الجزء المكثف حسابيًا في وحدة منفصلة.ثم على الأقل عند إعادة التحميل، لن تضطر إلى الانتظار.

  2. حاول تفريغ بنية البيانات باستخدام البروتوكول 2.الأمر الذي يجب تجربته سيكون cPickle.dump(FD, protocol=2).من الوثيقة ل cPickle.Pickler:

    Protocol 0 is the
    only protocol that can be written to a file opened in text
    mode and read back successfully.  When using a protocol higher
    than 0, make sure the file is opened in binary mode, both when
    pickling and unpickling. 
    

وانا ذاهب من خلال هذه المسألة نفسها ... الرف، وقواعد البيانات، الخ ... كلها بطيئة جدا لهذا النوع من المشاكل. سوف تحتاج إلى اتخاذ ضرب مرة واحدة، أدخله في متجر مفتاح / فال inmemory مثل رديس. وسوف مجرد العيش هناك في الذاكرة (محذرا من أنها قد تستهلك كمية لا بأس بها من الذاكرة لذلك قد ترغب مربع مخصصة). عليك أن لا داعي لتحميله وستحصل على مجرد النظر في الذاكرة لمفاتيح

r = Redis()
r.set(key, word)

word = r.get(key)

والتوسع في فكرة تأخر حساب، لماذا لا تتحول ديكت في فئة اللوازم (ومخابئ) عناصر الضرورة؟

هل يمكن أيضا استخدام psyco لتسريع التنفيذ العام ...

أو هل يمكن أن مجرد استخدام قاعدة بيانات لتخزين القيم في؟ تحقق من SQLObject، مما يجعل من السهل جدا لتخزين الأشياء إلى قاعدة بيانات.

وهناك حل واضح جدا آخر لهذه المشكلة. عندما يتم إعادة تحميل رمز النطاق الأصلي لا تزال متاحة.

وهكذا ... تفعل شيئا من هذا القبيل سوف نتأكد من يتم تنفيذ هذا الرمز مرة واحدة فقط.

try:
    FD
except NameError:
    FD = FreqDist(word for word in brown.words())
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top