2015-02-02 24 views
13

में एक सरणी को अनुक्रमणित करते समय स्मृति आवंटन से बचें प्रश्न: मैं स्मृति आवंटन को ट्रिगर किए बिना एक सरणी में अनुक्रमित करना चाहता हूं, खासकर जब किसी फ़ंक्शन में अनुक्रमित तत्वों को पारित करना। जूलिया डॉक्स पढ़ने से, मुझे लगता है इस सवाल का जवाब sub समारोह का उपयोग कर के आसपास घूमती है, लेकिन काफी कैसे नहीं देख सकता ...जूलिया

कार्य उदाहरण: मैं Float64 (x) की एक बड़ी वेक्टर और उसके बाद एक सूचकांक का निर्माण x में प्रत्येक अवलोकन के लिए।

N = 10000000 
x = randn(N) 
inds = [1:N] 

अब मैं समय x और x[inds] से अधिक mean समारोह (मैं mean(randn(2)) पहले समय में किसी भी संकलक अनियमितताओं से बचने के लिए चलाने):

@time mean(x) 
@time mean(x[inds]) 

यह एक समान गणना है, लेकिन उम्मीद के रूप में के परिणाम समय हैं:

elapsed time: 0.007029772 seconds (96 bytes allocated) 
elapsed time: 0.067880112 seconds (80000208 bytes allocated, 35.38% gc time) 

तो, क्या मनमाने ढंग से स्मृति आवंटन समस्या के आसपास एक तरीका है inds (और सरणी और फ़ंक्शन की मनमाने ढंग से पसंद) के hoices?

उत्तर

11

संपादित करें: पूरी तस्वीर पाने के लिए भी tholy का जवाब पढ़ें!

सूचकांक की एक सरणी का उपयोग करते समय, स्थिति नहीं महान अभी जूलिया 0.4-पूर्व पर (फरवरी 2015 की शुरुआत) है:

julia> N = 10000000; 
julia> x = randn(N); 
julia> inds = [1:N]; 
julia> @time mean(x) 
elapsed time: 0.010702729 seconds (96 bytes allocated) 
elapsed time: 0.012167155 seconds (96 bytes allocated) 
julia> @time mean(x[inds]) 
elapsed time: 0.088312275 seconds (76 MB allocated, 17.87% gc time in 1 pauses with 0 full sweep) 
elapsed time: 0.073672734 seconds (76 MB allocated, 3.27% gc time in 1 pauses with 0 full sweep) 
elapsed time: 0.071646757 seconds (76 MB allocated, 1.08% gc time in 1 pauses with 0 full sweep) 
julia> xs = sub(x,inds); # Only works on 0.4 
julia> @time mean(xs) 
elapsed time: 0.057446177 seconds (96 bytes allocated) 
elapsed time: 0.096983673 seconds (96 bytes allocated) 
elapsed time: 0.096711312 seconds (96 bytes allocated) 
julia> using ArrayViews 
julia> xv = view(x, 1:N) # Note use of a range, not [1:N]! 
julia> @time mean(xv) 
elapsed time: 0.012919509 seconds (96 bytes allocated) 
elapsed time: 0.013010655 seconds (96 bytes allocated) 
elapsed time: 0.01288134 seconds (96 bytes allocated) 
julia> xs = sub(x,1:N) # Works on 0.3 and 0.4 
julia> @time mean(xs) 
elapsed time: 0.014191482 seconds (96 bytes allocated) 
elapsed time: 0.014023089 seconds (96 bytes allocated) 
elapsed time: 0.01257188 seconds (96 bytes allocated) 
  • इसलिए जब हम स्मृति आवंटन से बच सकते हैं, हम वास्तव में धीमे (!) अभी भी हैं।
  • समस्या एक श्रृंखला के विपरीत, एक सरणी द्वारा अनुक्रमणित है। आप 0.3 पर 0.312 के लिए sub का उपयोग नहीं कर सकते, लेकिन आप 0.4 पर कर सकते हैं।
  • यदि हम किसी श्रेणी से अनुक्रमित कर सकते हैं, तो हम 0.3 पर ArrayViews.jl और 0.4 पर इसका अंतर्निहित उपयोग कर सकते हैं। यह मामला मूल mean जितना अच्छा है।

मैंने देखा है कि इस्तेमाल किया (पूरी श्रृंखला के बजाय) सूचकांकों की एक छोटी संख्या के साथ, अंतर बहुत छोटा है, और स्मृति आवंटन कम है, इसलिए sub लायक हो सकता है:

N = 100000000 
x = randn(N) 
inds = [1:div(N,10)] 

@time mean(x) 
@time mean(x) 
@time mean(x) 
@time mean(x[inds]) 
@time mean(x[inds]) 
@time mean(x[inds]) 
xi = sub(x,inds) 
@time mean(xi) 
@time mean(xi) 
@time mean(xi) 

elapsed time: 0.092831612 seconds (985 kB allocated) 
elapsed time: 0.067694917 seconds (96 bytes allocated) 
elapsed time: 0.066209038 seconds (96 bytes allocated) 
elapsed time: 0.066816927 seconds (76 MB allocated, 20.62% gc time in 1 pauses with 1 full sweep) 
elapsed time: 0.057211528 seconds (76 MB allocated, 19.57% gc time in 1 pauses with 0 full sweep) 
elapsed time: 0.046782848 seconds (76 MB allocated, 1.81% gc time in 1 pauses with 0 full sweep) 
elapsed time: 0.186084807 seconds (4 MB allocated) 
elapsed time: 0.057476269 seconds (96 bytes allocated) 
elapsed time: 0.05733602 seconds (96 bytes allocated) 
+0

बहुत जानकारीपूर्ण (हमेशा के रूप में)। बहुत धन्यवाद। शायद यह जोड़ना उचित है कि 'sub' v0.3.5 (' sub (x, inds) 'में वेक्टर इंडेक्स के लिए काम नहीं करता है, मेरी मशीन पर एक त्रुटि फेंकता है, लेकिन काम करता है अगर 'sub' के दूसरे तर्क है एक श्रेणी वस्तु) –

+3

'1: एन' और' [1: एन] 'के बीच भेद नोट करें, मेरा उत्तर देखें। – tholy

+1

IainDunning, स्पष्ट करने के लिए: जहां आप कहते हैं "अब यह अच्छा नहीं है, लेकिन 0.4 जारी होने से पहले तय किया जाएगा," यह सच नहीं है। यह कंप्यूटर हार्डवेयर परमिट के रूप में पहले से ही अनुकूलित है, और जब 'उप' श्रेणी के साथ अनुक्रमण करना उतना ही कलाकार के रूप में 'व्यू' के रूप में होता है। एक श्रेणी के साथ इंडेक्सिंग के रूप में कलाकार के रूप में एक वेक्टर के साथ अनुक्रमण बनाने का कोई तरीका नहीं है। – tholy

14

बस xs = sub(x, 1:N) का उपयोग करें। ध्यान दें कि यह x = sub(x, [1:N]) से अलग है; जूलिया 0.3 पर बाद वाला असफल हो जाएगा, और जुलिएआ 0.4-पूर्व पर पूर्व की तुलना में काफी धीमी होगी। जूलिया 0 पर।4-पूर्व, sub(x, 1:N) है बस के रूप में तेजी से view के रूप में:

julia> N = 10000000; 

julia> x = randn(N); 

julia> xs = sub(x, 1:N); 

julia> using ArrayViews 

julia> xv = view(x, 1:N); 

julia> mean(x) 
-0.0002491126429772525 

julia> mean(xs) 
-0.0002491126429772525 

julia> mean(xv) 
-0.0002491126429772525 

julia> @time mean(x); 
elapsed time: 0.015345806 seconds (27 kB allocated) 

julia> @time mean(xs); 
elapsed time: 0.013815785 seconds (96 bytes allocated) 

julia> @time mean(xv); 
elapsed time: 0.015871052 seconds (96 bytes allocated) 

कई कारण हैं क्यों sub(x, inds) है sub(x, 1:N) की तुलना में धीमी:

  • प्रत्येक पहुँच xs[i]x[inds[i]] से मेल खाती है; हम दो स्मृति के बजाय स्थानों में एक ऐसी
  • को देखने के लिए अगर inds क्रम में नहीं है आप x
  • के तत्वों तक पहुँचने यह SIMD vectorization उपयोग करने की क्षमता को नष्ट कर देता में गरीब कैश व्यवहार मिल जाएगा

इस मामले में, उत्तरार्द्ध शायद सबसे महत्वपूर्ण प्रभाव है। यह जूलिया सीमा नहीं है; वही बात होगी कि आप सी, फोरट्रान या असेंबली में समकक्ष कोड लिखना चाहते थे।

ध्यान दें कि sum(x[inds]) से अभी भी तेज़ है, (जब तक कि बाद वाला पूर्व नहीं हो जाता, जिसे जूलिया 0.4 आधिकारिक तौर पर बाहर किया जाना चाहिए)। लेकिन अगर आपको xs = sub(x, inds) के साथ कई परिचालन करना है, तो कुछ परिस्थितियों में यह प्रतिलिपि बनाने के लिए आपके समय के लायक होगा, भले ही यह स्मृति आवंटित करे, बस आप अनुकूल स्मृति का मूल्य ले सकते हैं जब मूल्यों को संगत स्मृति में संग्रहीत किया जाता है।

+1

यह बहुत दिलचस्प है। मैंने * संगत * स्मृति में एक नई वस्तु बनाने में अंतर्निहित लाभ नहीं माना था। बहुत धन्यवाद। –

+0

जिज्ञासा से, सरणी सबसेटिंग को इंडेक्स सबसेटिंग में बदलने के लिए, क्या यह जगह, उपनिवेश, और फिर जगह में सॉर्ट करने के लिए कभी भी लायक है? – Matthew