2012-02-21 12 views
10

हमारे कोड बेस में कुछ सजावटकर्ता हैं जिनका व्यापक रूप से उपयोग किया जाता है।बड़े पैमाने पर पुन: उपयोग किए गए सजावट वाले सिस्टम को प्रोफाइल करना

जब मैं रनटाइम प्रोफ़ाइल बनाता हूं, तो कॉल ग्राफ़ का एक बड़ा हिस्सा एक घंटे का गिलास जैसा दिखता है; कई फ़ंक्शन एक फ़ंक्शन (सजावट) कहते हैं, जो तब कई फ़ंक्शन कॉल करता है। यह एक कम उपयोगी प्रोफ़ाइल है जो मैं चाहूंगा।

क्या इस स्थिति को सुधारने का कोई तरीका है? सजावट को हटाने का विकल्प नहीं है; यह आवश्यक कार्यक्षमता प्रदान करता है।

हमने इस तथ्य के बाद सीपीआरफाइल डेटा से सजावटी को मैन्युअल रूप से अलग करने पर विचार किया है, लेकिन यह संभव नहीं लगता है, क्योंकि डेटा कॉलर-> कैली रिश्ते में संक्षेप में है, जो कॉलर-> सजावटी-> कैली को नष्ट कर देता है रिश्ते।

+0

क्यों कि उपयोगी नहीं है ? यदि प्रोफाइलिंग सजावट सजावटी की ओर इंगित करती है, तो सजावटी कार्यान्वयन में किए गए किसी भी सुधार में बड़े बदलाव हो सकते हैं। – jcollado

+4

jcollado: क्योंकि सजावटी रनटाइम का एक छोटा हिस्सा है, लेकिन यह कॉलली नहीं हैं।यह मुखौटा उन कॉलियों का "सच्चा कॉलर" क्या है, जो ऑप्टिमाइज़ करने का निर्णय लेने में जानकारी का एक महत्वपूर्ण टुकड़ा हो सकता है। – bukzor

उत्तर

6

(पायथन में 2.6+ या types) new पुस्तकालय की तरह कुछ का उपयोग करना, आप सैद्धांतिक रूप से गतिशील रूप से एक कोड वस्तु और उसके बाद एक समारोह उद्देश्य यह है कि कोड उद्देश्य यह है कि था के आधार पर बना सकते हैं एक अंतर्निहित नाम है कि समारोह के साथ-साथ विविध आप लपेट रहे थे।

इससे आपको <func>.__code__.co_name (जो आमतौर पर केवल पढ़ने के लिए) के रूप में गहराई से चीजों में हेरफेर करने की अनुमति मिलती है।


import functools 
import types 

def metadec(func): 

    @functools.wraps(func) 
    def wrapper(*args, **kwargs): 
     # do stuff 
     return func(*args, **kwargs) 

    c = wrapper.func_code 
    fname = "%s__%s" % (func.__name__, wrapper.__name__) 

    code = types.CodeType(
       c.co_argcount, 
       c.co_nlocals, 
       c.co_stacksize, 
       c.co_flags, 
       c.co_code,   
       c.co_consts,   
       c.co_names, 
       c.co_varnames, 
       c.co_filename, 
       fname, # change the name 
       c.co_firstlineno, 
       c.co_lnotab, 
       c.co_freevars, 
       c.co_cellvars, 
      ) 

    return types.FunctionType(
      code, # Use our updated code object 
      wrapper.func_globals, 
      fname, # Use the updated name 
      wrapper.func_defaults, 
      wrapper.func_closure, 
     ) 

(functools.wraps अभी भी क्रम में यहाँ प्रयोग किया जाता है आदि docstrings, मॉड्यूल के नाम की तरह चीजों के पास के माध्यम से, के लिए अनुमति देने के लिए)


In [1]: from metadec import metadec 

In [2]: @metadec 
    ...: def foobar(x): 
    ...:  print(x) 
    ...:  
    ...:  

In [3]: foobar.__name__ 
Out[3]: 'foobar__wrapper' 

In [4]: foobar(1) 
1 
+0

कार्य कोड एक सिद्धांत को एक उत्तर में बनाता है :) – bukzor

+0

मेरा उन्नत उत्तर देखें। :-) – kindall

5

मुझे लगता है कि यह सजावट करने वाला नहीं है जो आपके प्रोफाइलिंग को छेड़छाड़ कर रहा है, बल्कि रैपर फ़ंक्शन सजावट द्वारा बनाया गया है। और ऐसा इसलिए हो रहा है क्योंकि सभी रैपर कार्यों का एक ही नाम है। इसे संबोधित करने के लिए, सजावटकर्ता ने रैपर फ़ंक्शन का नाम बदल दिया है।

def decorator(func): 

    def wrapper(*args): 
     print "enter func", func.__name__ 
     return func(*args) 

    wrapper.__name__ += "_" + func.__name__ 
    return wrapper 

तुम भी functools.wraps() इस्तेमाल कर सकते हैं, लेकिन तब आवरण समारोह के नाम पर समारोह यह लपेटकर है के नाम से मेल होगा। मुझे लगता है कि प्रोफाइलिंग के लिए ठीक होगा।

अब, फ़ंक्शन के कोड ऑब्जेक्ट का नाम भी है। पाइथन स्टैक्स पर फ़ंक्शंस के संदर्भों को संदर्भित नहीं करता है, केवल कोड ऑब्जेक्ट्स के लिए, इसलिए यदि प्रोफाइलर को स्टैक्स फ्रेम से रैपर फ़ंक्शन का नाम मिल रहा है, तो यह यह नाम प्राप्त होगा। सामान्य तरीके से परिभाषित रैपर कोड ऑब्जेक्ट को साझा करते हैं (भले ही फ़ंक्शन ऑब्जेक्ट अलग है) जब तक कि आप स्पष्ट रूप से प्रत्येक ऑब्जेक्ट फ़ंक्शन के लिए कोड ऑब्जेक्ट और फ़ंक्शन ऑब्जेक्ट का पुनर्निर्माण नहीं करते। यह काफी अधिक काम है और बहुत सीपीथॉन-विशिष्ट (संस्करण-विशिष्ट भी हो सकता है)। लेकिन यहाँ कैसे आप इसके बारे में जाना दे सकता है:

from types import FunctionType, CodeType  

def decorator(func): 

    def wrapper(*args): 
     print "enter func", func.__name__ 
     return func(*args) 

    name = wrapper.__name__ + "_" + func.__name__ 

    func_code = wrapper.func_code 
    new_code = CodeType(
      func_code.co_argcount, func_code.co_nlocals, func_code.co_stacksize, 
      func_code.co_flags, func_code.co_code, func_code.co_consts, 
      func_code.co_names, func_code.co_varnames, func_code.co_filename, 
      name, func_code.co_firstlineno, func_code.co_lnotab, 
      func_code.co_freevars, func_code.co_cellvars) 
    wrapper = FunctionType(
      new_code, wrapper.func_globals, name, wrapper.func_defaults, 
      wrapper.func_closure) 

    return wrapper 

दोनों समारोह का नाम और कोड वस्तु का नाम यहाँ wrapper_originalfuncname की तैयारी में हैं और वे इस प्रकार प्रोफाइलर में लिपटे समारोह से अलग की जाती किया जाना चाहिए। आप आसानी से उन्हें केवल मूल कार्य के नाम पर सेट कर सकते हैं ताकि उनके रन टाइम को इसके बजाय मूल फ़ंक्शन के साथ रोल किया जा सके।

+0

सजावट को मानते हुए 'functools.wraps()' जैसी कुछ चीज़ों के साथ ठीक से बनाया गया था, '__name__' पहले से ही सेट हो रहा होगा। – Amber

+0

इस जवाब को उभारा क्योंकि अगर प्रोफाइलर को स्टैक फ्रेम से फ़ंक्शन का नाम मिल रहा है, तो यह वास्तव में कोड ऑब्जेक्ट का नाम होगा, न कि फ़ंक्शन का। – kindall

+0

मैंने अपनी भी अद्यतन की है (मैंने सेलवार्स और फ्रीवार्स को दुर्घटना से क्वार के रूप में पारित किया था, जब उन्हें पॉजर्ग माना जाता था, जो इसे सीगफॉल्ट बना रहा था)। मैं वहां 'functools.wraps() 'रख रहा हूं क्योंकि इस तरह' __module__', '__doc__' जैसी चीजें भी गुजरती हैं। – Amber

संबंधित मुद्दे