अपाचे स्पार्क: मैप बनाम मैपपार्टिशन?


133

RDD की विधि mapऔर mapPartitionsविधि में क्या अंतर है ? और flatMapव्यवहार करता है mapया पसंद करता है mapPartitions? धन्यवाद।

(संपादित करें) यानी अंतर क्या है (या तो शब्दार्थ या निष्पादन के संदर्भ में)

  def map[A, B](rdd: RDD[A], fn: (A => B))
               (implicit a: Manifest[A], b: Manifest[B]): RDD[B] = {
    rdd.mapPartitions({ iter: Iterator[A] => for (i <- iter) yield fn(i) },
      preservesPartitioning = true)
  }

तथा:

  def map[A, B](rdd: RDD[A], fn: (A => B))
               (implicit a: Manifest[A], b: Manifest[B]): RDD[B] = {
    rdd.map(fn)
  }

3
नीचे दिए गए उत्तर को पढ़ने के बाद, आप किसी ऐसे व्यक्ति द्वारा साझा किए गए [इस अनुभव] को देख सकते हैं, जिसने वास्तव में इसका उपयोग किया है। ( bzhangusc.wordpress.com/2014/06/19/… ) bzhangusc.wordpress.com/2014/06/19 /…
अभिमान

जवाबों:


121

RDD के मैप और मैपपार्टिशन विधि में क्या अंतर है?

विधि मानचित्र स्रोत RDD के प्रत्येक तत्व को फ़ंक्शन को लागू करके RDD के परिणाम के एकल तत्व में परिवर्तित करता है। mapPartitions स्रोत RDD के प्रत्येक विभाजन को परिणाम के कई तत्वों में परिवर्तित करता है (संभवतः कोई नहीं)।

और क्या फ्लैटपाइप नक्शे की तरह या नक्शे की तरह व्यवहार करता है?

न तो, फ्लैटपाइप एक तत्व (एस map) पर काम करता है और परिणाम के कई तत्वों (एस ) का उत्पादन करता है mapPartitions


3
धन्यवाद - तो क्या नक्शा फेरबदल का कारण बनता है (या अन्यथा विभाजन की संख्या को बदल दें)? क्या यह नोड्स के बीच डेटा ले जाता है? मैं नोड्स के बीच बढ़ते डेटा से बचने के लिए mapPartitions का उपयोग कर रहा हूं, लेकिन यह निश्चित नहीं था कि फ्लैप मैप ऐसा करेगा।
निकोलस व्हाइट

यदि आप स्रोत को देखते हैं - github.com/apache/incubator-spark/blob/… और github.com/apache/incubator-spark/blob/… - दोनों mapऔर flatMapमाता-पिता के समान विभाजन हैं।
एलेक्सी रोमानोव

13
एक नोट के रूप में, 2013 सैन फ्रांसिस्को स्पार्क शिखर सम्मेलन (goo.gl/JZXDCR) में एक वक्ता द्वारा प्रदान की गई एक प्रस्तुति इस बात पर प्रकाश डालती है कि उच्च प्रति-रिकॉर्ड ओवरहेड के साथ कार्य एक मानचित्र रूपांतरण के साथ मैपपार्टिशन के साथ बेहतर प्रदर्शन करते हैं। यह एक नए कार्य को स्थापित करने की उच्च लागत के कारण, प्रस्तुति के अनुसार है।
मिकेल उर्किया

1
मैं इसके विपरीत देख रहा हूं - यहां तक ​​कि बहुत छोटे ऑपरेशनों के साथ, इसके तेजी से मैपपार्टिशन और कॉल मैप की तुलना में पुनरावृति। मैं मान रहा हूं कि यह भाषा इंजन को शुरू करने का सिर्फ ओवरहेड है जो मानचित्र कार्य को संसाधित करेगा। (मैं आर में हूं, जिसमें अधिक स्टार्टअप ओवरहेड हो सकता है।) यदि आप कई ऑपरेशन कर रहे हैं, तो मैपपार्टिशन काफी तेज प्रतीत होता है - मैं यह मान रहा हूं क्योंकि यह आरडीडी को केवल एक बार पढ़ता है। भले ही आरडीडी रैम में कैश्ड है, जो कि टाइप रूपांतरण से बहुत अधिक ओवरहेड बचाता है।
बॉब

3
mapमूल रूप से आपका कार्य लेता है f, और इसे पास करता है iter.map(f)। तो मूल रूप से इसकी एक सुविधा विधि जो लपेटती है mapPartitions। मुझे आश्चर्य होगा कि शुद्ध मानचित्र शैली परिवर्तन कार्य (यानी जहां फ़ंक्शन समान है) के लिए प्रदर्शन का कोई तरीका था, यदि आपको प्रसंस्करण के लिए कुछ ऑब्जेक्ट बनाने की आवश्यकता है, तो इन वस्तुओं को साझा किया जा सकता है, तो mapPartitionsलाभप्रद होगा।
नाइटवॉल्फ

129

Imp। सुझाव:

जब भी आपके पास हैवीवेट इनिशियलाइज़ेशन हो जो एक RDDतत्व के बजाय कई तत्वों के लिए एक बार किया जाना चाहिए RDD, और यदि यह इनिशियलाइज़ेशन, जैसे कि किसी थर्ड-पार्टी लाइब्रेरी से ऑब्जेक्ट्स का निर्माण, को क्रमबद्ध नहीं किया जा सकता है (ताकि स्पार्क इसे क्लस्टर में प्रसारित कर सके। कार्यकर्ता नोड्स), के mapPartitions()बजाय का उपयोग करें map()उदाहरण के लिए mapPartitions()एक बार RDDडेटा तत्व के अनुसार श्रमिक कार्य / थ्रेड / विभाजन के अनुसार एक बार किए जाने के लिए आरंभिकता प्रदान करता है : नीचे देखें।

val newRd = myRdd.mapPartitions(partition => {
  val connection = new DbConnection /*creates a db connection per partition*/

  val newPartition = partition.map(record => {
    readMatchingFromDB(record, connection)
  }).toList // consumes the iterator, thus calls readMatchingFromDB 

  connection.close() // close dbconnection here
  newPartition.iterator // create a new iterator
})

Q2। नक्शे की तरह व्यवहार करता है flatMapया पसंद करता है mapPartitions?

हाँ। कृपया देखें उदाहरण 2 का flatmap.. इसकी स्व व्याख्या।

Q1। एक RDD के बीच क्या अंतर है mapऔरmapPartitions

mapmapPartitionsविभाजन के स्तर पर कार्य का अभ्यास करते हुए एक तत्व स्तर पर उपयोग किए जा रहे फ़ंक्शन को काम करता है।

उदाहरण परिदृश्य : यदि हमारे पास किसी विशेषRDDविभाजनमें 100K तत्व हैंतो हम उपयोग किए जा रहे फ़ंक्शन को मैपिंग परिवर्तन 100K बार बंद कर देंगेmap

इसके विपरीत, अगर हम उपयोग करते हैं mapPartitionsतो हम केवल एक बार विशेष फ़ंक्शन को कॉल करेंगे, लेकिन हम सभी 100K रिकॉर्ड में पास होंगे और एक फ़ंक्शन कॉल में सभी प्रतिक्रियाएं वापस प्राप्त करेंगे।

mapकिसी विशेष फ़ंक्शन पर कई बार काम करने के बाद से प्रदर्शन लाभ होगा , खासकर यदि फ़ंक्शन हर बार कुछ महंगा कर रहा है, तो यह करने की आवश्यकता नहीं होगी यदि हम एक बार में सभी तत्वों में पारित हो गए (मामले में mappartitions)।

नक्शा

RDD के प्रत्येक आइटम पर एक परिवर्तन फ़ंक्शन लागू करता है और एक नए RDD के रूप में परिणाम देता है।

सूचीबद्ध वेरिएंट

डिफ मैप [U: ClassTag] (f: T => U): RDD [U]

उदाहरण :

val a = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)
 val b = a.map(_.length)
 val c = a.zip(b)
 c.collect
 res0: Array[(String, Int)] = Array((dog,3), (salmon,6), (salmon,6), (rat,3), (elephant,8)) 

mapPartitions

यह एक विशेष नक्शा है जिसे प्रत्येक विभाजन के लिए केवल एक बार कहा जाता है। संबंधित विभाजनों की संपूर्ण सामग्री इनपुट तर्क (Iterarator [T]) के माध्यम से मूल्यों की एक अनुक्रमिक धारा के रूप में उपलब्ध है। कस्टम फ़ंक्शन को एक और Iterator [U] लौटना चाहिए। संयुक्त परिणाम पुनरावृत्तियों स्वचालित रूप से एक नए RDD में परिवर्तित हो जाते हैं। कृपया ध्यान दें, हमारे द्वारा चुने गए विभाजन के कारण ट्यूपल्स (3,4) और (6,7) निम्नलिखित परिणाम से गायब हैं।

preservesPartitioningइंगित करता है कि क्या इनपुट फ़ंक्शन पार्टीशनर को संरक्षित करता है, जो falseतब तक होना चाहिए जब तक कि यह एक जोड़ी आरडीडी नहीं है और इनपुट फ़ंक्शन कुंजियों को संशोधित नहीं करता है।

सूचीबद्ध वेरिएंट

def mapPartitions [U: ClassTag] (f: Iterator [T] => Iterator [U], संरक्षित करता है: Boolean = false): RDD [U]

उदाहरण 1

val a = sc.parallelize(1 to 9, 3)
 def myfunc[T](iter: Iterator[T]) : Iterator[(T, T)] = {
   var res = List[(T, T)]()
   var pre = iter.next
   while (iter.hasNext)
   {
     val cur = iter.next;
     res .::= (pre, cur)
     pre = cur;
   }
   res.iterator
 }
 a.mapPartitions(myfunc).collect
 res0: Array[(Int, Int)] = Array((2,3), (1,2), (5,6), (4,5), (8,9), (7,8)) 

उदाहरण 2

val x = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9,10), 3)
 def myfunc(iter: Iterator[Int]) : Iterator[Int] = {
   var res = List[Int]()
   while (iter.hasNext) {
     val cur = iter.next;
     res = res ::: List.fill(scala.util.Random.nextInt(10))(cur)
   }
   res.iterator
 }
 x.mapPartitions(myfunc).collect
 // some of the number are not outputted at all. This is because the random number generated for it is zero.
 res8: Array[Int] = Array(1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 7, 7, 7, 9, 9, 10) 

उपर्युक्त कार्यक्रम को फ्लैटपाइप का उपयोग करके भी लिखा जा सकता है।

उदाहरण 2 सपाट का उपयोग कर

val x  = sc.parallelize(1 to 10, 3)
 x.flatMap(List.fill(scala.util.Random.nextInt(10))(_)).collect

 res1: Array[Int] = Array(1, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10) 

निष्कर्ष:

mapPartitionsपरिवर्तन तेजी से है mapक्योंकि यह आपके फ़ंक्शन को एक बार / विभाजन कहता है, एक बार / तत्व नहीं ..

आगे पढ़े: foreach Vs foreachPartitions कब इस्तेमाल करें?


4
मुझे पता है कि आप एक ही परिणाम प्राप्त कर सकते हैं mapया mapPartitionsप्राप्त कर सकते हैं (प्रश्न में दो उदाहरण देखें); यह सवाल इस बारे में है कि आप दूसरे पर एक रास्ता क्यों चुनेंगे। अन्य उत्तर में टिप्पणियाँ वास्तव में उपयोगी हैं! इसके अलावा, आपने उस का उल्लेख नहीं किया mapऔर flatMapपास falseकिया preservesPartitioning, और उस के निहितार्थ क्या हैं।
निकोलस व्हाइट

2
फंक्शन के लिए हर एक बार फंक्शन एग्जीक्यूट किया जाता था। मैपपार्टिशन के साथ एक समय में एक से अधिक डेटा रिकॉर्ड तक पहुंच होना एक अमूल्य चीज है। उत्तर की सराहना करें
अर्धविराम और डक्ट टेप

1
वहाँ एक परिदृश्य है जहाँ mapसे बेहतर है mapPartitions? यदि mapPartitionsबहुत अच्छा है, तो यह डिफ़ॉल्ट मानचित्र कार्यान्वयन क्यों नहीं है?
रुहॉन्ग

1
@oneleggedmule: दोनों अलग-अलग आवश्यकताओं के लिए हैं जिन्हें हमें बुद्धिमानी से उपयोग करना होगा यदि आप डीबी कनेक्शन जैसे संसाधनों का त्वरित उपयोग कर रहे हैं (जैसे ऊपर उदाहरण में दिखाया गया है) जो महंगा है तो विभाजन के एक कनेक्शन के बाद से mappartitions सही दृष्टिकोण है। भी saveAsTextFile आंतरिक रूप से इस्तेमाल किए जाने वाले मानचित्रण देखें
राम गद्याराम

@oneleggedmule मेरे दृष्टिकोण से, मानचित्र () को समझना और सीखना आसान है, और यह कई अलग-अलग भाषाओं का एक सामान्य तरीका भी है। मैपपार्टिशन () की तुलना में इसका उपयोग करना आसान हो सकता है यदि कोई शुरुआत में इस स्पार्क विशिष्ट विधि से परिचित नहीं है। यदि कोई प्रदर्शन अंतर नहीं है, तो मैं मानचित्र () का उपयोग करना पसंद करता हूं।
रेमंड चेन

15

नक्शा :

  1. यह एक समय में एक पंक्ति को संसाधित करता है, मैपआरड्यूस के नक्शे के समान ()।
  2. आप हर पंक्ति के बाद परिवर्तन से लौटते हैं।

MapPartitions

  1. यह एक ही बार में पूर्ण विभाजन की प्रक्रिया करता है।
  2. आप पूरे विभाजन को संसाधित करने के बाद केवल एक बार फ़ंक्शन से लौट सकते हैं।
  3. जब तक आप पूरे विभाजन को संसाधित नहीं करते तब तक सभी मध्यवर्ती परिणामों को स्मृति में रखने की आवश्यकता होती है।
  4. आपको सेटअप () मैप () और क्लीनअप () MapReduce का फ़ंक्शन प्रदान करता है

Map Vs mapPartitions http://bytepadding.com/big-data/spark/spark-map-vs-mappartitions/

Spark Map http://bytepadding.com/big-data/spark/spark-map/

Spark mapPartitions http://bytepadding.com/big-data/spark/spark-mappartitions/


2 के बारे में - यदि आप इट्रेटर-टू-इटरेटर ट्रांसफॉर्मेशन कर रहे हैं, और इटरेटर को किसी प्रकार के संग्रह में नहीं ले जा रहे हैं, तो आपको पूरे विभाजन को मेमोरी में नहीं रखना पड़ेगा, वास्तव में, इस तरह से स्पार्क करने में सक्षम होगा विभाजन के कुछ हिस्सों को डिस्क पर रखें।
इलकोर्ड

4
आपको पूरे विभाजन को स्मृति में नहीं रखना है, लेकिन परिणाम। जब तक आप पूरे विभाजन को संसाधित नहीं कर लेते, आप परिणाम नहीं लौटा सकते
क्रेजीगॉटम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.