2010-04-06 7 views
6

मैं अपने कोर पर एक बहुत प्रोसेसर-गहन लूप के साथ पाइथन में एक वैज्ञानिक अनुप्रयोग लिख रहा हूं। मैं इसे यथासंभव अनुकूलित करना चाहता हूं, उपयोगकर्ताओं को कम से कम असुविधा पर, जो शायद इसे पायथन स्क्रिप्ट के एक असम्बद्ध संग्रह के रूप में उपयोग करेंगे, और विंडोज, मैक और (मुख्य रूप से उबंटू) लिनक्स का उपयोग करेंगे।पाइथन में उपयोग के लिए फास्ट लूप बनाने के लिए सबसे अधिक प्लेटफॉर्म- और पायथन-संस्करण-स्वतंत्र तरीका क्या है?

वर्तमान में यह पाइथन में न्यूमपी के डैश के साथ लिखा गया है, और मैंने नीचे दिया गया कोड शामिल किया है।

  1. क्या कोई ऐसा समाधान है जो उचित रूप से तेज़ होगा जिसके लिए संकलन की आवश्यकता नहीं होगी? प्लेटफ़ॉर्म-आजादी को बनाए रखने का यह सबसे आसान तरीका प्रतीत होता है।
  2. यदि Pyrex जैसे कुछ का उपयोग करना है, जिसे संकलन की आवश्यकता होती है, तो क्या कई मॉड्यूल को बंडल करने का एक आसान तरीका है और पाइथन उनके बीच पता लगाया गया ओएस और पायथन संस्करण के आधार पर चुनते हैं? क्या पाइथन के प्रत्येक संस्करण के साथ प्रत्येक सिस्टम तक पहुंच की आवश्यकता के बिना मॉड्यूल का संग्रह बनाने का कोई आसान तरीका है?
  3. क्या एक विधि विशेष रूप से बहु-प्रोसेसर अनुकूलन के लिए उधार देती है?

(आप रुचि रखते हैं, पाश, एक साथ पास के चुंबकीय आयनों की एक बड़ी संख्या के योगदान, छोटे बार मैग्नेट के रूप में इलाज जोड़कर एक क्रिस्टल के अंदर एक भी बिंदु पर चुंबकीय क्षेत्र की गणना करने के लिए है। मूल रूप से these की भारी राशि।)

# calculate_dipole 
# ------------------------- 
# calculate_dipole works out the dipole field at a given point within the crystal unit cell 
# --- 
# INPUT 
# mu = position at which to calculate the dipole field 
# r_i = array of atomic positions 
# mom_i = corresponding array of magnetic moments 
# --- 
# OUTPUT 
# B = the B-field at this point 

def calculate_dipole(mu, r_i, mom_i): 
    relative = mu - r_i 
    r_unit = unit_vectors(relative) 
    #4pi/mu0 (at the front of the dipole eqn) 
    A = 1e-7 
    #initalise dipole field 
    B = zeros(3,float) 

    for i in range(len(relative)): 
     #work out the dipole field and add it to the estimate so far 
     B += A*(3*dot(mom_i[i],r_unit[i])*r_unit[i] - mom_i[i])/sqrt(dot(relative[i],relative[i]))**3 
    return B 

उत्तर

1

अजगर उच्च प्रदर्शन अभिकलन के लिए इरादा नहीं है। सी में कोर लूप लिखें और इसे पायथन से कॉल करें।

4

Numpy सरणी प्रसंस्करण के लिए कुछ देशी अनुकूलन का उपयोग करता है। कुछ गति-अप प्राप्त करने के लिए आप Cython के साथ न्यूम्पी सरणी का उपयोग कर सकते हैं।

3

आपका पाइथन कोड शायद आपके लूप को जनरेटर अभिव्यक्ति के साथ बदलकर और mom_i [i], सापेक्ष [i] और r_unit [i] के सभी लुकअप को समानांतर में सभी तीन अनुक्रमों के माध्यम से पुन: स्थापित करके थोड़ा सा बढ़ाया जा सकता है। itertools.izip का उपयोग कर।

यानी की जगह

B = zeros(3,float) 

for i in range(len(relative)): 
    #work out the dipole field and add it to the estimate so far 
    B += A*(3*dot(mom_i[i],r_unit[i])*r_unit[i] - mom_i[i])/sqrt(dot(relative[i],relative[i]))**3 
return B 

साथ:

from itertools import izip 
... 
return sum((A*(3*dot(mom,ru)*ru - mom)/sqrt(dot(rel,rel))**3 
      for mom, ru, rel in izip(mom_i, r_unit, relative)), 
      zeros(3,float)) 

यह भी कोर समीकरण के बाद से अधिक पठनीय IMHO है [i] हर जगह से भरा नहीं है ..

मैं हालांकि संदेह है कि सिथॉन जैसे संकलित भाषा में पूरे कार्य करने की तुलना में यह आपको केवल मामूली लाभ प्राप्त करेगा।

+1

मुझे 20000 अलग-अलग डिप्लोल्स के साथ इसका उपयोग करके 1% से कम गति प्राप्त हुई। –

+0

ठीक है मैंने कहा था कि मुझे मामूली लाभ की उम्मीद है। –

+0

हां, मैंने अभी यह देखने का फैसला किया कि यह कितना लाभ होगा। –

2

एक सरल, लेकिन महत्वपूर्ण गति-अप आपके योग के बाहर ए द्वारा गुणा करने के लिए है। तुम बस बार इसके साथ बी आप इसे वापस के रूप में कर सकते हैं:

for i in range(len(relative)): 
    #work out the dipole field and add it to the estimate so far 
    B += (3*dot(mom_i[i],r_unit[i])*r_unit[i] - mom_i[i])/sqrt(dot(relative[i],relative[i]))**3 

return A*B 

यह दिया के बारे में 8% की गति-अप 20,000 यादृच्छिक द्विध्रुव का उपयोग कर।

उस आसान गति से परे, मैं साइथन (जिसे आमतौर पर पियरेक्स का उपयोग करने की सिफारिश की जाती है) या सिसि से वीव का उपयोग करने की सलाह दी जाएगी। कुछ उदाहरणों के लिए Performance Python पर नज़र डालें और नम्पी/सिस्पी को गति देने के विभिन्न तरीकों की तुलना करें।

यदि आप इसे समानांतर बनाने का प्रयास करना चाहते हैं, तो मैं शुरू करने के लिए Scipy के Parallel Programming को देखने की अनुशंसा करता हूं।

एसओ पर एक और भौतिक विज्ञानी देखना अच्छा है। यहां बहुत सारे नहीं हैं।

संपादित करें:

मैं कुछ Cython कौशल विकसित करने के लिए एक चुनौती के रूप में इस लेने का फैसला किया है और एक Psyco अनुकूलित संस्करण पर एक 10x समय सुधार के बारे में मिल गया। अगर आप मेरा कोड देखना चाहते हैं तो मुझे बताएं।

EDIT2:

ठीक है, वापस चला गया और पाया कि मेरी Cython संस्करण में चीजों को धीमा किया गया था। अब गति-अप 100x से अधिक है। यदि आप चाहते हैं या रे के स्पैड-अप नम्पी संस्करण पर 2x या उससे अधिक के किसी अन्य कारक की आवश्यकता है, तो मुझे बताएं और मैं अपना कोड पोस्ट करूंगा।

Cython स्रोत कोड:

यहाँ Cython कोड है कि मैं ऊपर drummed है:

import numpy as np 
cimport numpy as np 
cimport cython 
cdef extern from "math.h": 
    double sqrt(double theta) 
ctypedef np.float64_t dtype_t 

@cython.boundscheck(False) 
@cython.wraparound(False) 
def calculate_dipole_cython(np.ndarray[dtype_t,ndim=2,mode="c"] mu, 
          np.ndarray[dtype_t,ndim=2,mode="c"] r_i, 
          np.ndarray[dtype_t,ndim=2,mode="c"] mom_i): 
    cdef Py_ssize_t i 
    cdef np.ndarray[dtype_t,ndim=1,mode="c"] tmp = np.empty(3,np.float64) 
    cdef np.ndarray[dtype_t,ndim=1,mode="c"] relative = np.empty(3,np.float64) 
    cdef double A = 1e-7 
    cdef double C, D, F 
    cdef np.ndarray[dtype_t,ndim=1,mode="c"] B = np.zeros(3,np.float64) 
    for i in xrange(r_i.shape[0]): 
     relative[0] = mu[0,0] - r_i[i,0] 
     relative[1] = mu[0,1] - r_i[i,1] 
     relative[2] = mu[0,2] - r_i[i,2] 
     C = relative[0]*relative[0] + relative[1]*relative[1] + relative[2]*relative[2] 
     C = 1.0/sqrt(C) 
     D = C**3 
     tmp[0] = relative[0]*C 
     F = mom_i[i,0]*tmp[0] 
     tmp[1] = relative[1]*C 
     F += mom_i[i,1]*tmp[1] 
     tmp[2] = relative[2]*C 
     F += mom_i[i,2]*tmp[2] 
     F *= 3 
     B[0] += (F*tmp[0] - mom_i[i,0])*D 
     B[1] += (F*tmp[1] - mom_i[i,1])*D 
     B[2] += (F*tmp[2] - mom_i[i,2])*D 
    return A*B 

मैं इसे काफ़ी मुझे लगता है कि अनुकूलित किया है, लेकिन हो सकता है एक छोटे से अधिक आप कर सकते हैं इससे बाहर निकल जाओ। आप अभी भी nppy सी एपीआई से सीधे कॉल के साथ np.zeros और np.empty को प्रतिस्थापित कर सकते हैं, लेकिन इससे कोई फर्क नहीं पड़ता है। जैसा कि यह खड़ा है, यह कोड आपके पास नम्पी अनुकूलित कोड पर 2-3 गुना सुधार देता है। हालांकि, आपको संख्याओं को सही तरीके से पास करने की आवश्यकता है। सरणी को सी प्रारूप में होना आवश्यक है (जो कि अम्पी सरणी के लिए डिफ़ॉल्ट है, लेकिन Numpy में सी स्वरूपित सरणी का स्थानांतरण एक फोरट्रान स्वरूपित सरणी है)।

उदाहरण के लिए, your other question से कोड चलाने के लिए, आपको एस np.random.random((N,3)) के साथ प्रतिस्थापित करने की आवश्यकता होगी। इसके अलावा, `

r_test_fast = reshape_vector(r_test) 

r_test_fast = np.array(np.matrix(r_test)) 

को परिवर्तित करने की यह अंतिम पंक्ति सरल बनाया जा सकता है/तेजी की जरूरत है, लेकिन यह मेरी राय में समय से पहले अनुकूलन होगा।

यदि आपने पहले साइथन का उपयोग नहीं किया है और यह नहीं जानते कि इसे कैसे संकलित किया जाए, तो मुझे बताएं और मुझे सहायता करने में खुशी होगी।

आखिरकार, मैं this paper को देखने की अनुशंसा करता हूं। मैंने इसे अपने अनुकूलन के लिए एक गाइड के रूप में इस्तेमाल किया। अगला चरण बीएलएएस फ़ंक्शंस का उपयोग करने का प्रयास करना होगा जो एसएसई 2 निर्देश सेट का उपयोग करते हैं, एसएसई एपीआई का उपयोग करने की कोशिश कर रहे हैं, या अधिकतर Numpy सी एपीआई का उपयोग करने की कोशिश कर रहे हैं जो एसएसई 2 सामान के साथ इंटरफेस करता है। इसके अलावा, आप समांतरता में देख सकते हैं।

+0

निश्चित रूप से आपके साइथन को देखने में रुचि होगी। :) – Statto

10

यदि आप लूप को खत्म करते हैं और नम्पी के वेक्टरिज्ड ऑपरेशंस का उपयोग करते हैं तो आप इसे अधिक तेज़ी से चलाने के लिए प्राप्त कर सकते हैं। आकार (3, एन) के NumPy सरणी में अपने डेटा रखो और निम्न प्रयास करें:

import numpy as np 

N = 20000 
mu = np.random.random((3,1)) 
r_i = np.random.random((3,N)) 
mom_i = np.random.random((3,N)) 

def unit_vectors(r): 
    return r/np.sqrt((r*r).sum(0)) 

def calculate_dipole(mu, r_i, mom_i): 
    relative = mu - r_i 
    r_unit = unit_vectors(relative) 
    A = 1e-7 

    num = A*(3*np.sum(mom_i*r_unit, 0)*r_unit - mom_i) 
    den = np.sqrt(np.sum(relative*relative, 0))**3 
    B = np.sum(num/den, 1) 
    return B 

इस पाश के लिए एक का उपयोग करने से मेरे लिए लगभग 50 गुना तेजी से चलाता है।

+0

धन्यवाद! यह आश्चर्यजनक है, गति वृद्धि मेरे लिए 100x की तरह है। :) मैंने पूछा [आस-पास के लूप को अनुकूलित करने के बारे में एक अनुवर्ती प्रश्न] (http://stackoverflow.com/questions/2592696/rewriting-a-for-loop-in-pure-numpy-to-decrease निष्पादन-समय), किसी भी इनपुट आभारी रूप से प्राप्त किया। :) – Statto

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

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