2011-06-29 9 views
13

मारा मैं अजगर में एक साधारण raytracer है। एक छवि 200x200 प्रतिपादन 4 मिनट लेता है, जो मेरे स्वाद के लिए निश्चित रूप से बहुत अधिक है। मैं स्थिति में सुधार करना चाहता हूं।प्रदर्शन सुधारना समारोह

कुछ बिंदुओं: मैं प्रत्येक पिक्सेल प्रति एकाधिक किरणों गोली मार पिक्सेल प्रति 16 किरणों के कुल योग के लिए (एंटीलायज़िंग़ प्रदान करने के लिए)। 200x200x16 640000 किरणों की एक भव्य कुल है। दृश्य में कई क्षेत्र वस्तुओं पर प्रभाव के लिए प्रत्येक किरण का परीक्षण किया जाना चाहिए। रे ने भी एक नहीं बल्कि तुच्छ वस्तु

class Ray(object): 
    def __init__(self, origin, direction): 
     self.origin = numpy.array(origin) 
     self.direction = numpy.array(direction) 

क्षेत्र से थोड़ा अधिक जटिल है, और हिट/nohit के लिए तर्क किया जाता है: अब

class Sphere(object): 
    def __init__(self, center, radius, color): 
     self.center = numpy.array(center) 
     self.radius = numpy.array(radius) 
     self.color = color 

    @profile 
    def hit(self, ray): 
     temp = ray.origin - self.center 
     a = numpy.dot(ray.direction, ray.direction) 
     b = 2.0 * numpy.dot(temp, ray.direction) 
     c = numpy.dot(temp, temp) - self.radius * self.radius 
     disc = b * b - 4.0 * a * c 

     if (disc < 0.0): 
      return None 
     else: 
      e = math.sqrt(disc) 
      denom = 2.0 * a 
      t = (-b - e)/denom 
      if (t > 1.0e-7): 
       normal = (temp + t * ray.direction)/self.radius 
       hit_point = ray.origin + t * ray.direction 
       return ShadeRecord.ShadeRecord(normal=normal, 
               hit_point=hit_point, 
               parameter=t, 
               color=self.color)   

      t = (-b + e)/denom 

      if (t > 1.0e-7): 
       normal = (temp + t * ray.direction)/self.radius    hit_point = ray.origin + t * ray.direction 
       return ShadeRecord.ShadeRecord(normal=normal, 
               hit_point=hit_point, 
               parameter=t, 
               color=self.color)  

     return None  

, मैं कुछ रूपरेखा भाग गया, और यह है कि सबसे लंबे समय तक प्रसंस्करण प्रकट होता है समय हिट() फ़ंक्शन

ncalls tottime percall cumtime percall filename:lineno(function) 
    2560000 118.831 0.000 152.701 0.000 raytrace/objects/Sphere.py:12(hit) 
    1960020 42.989 0.000 42.989 0.000 {numpy.core.multiarray.array} 
     1 34.566 34.566 285.829 285.829 raytrace/World.py:25(render) 
    7680000 33.796 0.000 33.796 0.000 {numpy.core._dotblas.dot} 
    2560000 11.124 0.000 163.825 0.000 raytrace/World.py:63(f) 
    640000 10.132 0.000 189.411 0.000 raytrace/World.py:62(hit_bare_bones_object) 
    640023 6.556 0.000 170.388 0.000 {map} 

यह मैं आश्चर्य नहीं है में है, और मैं जितना संभव हो उतना इस मूल्य को कम करना चाहते हैं। मैं रूपरेखा लाइन गुजरती है, और परिणाम

Line #  Hits   Time Per Hit % Time Line Contents 
============================================================== 
    12            @profile 
    13            def hit(self, ray): 
    14 2560000  27956358  10.9  19.2   temp = ray.origin - self.center 
    15 2560000  17944912  7.0  12.3   a = numpy.dot(ray.direction, ray.direction) 
    16 2560000  24132737  9.4  16.5   b = 2.0 * numpy.dot(temp, ray.direction) 
    17 2560000  37113811  14.5  25.4   c = numpy.dot(temp, temp) - self.radius * self.radius 
    18 2560000  20808930  8.1  14.3   disc = b * b - 4.0 * a * c 
    19             
    20 2560000  10963318  4.3  7.5   if (disc < 0.0): 
    21 2539908  5403624  2.1  3.7    return None 
    22             else: 
    23  20092  75076  3.7  0.1    e = math.sqrt(disc) 
    24  20092  104950  5.2  0.1    denom = 2.0 * a 
    25  20092  115956  5.8  0.1    t = (-b - e)/denom 
    26  20092  83382  4.2  0.1    if (t > 1.0e-7): 
    27  20092  525272  26.1  0.4     normal = (temp + t * ray.direction)/self.radius 
    28  20092  333879  16.6  0.2     hit_point = ray.origin + t * ray.direction 
    29  20092  299494  14.9  0.2     return ShadeRecord.ShadeRecord(normal=normal, hit_point=hit_point, parameter=t, color=self.color) 

तो, ऐसा लगता है कि समय के सबसे कोड के इस हिस्से में खर्च किया जाता है है:

 temp = ray.origin - self.center 
     a = numpy.dot(ray.direction, ray.direction) 
     b = 2.0 * numpy.dot(temp, ray.direction) 
     c = numpy.dot(temp, temp) - self.radius * self.radius 
     disc = b * b - 4.0 * a * c 

कहाँ मैं वास्तव में एक बहुत कुछ नहीं दिख रहा है अनुकूलन करने के लिए। क्या आपको कोई विचार है कि इस कोड को सी के बिना और अधिक प्रदर्शन करने के लिए कैसे बनाया जाए?

+3

+1 बहुत अच्छी तरह से प्रस्तुत किया गया। मुझे पायथन नहीं पता है, इसलिए एक प्रश्न के रूप में: numpy.dot एक सी कार्यान्वयन के लिए बुला रहा है? यदि नहीं, तो शायद आप मैन्युअल डॉट उत्पाद गणना करके गति में सुधार कर सकते हैं। – Phrogz

+0

हां, नमस्ते को सी में लागू किया गया है। यही कारण है कि मुझे लगता है कि सी –

+0

में हिट फ़ंक्शन को पुन: कार्यान्वित करने के लिए बहुत कुछ नहीं है, आपकी दिशा वैक्टर यूनिट वैक्टर हैं? क्या आप उन्हें '__init__' में इकाई वैक्टर बना सकते हैं? यदि हां, तो आपका डॉट उत्पाद गणित सरल हो जाता है। – underrun

उत्तर

1
यह आप self.radius * self.radius self.radius2 के रूप में और 1/self.radius self.one_over_radius के रूप में की तरह आम subexpressions memoizing से लाभ हो सकता है जैसे कोड के साथ

। पायथन दुभाषिया का ऊपरी भाग ऐसे छोटे सुधारों पर हावी हो सकता है।

+0

मैंने कोशिश की। अच्छा विचार है, लेकिन यह बहुत कुछ नहीं बदलता है। मुझे एहसास हुआ कि मैं एक साधारण फ्लोट की बजाय self.radius को एक numpy सरणी के रूप में उपयोग करने में त्रुटि कर रहा था। फिर, एक मापनीय परिवर्तन नहीं। मैं बदलते डिजाइन पर विचार कर रहा हूं और कॉल की संख्या को कम करने की कोशिश कर रहा हूं, लेकिन मुझे संदेह है कि प्रदर्शन में बहुत कुछ हासिल किया जा सकता है, और कोड की स्पष्टता में बहुत कुछ खो जाना है। मुझे लगता है कि मुझे जल्द ही समानांतर जाना होगा। –

+0

तो सच है। एक सी कार्यान्वयन उन चीज़ों की गणना करेगा जो पाइथन वीएम से एक ओपोड ला सकता है। वे मान्य अनुकूलन हैं हालांकि छोटे दिखाएंगे लेकिन मापनीय सुधार एक अच्छा प्रदर्शन करने वाला सी प्रोग्राम है। – phkahler

7

1) रे ट्रेसिंग मज़ा है, लेकिन आप सब के बारे में प्रदर्शन, डंप अजगर पर देखभाल और सी नहीं सी ++ के लिए स्विच जब तक आप, बस सी

2) बड़ा में जीत सुपर विशेषज्ञ किसी तरह का कर रहे हैं एकाधिक (20 या अधिक) वस्तुओं वाले दृश्य चौराहे परीक्षणों की संख्या को कम करने के लिए स्थानिक सूचकांक का उपयोग करना है। लोकप्रिय विकल्प केडी-पेड़, ऑक्टट्री, एएबीबी हैं।

3) यदि आप गंभीर हैं, तो बाहर ompf.org देखें - यह इस बात के लिए स्रोत है।

4) ऑप्टिमाइज़ेशन के बारे में पूछने वाले अजगर के साथ ओएमपीएफ पर न जाएं - अधिकांश लोग 100 हजार त्रिकोण के साथ एक इनडोर दृश्य के माध्यम से प्रति सेकंड 2 मिलियन से 2 मिलियन किरणों को शूट कर सकते हैं ... प्रति कोर।

मैं अजगर और रे ट्रेसिंग से प्यार है, लेकिन उन्हें एक साथ डाल पर विचार कभी नहीं होगा। इस मामले में, सही अनुकूलन भाषा स्विच करना है।

+0

+1 मैं इसके साथ बिल्कुल सहमत हूं। पायथन प्रोटोटाइप के लिए अच्छा हो सकता है, लेकिन एक बार जब आप अपने एल्गोरिदम साबित कर लेते हैं और प्रदर्शन के बारे में परवाह करते हैं तो यह सी/सी ++ (या कुछ जो उचित बाइनरी के लिए संकलित होता है) के लिए समय है; स्वयं को कुछ वीएम/जीसी भाषा नहीं करना चाहिए)। हर तरह से एक अजगर घटक के रूप में रे ट्रेसर को लपेटें और इसे उच्च स्तर के सिस्टम में उपयोग करें। – timday

+0

@ टिमडे - 1 99 5 के आसपास मैंने सी ++ में एक रे ट्रेसिंग ओएलई घटक बनाया (जिसे वे तब भी बुलाया गया था) और विजुअल बेसिक ऐप से कैमरा आंदोलन किया। 120x 9 0 पिक्सेल छवियां कई एफपीएस थीं जिन्हें मैंने अपनी लाइब्रेरी के चारों ओर पियरगैम लपेटने पर विचार किया है :-) इसके लिए कोई समय नहीं ... – phkahler

+0

मुझे लगता है कि सी ++ इन दिनों उच्च प्रदर्शन रेराट्रिंग कोड लिखने के लिए ठीक है। वास्तव में, मैं इसे एक जीवित रहने के लिए करता हूं। आपको केवल अपवाद और एसटीएल जैसे सी ++ के क्रैपी धीमी हिस्सों का उपयोग करने से बचने की आवश्यकता है। – Crashworks

1

एक नाबालिग अनुकूलन है: a और b * b हमेशा सकारात्मक हैं, इसलिए disc < 0.0 अगर (c > 0 && b < min(a, c)) सच है। इस मामले में आप b * b - 4.0 * a * c की गणना से बच सकते हैं। आपके द्वारा किए गए प्रोफाइल को देखते हुए मुझे लगता है कि हिट के रन टाइम के 7% अधिकतर शेव करेंगे।

1

आपका सबसे अच्छा शर्त देखने टेबल और जितना संभव हो उतना पूर्व परिकलित मानों का उपयोग करने के लिए किया जाएगा।

मेरी टिप्पणी करने के लिए आपकी प्रतिक्रिया दर्शाता है कि आपके रे दिशा वैक्टर इकाई वैक्टर कर रहे हैं, महत्वपूर्ण अनुभाग आप सूचीबद्ध आप सही बल्ले से दूर कम से कम एक अनुकूलन कर सकते हैं।कोई भी वेक्टर डॉट स्वयं लंबाई वर्ग होता है, इसलिए एक यूनिट वेक्टर डॉट हमेशा होता है 1.

इसके अलावा, त्रिज्या वर्ग की गणना करें (आपके क्षेत्र के __init__ फ़ंक्शन में)।

तो फिर तुम मिल गया है:

temp = ray.origin - self.center 
a = 1 # or skip this and optimize out later 
b = 2.0 * numpy.dot(temp, ray.direction) 
c = numpy.dot(temp, temp) - self.radius_squared 
disc = b * b - 4.0 * c 

अस्थायी डॉट अस्थायी आप sum(map(lambda component: component*component, temp)) के बराबर देने के लिए जा रहा है ... मुझे यकीन है कि जो तेजी से हालांकि है नहीं कर रहा हूँ।

+0

पर वापस गया कुछ परीक्षण - numpy.dot() योग (मानचित्र()) से बहुत तेज है। – underrun

6

अपना कोड देखकर, ऐसा लगता है कि आपकी मुख्य समस्या यह है कि आपके पास कोड की रेखाएं हैं जिन्हें 2560000 बार कहा जा रहा है। उस कोड में आप किस तरह का काम कर रहे हैं इस पर ध्यान दिए बिना इसमें बहुत समय लगेगा। हालांकि, numpy का उपयोग करके, आप इस काम को बहुत कम संख्या में numpy कॉल में जोड़ सकते हैं।

पहली बात यह है कि अपनी किरणों को बड़े सरणी में एक साथ जोड़ना है। रे ऑब्जेक्ट का उपयोग करने के बजाय जिसमें उत्पत्ति और दिशा के लिए 1x3 वैक्टर हैं, Nx3 arrays का उपयोग करें जिसमें हिट डिटेक्शन के लिए आपको आवश्यक सभी किरणें हों। अगले भाग के लिए

temp = rays.origin - self.center 
b = 2.0 * numpy.sum(temp * rays.direction,1) 
c = numpy.sum(numpy.square(temp), 1) - self.radius * self.radius 
disc = b * b - 4.0 * c 

आप

possible_hits = numpy.where(disc >= 0.0) 
a = a[possible_hits] 
disc = disc[possible_hits] 
... 

उपयोग कर सकते हैं बस मानों विभेदक परीक्षा उत्तीर्ण के साथ जारी रखने के लिए: अपने हिट समारोह के शीर्ष इस तरह देख खत्म हो जाएगा। आप आसानी से इस तरह के परिमाण प्रदर्शन सुधार के आदेश प्राप्त कर सकते हैं।

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