(उत्तर क्रेडिट:। रेंज़ो 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
का उपयोग समानांतरवाद प्राप्त करने के लिए। ध्यान दें कि समानांतर संदर्भ में किसी भी ट्रांसड्यूसर का उपयोग करने के लिए स्टेटलेस होना आवश्यक है। यह भी ध्यान रखें कि ConcurrentHashMap
nil
का उपयोग किसी कुंजी या मूल्य के रूप में नहीं करता है; सौभाग्य से, हमें यहां ऐसा करने की ज़रूरत नहीं है।
आउटपुट अंत में एक अपरिवर्तनीय क्लोजर हैशप में परिवर्तित हो गया है। आप उस चरण को हटा सकते हैं और मेरी मशीन पर अतिरिक्त गति के लिए ConcurrentHashMap इंस्टेंस का उपयोग कर सकते हैं, into
चरण को हटाकर पूरे fold
को लगभग 26ms लेते हैं।
संपादित करें 2017/11/20: उपयोगकर्ता @clojuremostly सही ढंग से बताया इस उत्तर के पिछले संस्करण let
ब्लॉक कि समवर्ती हैश नक्शे उदाहरण प्रारंभ, जिसका मतलब था कि बेंचमार्क इस्तेमाल किया अंदर quick-bench
करने के लिए कॉल किया था कि इसके सभी रनों के लिए एक ही उदाहरण। मैंने let
ब्लॉक के बाहर होने के लिए quick-bench
पर कॉल को स्थानांतरित कर दिया। इसने परिणामों को महत्वपूर्ण रूप से प्रभावित नहीं किया।
मुझे नहीं लगता कि आपको अपने दूसरे बेंचमार्क में रन के बीच ConcurrentHashMap का पुन: उपयोग करना चाहिए। – ClojureMostly
@ClojureMostly - अच्छी पकड़, धन्यवाद! उत्तर अपडेट किया गया; अंतिम पैराग्राफ देखें। समय में काफी बदलाव नहीं आया। –