2010-11-11 8 views
6

संदर्भ: ओपनजीएल में 2 डी शिखर के मेरे पायथन कोड पास सरणी।फ्लोट में ट्यूल्स की सूची से सी सरणी बनाने के लिए पाइथन में सबसे तेज़ तरीका क्या है?

मैंने 2 दृष्टिकोणों का परीक्षण किया, एक प्रकार के साथ, दूसरा संरचना के साथ, दूसरा बाद में दो गुना तेज है।

from random import random 
points = [(random(), random()) for _ in xrange(1000)] 

from ctypes import c_float 
def array_ctypes(points): 
    n = len(points) 
    return n, (c_float*(2*n))(*[u for point in points for u in point]) 

from struct import pack 
def array_struct(points): 
    n = len(points) 
    return n, pack("f"*2*n, *[u for point in points for u in point]) 

कोई अन्य विकल्प? इस तरह के कोड को तेज करने के बारे में कोई संकेत (और हाँ, यह मेरे कोड की एक बाधा है)?

+0

मैंने इस प्रश्न को न्यूज ग्रुप gmane.comp.python.opengl.user पर भी पोस्ट किया है, जो नीचे दिए गए समान उत्तर लौटाता है। –

उत्तर

2

आप साइथन का प्रयास कर सकते हैं। मेरे लिए, यह देता है:

function  usec per loop: 
       Python Cython 
array_ctypes 1370 1220 
array_struct 384  249 
array_numpy  336  339 

तो Numpy केवल, मेरे हार्डवेयर पर 15% लाभ (पुराने लैपटॉप चल रहा है Windows XP) देता है जबकि Cython के बारे में 35% देता है (आपके वितरित कोड में किसी भी अतिरिक्त निर्भरता के बिना)।

आप अपनी आवश्यकता है कि प्रत्येक बिंदु तैरता के एक टपल है ढीला कर सकते हैं और 'अंक' तैरता की एक चपटा सूची करते हैं:

def array_struct_flat(points): 
    n = len(points) 
    return pack(
     "f"*n, 
     *[ 
      coord 
      for coord in points 
     ] 
    ) 

points = [random() for _ in xrange(1000 * 2)] 

फिर परिणामी उत्पादन समय चला जाता है एक ही है, लेकिन और नीचे:

function   usec per loop: 
        Python Cython 
array_struct_flat   157 

Cython काफी हद तक इससे बेहतर करने में सक्षम हो सकता है भी, अगर कोई होशियार की तुलना में मेरे कोड को स्थिर प्रकार घोषणाओं जोड़ना चाहते थे। ('साइथन-ए test.pyx' चलाना इसके लिए अमूल्य है, यह आपको एक एचटीएमएल फाइल दिखाता है जहां आपको सबसे धीमा (पीला) सादा पाइथन आपके कोड में है, बनाम पायथन जिसे शुद्ध सी (सफेद) में परिवर्तित कर दिया गया है। यही कारण है कि मैं ऊपर बाहर इतने सारे लाइनों पर है, क्योंकि रंग प्रति-लाइन किया जाता है कोड का प्रसार, तो यह उस तरह इसे बाहर का प्रसार करने में मदद करता है)

पूर्ण Cython निर्देश यहां हैं:। http://docs.cython.org/src/quickstart/build.html

Cython उत्पादन हो सकता है आपके पूरे कोडबेस में समान प्रदर्शन लाभ, और आदर्श स्थितियों में उचित स्थिर टाइपिंग लागू होने के साथ, दस या सौ के कारकों द्वारा गति में सुधार कर सकते हैं।

0

आप array (नोटिस भी सूची समझ जनरेटर अभिव्यक्ति के बजाय) का उपयोग कर सकते हैं:

array("f", (u for point in points for u in point)).tostring() 

एक और अनुकूलन अंक शुरू से चपटा रखने के लिए किया जाएगा।

+0

मैंने अपने पहले प्रयासों में जेनरेटर की कोशिश की, और यह पता चला कि यह कार्यों को धीमा कर देता है। – rndblnch

+0

(और यह भी इस सरणी संस्करण को धीमा करता है)। वैसे, सूची समझ के साथ भी, सरणी आधारित समाधान संरचना संस्करण की तुलना में अभी भी 20% धीमी है ... – rndblnch

3

आप किसी भी ओवरहेड के बिना PyOpenGL को numpy arrays पास कर सकते हैं।

import numpy as np 
def array_numpy(points): 
    n = len(points) 
    return n, np.array(points, dtype=np.float32) 

अपने कंप्यूटर पर (numpy सरणी के data विशेषता एक बफर कि अंतर्निहित सी डेटा संरचना है कि सरणी आप का निर्माण कर रहे रूप में एक ही जानकारी होती है ओर इशारा करता है), इस बारे में 40% की तुलना में तेजी है struct-आधारित दृष्टिकोण।

+0

प्रभावशाली! मैं अपने कोड पर numpy निर्भरता जोड़ना नहीं चाहता था, लेकिन ऐसा लगता है कि यह इसके लायक है। (साइड नोट: डीटीपी पैरामीटर निर्दिष्ट नहीं करना एक कारक द्वारा perf को मारता है 10) – rndblnch

+0

क्या इस तकनीक को आगे बढ़ाया जा सकता है, numpy सरणी को आगे बढ़ाकर, और फिर प्रत्येक फ्रेम के रूप में तत्वों को अद्यतन कर सकते हैं। मैं परिस्थितियों की कल्पना कर रहा हूं जहां चरम अधिकतर स्थैतिक होंगे, लेकिन कभी-कभी उनमें से एक हिस्से को एनिमेशन के लिए अद्यतन करने की आवश्यकता होगी। –

+0

आपको मौजूद होने के बाद सरणी में हेरफेर करने के लिए numpy का उपयोग करने से अतिरिक्त लाभ भी मिल सकते हैं। जैसे आप पदों की एक सरणी में वेगों की एक सरणी जोड़ सकते हैं। यह विशेष रूप से कण प्रणालियों जैसी चीजों के लिए अच्छा हो सकता है, जहां आपके पायथन कोड को परिणामी स्थितियों के मूल्य तक लगातार पहुंच की आवश्यकता नहीं होती है। –

1

एक और विचार है जिसे मैंने भर दिया। मैं अभी यह प्रोफ़ाइल करने के लिए समय नहीं है, लेकिन इस मामले में किसी और करता है:

# untested, but I'm fairly confident it runs 
# using 'flattened points' list, i.e. a list of n*2 floats 
points = [random() for _ in xrange(1000 * 2)] 
c_array = c_float * len(points * 2) 
c_array[:] = points 

है यही कारण है, पहले हम ctypes सरणी बनाने लेकिन उसे पॉप्युलेट नहीं है। फिर हम स्लाइस नोटेशन का उपयोग करके इसे पॉप्युलेट करते हैं। मुझसे ज्यादा चालाक लोग मुझे बताते हैं कि इस तरह के टुकड़े को सौंपने से प्रदर्शन में मदद मिल सकती है।यह हमें * पुन: उपयोग करने योग्य सिंटैक्स का उपयोग किए बिना सीधे असाइनमेंट के आरएचएस पर एक सूची या पुनरावर्तनीय पास करने की अनुमति देता है, जो पुनरावर्तनीय के कुछ मध्यवर्ती wrangling प्रदर्शन करेगा। मुझे संदेह है कि यह पिगलेट के बैचों को बनाने की गहराई में होता है।

संभवतः आप केवल एक बार c_array बना सकते हैं, फिर पॉइंट सूची में हर बार इसे फिर से असाइन करें (उपरोक्त कोड में अंतिम पंक्ति)।

संभवतः एक विकल्प तैयार करने जो अंक की मूल परिभाषा ((एक्स, वाई) tuples की एक सूची।) स्वीकार करता है इस तरह कुछ है:

# very untested, likely contains errors 
# using a list of n tuples of two floats 
points = [(random(), random()) for _ in xrange(1000)] 
c_array = c_float * len(points * 2) 
c_array[:] = chain(p for p in points) 
+0

प्रतिक्रिया @DanielLemire के लिए धन्यवाद। ब्याज से, क्या आपने इस उत्तर से सुझाए गए दृष्टिकोण दोनों को आजमाया है? –

+0

इस प्रश्न का मेरा अद्यतन उत्तर देखें। –

1

यदि प्रदर्शन एक मुद्दा है, आप नहीं चाहते हैं स्टार ऑपरेशन के साथ सीटीपीएस एरे का उपयोग करने के लिए (उदाहरण के लिए, (ctypes.c_float * size)(*t))।

मेरे परीक्षण में pack पते के एक कलाकार (या from_buffer फ़ंक्शन का उपयोग करके array मॉड्यूल के उपयोग के बाद सबसे तेज़ है।

python3 convert.py 
0.004665990360081196 
0.004661010578274727 
0.026358536444604397 
0.0028003649786114693 
0.005843495950102806 
0.009067213162779808 

शुद्ध विजेता पैक है: numpy दृष्टिकोण के बारे में आधा गति है, जबकि

import timeit 
repeat = 100 
setup="from struct import pack; from random import random; import numpy; from array import array; import ctypes; t = [random() for _ in range(2* 1000)];" 
print(timeit.timeit(stmt="v = array('f',t); addr, count = v.buffer_info();x = ctypes.cast(addr,ctypes.POINTER(ctypes.c_float))",setup=setup,number=repeat)) 
print(timeit.timeit(stmt="v = array('f',t);a = (ctypes.c_float * len(v)).from_buffer(v)",setup=setup,number=repeat)) 
print(timeit.timeit(stmt='x = (ctypes.c_float * len(t))(*t)',setup=setup,number=repeat)) 
print(timeit.timeit(stmt="x = pack('f'*len(t), *t);",setup=setup,number=repeat)) 
print(timeit.timeit(stmt='x = (ctypes.c_float * len(t))(); x[:] = t',setup=setup,number=repeat)) 
print(timeit.timeit(stmt='x = numpy.array(t,numpy.float32).data',setup=setup,number=repeat)) 

array.array दृष्टिकोण अपने परीक्षण में थोड़ा तेजी से जोनाथन हार्टले का दृष्टिकोण की तुलना में है।

+0

शानदार। प्रजनन तुलनात्मक माप - पृष्ठ पर सबसे अच्छा जवाब। –

+0

समय के साथ (संख्या = 10), मुझे समय के क्रम में भिन्नता की उचित मात्रा दिखाई देती है। मुझे काफी लगातार क्रम में बसने से पहले इसे 1000 तक बढ़ाना पड़ा। –

+0

डैनियल की लिपि का उपयोग करके, मैं पैक को मापने के लिए सबसे तेज प्रविष्टि में भी मापता हूं। हालांकि, मैं उत्सुक हूं क्योंकि 'पैक' उदाहरण में कॉल में '* t' वाक्यविन्यास का अर्थ है कि 'पैक' के तर्कों के लिए सूची 'टी' को टुपल में अनपॅक किया जा रहा है। ऐसा लगता है कि यहां अभी भी कुछ अक्षमता है, इसलिए संभवतः इसमें सुधार किया जा सकता है। –

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

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