2015-09-04 10 views
6

स्काला और स्पार्क का उपयोग करना, मैं निम्नलिखित निर्माण किया है:स्कैला कंपाइलर कैसे अप्रयुक्त परिवर्तनीय मानों को संभालता है?

val rdd1: RDD[String] = ... 
val rdd2: RDD[(String, Any)] = ... 

val rdd1pairs = rdd1.map(s => (s, s)) 
val result = rdd2.join(rdd1pairs) 
       .map { case (_: String, (e: Any, _)) => e } 

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

मेरा प्रश्न rdd1pairs की चाबियों को संदर्भित करता है: वे केवल पहले मानचित्र चरण में सिंटैक्टिकल कारणों (शामिल होने की अनुमति देने के लिए) बनाए जाते हैं और बाद में किसी भी उपयोग के बिना त्याग दिए जाते हैं। संकलक इसे कैसे संभालता है? क्या यह स्मृति खपत के मामले में मायने रखता है चाहे मैं स्ट्रिंग s (उदाहरण में दिखाया गया है) का उपयोग करता हूं? क्या मुझे इसे थोड़ी मेमोरी बचाने के लिए null या 0 द्वारा प्रतिस्थापित करना चाहिए? क्या संकलक वास्तव में इन वस्तुओं (संदर्भ) को बनाते हैं और स्टोर करते हैं या क्या यह ध्यान देता है कि उनका कभी भी उपयोग नहीं किया जाता है?

उत्तर

3

इस मामले में, स्पार्क ड्राइवर ऐसा करेगा जो संकलक के बजाय परिणाम को प्रभावित करता है, मुझे लगता है। s के अनावश्यक डुप्लिकेशन को रोकने से बचने के लिए स्पार्क अपनी निष्पादन पाइपलाइन को अनुकूलित कर सकता है या नहीं। मुझे यकीन नहीं है लेकिन मुझे लगता है कि स्पार्क स्मृति में rdd1pairs बनाएगा।

(String, String) के मानचित्रण के बजाय आप (String, Unit) इस्तेमाल कर सकते हैं:

rdd1.map(s => (s,())) 

क्या आप कर रहे हैं मूल रूप से rdd1 के आधार पर rdd2 के एक फिल्टर है। यदि rdd1 rdd2 से काफी छोटा है, तो एक और तरीका rdd1 के डेटा को आरडीडी की बजाय प्रसारण चर के रूप में प्रस्तुत करना होगा, और बस rdd2 फ़िल्टर करें। यह किसी भी शफल या चरण को कम करने से बचाता है, इसलिए तेज़ हो सकता है, लेकिन केवल तभी काम करेगा यदि rdd1 का डेटा प्रत्येक नोड पर फिट करने के लिए पर्याप्त छोटा है।

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

कैसे यूनिट का उपयोग कर के बजाय स्ट्रिंग स्थान की बचत होती है, तो निम्न उदाहरण पर विचार को ध्यान में रखते: इस सवाल How to check heap usage of a running JVM from the command line? में वर्णित के रूप

object size extends App { 

    (1 to 1000000).map(i => ("foo"+i,())) 
    val input = readLine("prompt> ") 
} 

और

object size extends App { 

    (1 to 1000000).map(i => ("foo"+i, "foo"+i)) 
    val input = readLine("prompt> ") 
} 

jstat आदेश का उपयोग करना पहला संस्करण उत्तरार्द्ध की तुलना में काफी कम ढेर का उपयोग करता है।

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

Unit प्रभावी ढंग से, कोई सामग्री के साथ एक सिंगलटन वस्तु है तो तार्किक रूप से, यह किसी भी क्रमबद्धता आवश्यकता नहीं होनी चाहिए। तथ्य यह है कि टाइप परिभाषा में Unit आपको बताता है कि आपको एक ऐसी संरचना को deserialize करने में सक्षम होना चाहिए जिसमें यूनिट का प्रकार है।

स्पार्क डिफ़ॉल्ट रूप से जावा सीरियलाइजेशन का उपयोग करता है।निम्नलिखित पर विचार करें:

object Main extends App { 

    import java.io.{ObjectOutputStream, FileOutputStream} 

    case class Foo (a: String, b:String) 
    case class Bar (a: String, b:String, c: Unit) 

    val str = "abcdef" 
    val foo = Foo("abcdef", "xyz") 
    val bar = Bar("abcdef", "xyz",()) 

    val fos = new FileOutputStream("foo.obj") 
    val fo = new ObjectOutputStream(fos) 
    val bos = new FileOutputStream("bar.obj") 
    val bo = new ObjectOutputStream(bos) 
    fo writeObject foo 
    bo writeObject bar 
} 

दो फ़ाइलों समान आकार के होते हैं:

�� sr Main$Foo3�,�z \ L at Ljava/lang/String;L bq ~ xpt abcdeft xyz 

और

�� sr Main$Bar+a!N��b L at Ljava/lang/String;L bq ~ xpt abcdeft xyz 
+0

उचित लगता है, धन्यवाद। हालांकि, मुझे अभी भी यकीन नहीं है कि यूनिट के संदर्भों को संग्रहीत करने से मूल स्ट्रिंग संस्करण की तुलना में स्मृति की एक महत्वपूर्ण मात्रा बचाती है। क्या यह? – Carsten

+0

उस विषय को कवर करने के लिए मेरा उत्तर बढ़ाया है – mattinbits

+1

लेकिन मूल प्रश्न में कोई नया स्ट्रिंग नहीं बनाया गया है। स्ट्रिंग का संदर्भ '()' के संदर्भ के समान आकार है। –

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