2012-08-06 4 views
15

में फास्ट कॉम्प्लेक्स अंक अंकगणितीय क्लोजर में कुछ बुनियादी जटिल संख्या अंकगणितीय लागू कर रहा था, और ध्यान दिया कि यह लगभग संकेतों के साथ लगभग बराबर समकक्ष जावा कोड की तुलना में लगभग 10 गुना धीमा था।क्लोजर

की तुलना करें:

(defn plus [[^double x1 ^double y1] [^double x2 ^double y2]] 
    [(+ x1 x2) (+ y1 y2)]) 

(defn times [[^double x1 ^double y1] [^double x2 ^double y2]] 
    [(- (* x1 x2) (* y1 y2)) (+ (* x1 y2) (* y1 x2))]) 

(time (dorun (repeatedly 100000 #(plus [1 0] [0 1])))) 
(time (dorun (repeatedly 100000 #(times [1 0] [0 1])))) 

उत्पादन:

साथ
"Elapsed time: 69.429796 msecs" 
"Elapsed time: 72.232479 msecs" 

:

public static void main(String[] args) { 
    double[] z1 = new double[] { 1, 0 }; 
    double[] z2 = new double[] { 0, 1 }; 
    double[] z3 = null; 

    long l_StartTimeMillis = System.currentTimeMillis(); 
    for (int i = 0; i < 100000; i++) { 
    z3 = plus(z1, z2); // assign result to dummy var to stop compiler from optimising the loop away 
    } 
    long l_EndTimeMillis = System.currentTimeMillis(); 
    long l_TimeTakenMillis = l_EndTimeMillis - l_StartTimeMillis; 
    System.out.format("Time taken: %d millis\n", l_TimeTakenMillis); 


    l_StartTimeMillis = System.currentTimeMillis(); 
    for (int i = 0; i < 100000; i++) { 
    z3 = times(z1, z2); 
    } 
    l_EndTimeMillis = System.currentTimeMillis(); 
    l_TimeTakenMillis = l_EndTimeMillis - l_StartTimeMillis; 
    System.out.format("Time taken: %d millis\n", l_TimeTakenMillis); 

    doNothing(z3); 
} 

private static void doNothing(double[] z) { 

} 

public static double[] plus (double[] z1, double[] z2) { 
    return new double[] { z1[0] + z2[0], z1[1] + z2[1] }; 
} 

public static double[] times (double[] z1, double[] z2) { 
    return new double[] { z1[0]*z2[0] - z1[1]*z2[1], z1[0]*z2[1] + z1[1]*z2[0] }; 
} 

उत्पादन:

Time taken: 6 millis 
Time taken: 6 millis 

वास्तव में, प्रकार संकेत एक फर्क नहीं पड़ता प्रतीत होता है: यदि मैं उन्हें हटा देता हूं तो मुझे लगभग एक ही परिणाम मिलता है।

"Elapsed time: 137.337782 msecs" 
"Elapsed time: 214.213993 msecs" 

तो मेरी सवाल कर रहे हैं: कैसे मैं जावा कोड के प्रदर्शन के करीब प्राप्त कर सकते हैं क्या वास्तव में अजीब बात है कि अगर मैं एक आरईपीएल बिना Clojure स्क्रिप्ट चलाने के लिए, मैं धीमी परिणाम प्राप्त है? और आरईपीएल के बिना क्लोजर चलाने के दौरान पृथ्वी पर क्यों अभिव्यक्ति का मूल्यांकन करने में अधिक समय लगता है?

अद्यतन ==============

बढ़िया है, deftype में टाइप संकेत के साथ और defn रों में deftype उपयोग कर, और dotimes बजाय repeatedly का उपयोग कर के रूप में के रूप में अच्छा प्रदर्शन देता है या जावा संस्करण से बेहतर है। आप दोनों को धन्यवाद।

(deftype complex [^double real ^double imag]) 

(defn plus [^complex z1 ^complex z2] 
    (let [x1 (double (.real z1)) 
     y1 (double (.imag z1)) 
     x2 (double (.real z2)) 
     y2 (double (.imag z2))] 
    (complex. (+ x1 x2) (+ y1 y2)))) 

(defn times [^complex z1 ^complex z2] 
    (let [x1 (double (.real z1)) 
     y1 (double (.imag z1)) 
     x2 (double (.real z2)) 
     y2 (double (.imag z2))] 
    (complex. (- (* x1 x2) (* y1 y2)) (+ (* x1 y2) (* y1 x2))))) 

(println "Warm up") 
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1))))) 
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1))))) 
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1))))) 
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1))))) 
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1))))) 
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1))))) 

(println "Try with dorun") 
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1))))) 
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1))))) 

(println "Try with dotimes") 
(time (dotimes [_ 100000] 
     (plus (complex. 1 0) (complex. 0 1)))) 

(time (dotimes [_ 100000] 
     (times (complex. 1 0) (complex. 0 1)))) 

आउटपुट:

Warm up 
"Elapsed time: 92.805664 msecs" 
"Elapsed time: 164.929421 msecs" 
"Elapsed time: 23.799012 msecs" 
"Elapsed time: 32.841624 msecs" 
"Elapsed time: 20.886101 msecs" 
"Elapsed time: 18.872783 msecs" 
Try with dorun 
"Elapsed time: 19.238403 msecs" 
"Elapsed time: 17.856938 msecs" 
Try with dotimes 
"Elapsed time: 5.165658 msecs" 
"Elapsed time: 5.209027 msecs" 
+0

आप स्थापित करने की कोशिश की है [ '* चेतावनी दी-ऑन-प्रतिबिंब *'] (http://clojuredocs.org/clojure_core /clojure.core/*warn-on-reflection*) यह देखने के लिए कि क्या कोई प्रतिबिंब छेड़छाड़ कर रहा है? – DaoWen

+0

@ दाओवेन: नहीं, मैंने कभी भी उस सेटिंग का उपयोग नहीं किया है। मैंने अभी भी स्क्रिप्ट को '(सेट! * चेतावनी-पर-प्रतिबिंब * सत्य) के साथ फिर से चलाया है, और इसके अलावा स्टडआउट पर कोई चेतावनी मुद्रित नहीं है, इसका मतलब है कि कोई प्रतिबिंब उपयोग नहीं किया जा रहा है, है ना? बस यह सुनिश्चित करना चाहते हैं कि मैं इसे सही तरीके से उपयोग कर रहा हूं। – OpenSauce

उत्तर

22

अपने धीमी गति से प्रदर्शन के लिए होने की संभावना कारण हैं:

  • Clojure वैक्टर आंतरिक रूप से जावा डबल [] सरणियों की तुलना में अधिक हैवीवेट डेटा संरचनाओं हैं। तो वैक्टर बनाने और पढ़ने में आपके पास अतिरिक्त ओवरहेड है।
  • आप अपने कार्यों के तर्क के रूप में मुक्केबाजी युगल हैं और जब वे वैक्टर में डाल दिए जाते हैं। बॉक्सिंग/अनबॉक्सिंग इस तरह के निम्न-स्तर संख्यात्मक कोड में अपेक्षाकृत महंगा है।
  • प्रकार संकेत (^double) आपकी मदद नहीं कर रहे हैं: जबकि आप सामान्य क्लोजर फ़ंक्शंस पर आदिम प्रकार के संकेत प्राप्त कर सकते हैं, वे वैक्टर पर काम नहीं करेंगे।

कुछ और विवरणों के लिए यह blog post on accelerating primitive arithmetic देखें।

(deftype Complex [^double real ^double imag]) 

और फिर इस प्रकार का उपयोग कर अपने सभी जटिल कार्यों को परिभाषित:

तुम सच में Clojure में तेजी से जटिल संख्याओं चाहते हैं, तो आप शायद deftype, की तरह कुछ का उपयोग कर उन्हें लागू करने की आवश्यकता होगी। यह आपको संपूर्ण अंकगणितीय का उपयोग करने में सक्षम बनाता है, और अच्छी तरह से लिखित जावा कोड के प्रदर्शन के बराबर होना चाहिए।

+0

मुझे लगता है कि [defrecord] (http://clojuredocs.org/clojure_core/clojure.core/defrecord) की सिफारिश की जाती है [deftype] (http://clojuredocs.org/clojure_core/clojure.core/deftype) सरल प्रकारों के लिए इस। – DaoWen

+1

@ दाओवेन - मैं गलत हो सकता हूं लेकिन मेरा मानना ​​है कि आपको डेफटाइप से बेहतर प्रदर्शन मिलेगा - इसमें डिफ्रेकॉर्ड की तुलना में थोड़ा सा ओवरहेड है। defrecord पूर्ण नक्शा जैसे व्यवहार को लागू करता है और "व्यापार ऑब्जेक्ट डेटा" के लिए अधिक उपयुक्त है जबकि डेफटाइप थोड़ा निम्न स्तर डेटा प्रकारों के लिए अधिक उपयुक्त है। – mikera

+0

धन्यवाद, मैंने डेफटाइप/डिफ्रेकॉर्ड के बारे में सोचा लेकिन सोचा कि वे और भी अधिक ओवरहेड पेश कर सकते हैं, लेकिन मैं डेफटाइप को कोशिश करूँगा (और उस ब्लॉग पोस्ट में सामान) और वापस रिपोर्ट करें। – OpenSauce

4
  • मैं बेंचमार्क परीक्षण के बारे में ज्यादा पता नहीं है लेकिन ऐसा लगता है कि आप जरूरत JVM को गर्म करने के लिए जब आप परीक्षण शुरू करते हैं। तो जब आप इसे आरईपीएल में करते हैं तो यह पहले ही गर्म हो चुका है। जब आप स्क्रिप्ट के रूप में दौड़ते हैं तो यह अभी तक नहीं है।

  • जावा में आप सभी विधियों को 1 विधि के अंदर चलाते हैं। plus और times को छोड़कर कोई अन्य विधि नहीं कहा जाता है। क्लोजर में आप अनाम कार्य बनाते हैं और इसे कॉल करने के लिए बार-बार कॉल करते हैं। इसमें कुछ समय लगता है। आप इसे dotimes से बदल सकते हैं।

मेरे कोशिश:

(println "Warm up") 
(time (dorun (repeatedly 100000 #(plus [1 0] [0 1])))) 
(time (dorun (repeatedly 100000 #(times [1 0] [0 1])))) 
(time (dorun (repeatedly 100000 #(plus [1 0] [0 1])))) 
(time (dorun (repeatedly 100000 #(times [1 0] [0 1])))) 
(time (dorun (repeatedly 100000 #(plus [1 0] [0 1])))) 
(time (dorun (repeatedly 100000 #(times [1 0] [0 1])))) 

(println "Try with dorun") 
(time (dorun (repeatedly 100000 #(plus [1 0] [0 1])))) 
(time (dorun (repeatedly 100000 #(times [1 0] [0 1])))) 

(println "Try with dotimes") 
(time (dotimes [_ 100000] 
     (plus [1 0] [0 1]))) 

(time (dotimes [_ 100000] 
     (times [1 0] [0 1]))) 

परिणाम:

Warm up 
"Elapsed time: 367.569195 msecs" 
"Elapsed time: 493.547628 msecs" 
"Elapsed time: 116.832979 msecs" 
"Elapsed time: 46.862176 msecs" 
"Elapsed time: 27.805174 msecs" 
"Elapsed time: 28.584179 msecs" 
Try with dorun 
"Elapsed time: 26.540489 msecs" 
"Elapsed time: 27.64626 msecs" 
Try with dotimes 
"Elapsed time: 7.3792 msecs" 
"Elapsed time: 5.940705 msecs" 
+0

धन्यवाद, यह समझ में आता है। मुझे इसी तरह के परिणाम मिलते हैं। – OpenSauce