2017-11-17 31 views
5

(प्रश्न क्रेडिट: फर्नांडो अबराव।)क्लोजर में, मैं ट्रांसड्यूसर के साथ 'आवृत्तियों' का एक प्रदर्शन संस्करण कैसे कर सकता हूं?

मैं क्लोजर में ट्रांसड्यूसर के प्रदर्शन लाभों के बारे में सुनता हूं, लेकिन मुझे यकीन नहीं है कि उनका उपयोग कैसे किया जाए। ,

[ 
    { :samplevalue 1.3, ... }, 
    { :othervalue -27.7, ... }, 
    { :samplevalue 7.5, ... }, 
    { :samplevalue 1.9, ... }, 
] 

मैं हर पूर्णांक बिन में कितने :samplevalue रों गिरावट देखना चाहते हैं:

मैं एक qos/device-qos-range समारोह है कि नक्शे, जिनमें से कुछ एक दशमलव :samplevalue होते हैं, इसलिए तरह के अनुक्रम रिटर्न है कहो जैसे इतना:

(frequencies 
    (reduce #(if (not (nil? (:samplevalue %2))) 
      (conj %1 (.intValue (:samplevalue %2)))) 
      [] 
      (qos/device-qos-range origem device qos alvo inicio fim))) 

;; => {1 2, 7 1} 

मैं इस ट्रांसड्यूसर (जैसे reduce द्वारा दिया एक के रूप में) मध्यवर्ती डेटा संरचनाओं समाप्त साथ एक तेजी से संस्करण में बदल सकते हैं? कोड के लिए बोनस पॉइंट जो समांतर प्रसंस्करण करने के लिए एकाधिक कोर का लाभ उठा सकते हैं।

उत्तर

5

(उत्तर क्रेडिट:। रेंज़ो Borgatti (@reborg))

सबसे पहले, चलो कुछ नमूना डेटा है, जो हम बाद में प्रदर्शन परीक्षणों के लिए इस्तेमाल करेंगे सेटअप करते हैं। इस वेक्टर में एक ही कुंजी के साथ 500k मानचित्र हैं। मूल्य 1/5 वें समय ओवरलैप कर रहे हैं।

(def data 
(mapv hash-map 
     (repeat :samplevalue) 
     (concat (range 1e5) 
       (range 1e5) 
       (range 1e5) 
       (range 1e5) 
       (range 1e5)))) 

अब ट्रांसड्यूसर के साथ अपना परिवर्तन करें। ध्यान दें कि यह समाधान समानांतर नहीं है। मैंने आपके .intValue को केवल int पर छोटा कर दिया, जो एक ही चीज करता है। साथ ही, प्रत्येक मानचित्र से :samplevalue को सशर्त रूप से लाने के लिए (keep :samplevalue sequence) तक छोटा किया जा सकता है, जो (remove nil? (map :samplevalue sequence)) के बराबर है। हम बेंचमार्क के लिए Criterium का उपयोग करेंगे।

(require '[criterium.core :refer [quick-bench]]) 
(quick-bench 
    (transduce 
    (comp 
     (keep :samplevalue) 
     (map int)) 
    (completing #(assoc! %1 %2 (inc (get %1 %2 0))) persistent!) 
    (transient {}) 
    data)) 
;; My execution time mean: 405 ms 

ध्यान दें कि हम बुला नहीं कर रहे हैं frequencies एक बाहरी कदम के रूप में इस बार। इसके बजाए, हमने इसे ऑपरेशन में बुनाया है। और जैसे frequencies करता है, हमने अतिरिक्त प्रदर्शन के लिए एक क्षणिक हैशप पर ऑपरेशन किए हैं। हम इस पर persistent! पर कॉल करके अंतिम मूल्य को बीज और completing के रूप में एक क्षणिक हैशैप का उपयोग करके करते हैं।

हम इसे समानांतर बना सकते हैं। अधिकतम प्रदर्शन के लिए, हम एक अपरिवर्तनीय क्लोजर डेटा संरचना के बजाय एक परिवर्तनीय जावा ConcurrentHashMap का उपयोग करते हैं।

(require '[clojure.core.reducers :as r]) 
(import '[java.util HashMap Collections Map] 
     'java.util.concurrent.atomic.AtomicInteger 
     'java.util.concurrent.ConcurrentHashMap) 

(quick-bench 
    (let [concurrency-level (.availableProcessors (Runtime/getRuntime)) 
     m (ConcurrentHashMap. (quot (count data) 2) 0.75 concurrency-level) 
     combinef (fn ([] m) ([_ _])) ; just return `m` from the combine step 
     rf (fn [^Map m k] 
      (let [^AtomicInteger v (or (.get m k) (.putIfAbsent m k (AtomicInteger. 1)))] 
       (when v (.incrementAndGet v)) 
       m)) 
     reducef ((comp (keep :samplevalue) (map int)) rf)] 
    (r/fold combinef reducef data) 
    (into {} m))) 
;; My execution time mean: 70 ms 

यहाँ हम clojure.core.reducers पुस्तकालय से fold का उपयोग समानांतरवाद प्राप्त करने के लिए। ध्यान दें कि समानांतर संदर्भ में किसी भी ट्रांसड्यूसर का उपयोग करने के लिए स्टेटलेस होना आवश्यक है। यह भी ध्यान रखें कि ConcurrentHashMapnil का उपयोग किसी कुंजी या मूल्य के रूप में नहीं करता है; सौभाग्य से, हमें यहां ऐसा करने की ज़रूरत नहीं है।

आउटपुट अंत में एक अपरिवर्तनीय क्लोजर हैशप में परिवर्तित हो गया है। आप उस चरण को हटा सकते हैं और मेरी मशीन पर अतिरिक्त गति के लिए ConcurrentHashMap इंस्टेंस का उपयोग कर सकते हैं, into चरण को हटाकर पूरे fold को लगभग 26ms लेते हैं।

संपादित करें 2017/11/20: उपयोगकर्ता @clojuremostly सही ढंग से बताया इस उत्तर के पिछले संस्करण let ब्लॉक कि समवर्ती हैश नक्शे उदाहरण प्रारंभ, जिसका मतलब था कि बेंचमार्क इस्तेमाल किया अंदर quick-bench करने के लिए कॉल किया था कि इसके सभी रनों के लिए एक ही उदाहरण। मैंने let ब्लॉक के बाहर होने के लिए quick-bench पर कॉल को स्थानांतरित कर दिया। इसने परिणामों को महत्वपूर्ण रूप से प्रभावित नहीं किया।

+0

मुझे नहीं लगता कि आपको अपने दूसरे बेंचमार्क में रन के बीच ConcurrentHashMap का पुन: उपयोग करना चाहिए। – ClojureMostly

+0

@ClojureMostly - अच्छी पकड़, धन्यवाद! उत्तर अपडेट किया गया; अंतिम पैराग्राफ देखें। समय में काफी बदलाव नहीं आया। –

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