स्पार्क: पायथन ने मेरे उपयोग के मामले में स्कैला को काफी बेहतर बना दिया है?


16

पायथन और स्काला का उपयोग करते समय स्पार्क के प्रदर्शन की तुलना करने के लिए मैंने दोनों भाषाओं में समान नौकरी बनाई और रनटाइम की तुलना की। मुझे उम्मीद थी कि दोनों नौकरियों में लगभग समान समय लगेगा, लेकिन पायथन की नौकरी में केवल इतना ही 27minसमय लगा , जबकि स्काला की नौकरी में 37minलगभग 40% लंबा समय लगा!)। मैंने जावा में भी यही काम लागू किया और इसे 37minutesभी किया। यह कैसे संभव है कि पायथन इतना तेज है?

न्यूनतम सत्यापन योग्य उदाहरण:

पायथन नौकरी:

# Configuration
conf = pyspark.SparkConf()
conf.set("spark.hadoop.fs.s3a.aws.credentials.provider", "org.apache.hadoop.fs.s3a.AnonymousAWSCredentialsProvider")
conf.set("spark.executor.instances", "4")
conf.set("spark.executor.cores", "8")
sc = pyspark.SparkContext(conf=conf)

# 960 Files from a public dataset in 2 batches
input_files = "s3a://commoncrawl/crawl-data/CC-MAIN-2019-35/segments/1566027312025.20/warc/CC-MAIN-20190817203056-20190817225056-00[0-5]*"
input_files2 = "s3a://commoncrawl/crawl-data/CC-MAIN-2019-35/segments/1566027312128.3/warc/CC-MAIN-20190817102624-20190817124624-00[0-3]*"

# Count occurances of a certain string
logData = sc.textFile(input_files)
logData2 = sc.textFile(input_files2)
a = logData.filter(lambda value: value.startswith('WARC-Type: response')).count()
b = logData2.filter(lambda value: value.startswith('WARC-Type: response')).count()

print(a, b)

स्केल नौकरी:

// Configuration
config.set("spark.executor.instances", "4")
config.set("spark.executor.cores", "8")
val sc = new SparkContext(config)
sc.setLogLevel("WARN")
sc.hadoopConfiguration.set("fs.s3a.aws.credentials.provider", "org.apache.hadoop.fs.s3a.AnonymousAWSCredentialsProvider")

// 960 Files from a public dataset in 2 batches 
val input_files = "s3a://commoncrawl/crawl-data/CC-MAIN-2019-35/segments/1566027312025.20/warc/CC-MAIN-20190817203056-20190817225056-00[0-5]*"
val input_files2 = "s3a://commoncrawl/crawl-data/CC-MAIN-2019-35/segments/1566027312128.3/warc/CC-MAIN-20190817102624-20190817124624-00[0-3]*"

// Count occurances of a certain string
val logData1 = sc.textFile(input_files)
val logData2 = sc.textFile(input_files2)
val num1 = logData1.filter(line => line.startsWith("WARC-Type: response")).count()
val num2 = logData2.filter(line => line.startsWith("WARC-Type: response")).count()

println(s"Lines with a: $num1, Lines with b: $num2")

बस कोड को देखकर, वे समान प्रतीत होते हैं। मैंने डीएजी को देखा और उन्होंने कोई जानकारी नहीं दी (या कम से कम मुझे उनके आधार पर स्पष्टीकरण के साथ आने के लिए पता नहीं है) का अभाव है।

मैं वास्तव में किसी भी संकेत की सराहना करूंगा।


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
शमूएल एलवाई

1
मैंने विश्लेषण शुरू कर दिया होगा, इससे पहले कि ब्लॉक और बयानों को देखने के लिए कुछ भी पूछने से पहले यह देखने के लिए कि क्या कोई विशेष स्थान है जहां अजगर संस्करण तेज है। तब आप शायद इस सवाल को 'तीखा बयान क्यों दे रहे हैं' तेज कर सकते हैं।
टेरी जान कॉमेडी

जवाबों:


11

आपकी मूल धारणा, कि स्काला या जावा इस विशिष्ट कार्य के लिए तेज होना चाहिए, सिर्फ गलत है। आप इसे न्यूनतम स्थानीय अनुप्रयोगों के साथ आसानी से सत्यापित कर सकते हैं। स्केल एक:

import scala.io.Source
import java.time.{Duration, Instant}

object App {
  def main(args: Array[String]) {
    val Array(filename, string) = args

    val start = Instant.now()

    Source
      .fromFile(filename)
      .getLines
      .filter(line => line.startsWith(string))
      .length

    val stop = Instant.now()
    val duration = Duration.between(start, stop).toMillis
    println(s"${start},${stop},${duration}")
  }
}

अजगर एक

import datetime
import sys

if __name__ == "__main__":
    _, filename, string = sys.argv
    start = datetime.datetime.now()
    with open(filename) as fr:
        # Not idiomatic or the most efficient but that's what
        # PySpark will use
        sum(1 for _ in filter(lambda line: line.startswith(string), fr))

    end = datetime.datetime.now()
    duration = round((end - start).total_seconds() * 1000)
    print(f"{start},{end},{duration}")

परिणाम (300 repetitions प्रत्येक, पायथन 3.7.6, स्काला 2.11.12), पर Posts.xmlसे hermeneutics.stackexchange.com डेटा डंप मिलान और गैर मिलान पैटर्न के मिश्रण के साथ:

उपरोक्त कार्यक्रमों के लिए मिलिस में डार्टार्टियन के बॉक्सप्लेट

  • पायथन 273.50 (258.84, 288.16)
  • स्काला 634.13 (533.81, 734.45)

जैसा कि आप देखते हैं कि पायथन न केवल व्यवस्थित रूप से तेज़ है, बल्कि अधिक सुसंगत (कम प्रसार) है।

संदेश को ले लो - विश्वास न करें FUD - भाषाएं विशिष्ट कार्यों पर या विशिष्ट वातावरण के साथ तेज या धीमी हो सकती हैं (उदाहरण के लिए यहां स्काला को JVM स्टार्टअप और / या GC और / या JIT द्वारा मारा जा सकता है), लेकिन आप दावा करते हैं जैसे "XYZ X4 तेज़ है" या "XYZ ZYX (..) की तुलना में धीमा है, लगभग 10x धीमा" इसका आमतौर पर मतलब है कि किसी ने चीजों को परखने के लिए वास्तव में बुरा कोड लिखा है।

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

टिप्पणियों में उठाई गई कुछ चिंताओं को दूर करने के लिए:

  • ओपी कोड में डेटा को ज्यादातर एक दिशा (JVM -> पायथन) में पास किया जाता है और कोई वास्तविक क्रमांकन की आवश्यकता नहीं होती है (यह विशिष्ट मार्ग बस के रूप में गुजरता है और दूसरी तरफ UTF-8 पर डिकोड होता है)। यह उतना ही सस्ता है जितना कि "सीरियलाइजेशन" की बात आती है।
  • जो वापस पारित किया गया है वह विभाजन द्वारा केवल एक पूर्णांक है, इसलिए उस दिशा में प्रभाव नगण्य है।
  • संचार स्थानीय सॉकेट्स पर किया जाता है (प्रारंभिक कनेक्ट से परे कार्यकर्ता पर सभी संचार और फ़ाइल डिस्क्रिप्टर से लौटाए गए विवरण का उपयोग करके किया जाता है local_connect_and_auth, और सॉकेट से संबंधित फ़ाइल के अलावा और कुछ नहीं )। फिर से, सस्ते के रूप में यह हो जाता है जब यह प्रक्रियाओं के बीच संचार की बात आती है।
  • ऊपर दिखाए गए कच्चे प्रदर्शन में अंतर को ध्यान में रखते हुए (जो आप कार्यक्रम में देखते हैं उससे कहीं अधिक), ऊपर सूचीबद्ध ओवरहेड के लिए बहुत अधिक मार्जिन है।
  • यह मामला उन मामलों से पूरी तरह से अलग है जहां या तो सरल या जटिल वस्तुओं को पाइथन इंटरप्रेटर से एक ऐसे रूप में पारित करना पड़ता है जो अचार-संगत डंप के रूप में दोनों पक्षों के लिए सुलभ है (सबसे उल्लेखनीय उदाहरणों में पुरानी शैली के यूडीएफ शामिल हैं, पुराने के कुछ हिस्से -स्टाइल MLLib)।

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

चूंकि जैस्पर-एम यहां स्टार्टअप लागत के बारे में चिंतित था, इसलिए कोई भी आसानी से यह साबित कर सकता है कि इनपुट आकार में काफी वृद्धि होने पर भी पायथन को स्काला पर महत्वपूर्ण लाभ होगा।

यहां 2003360 लाइनों / 5.6G (एक ही इनपुट, बस कई बार दोहराए गए, 30 पुनरावृत्ति) के परिणाम हैं, जिस तरह से आप एकल स्पार्क कार्य में उम्मीद कर सकते हैं।

यहाँ छवि विवरण दर्ज करें

  • पायथन 22809.57 (21466.26, 24152.87)
  • स्केल 27315.28 (24367.24, 30263.31)

कृपया गैर-अतिव्यापी आत्मविश्वास अंतराल पर ध्यान दें।

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

जैस्पर-एम की एक अन्य टिप्पणी को संबोधित करने के लिए :

सभी प्रसंस्करण के थोक अभी भी स्पार्क मामले में एक जेवीएम के अंदर हो रहा है।

यह इस विशेष मामले में बस गलत है:

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

3
समस्या का उत्कृष्ट दृष्टिकोण। इसे साझा करने के लिए धन्यवाद
अलेक्जेंड्रोस बिराटिस

1
@egordoe Alexandros ने कहा "यहाँ कोई UDF नहीं लगाया गया है" ऐसा नहीं है कि "पायथन को आमंत्रित नहीं किया गया है" - जो सभी अंतर बनाता है। सीरियलाइजेशन ओवरहेड महत्वपूर्ण है जहां सिस्टम के बीच डेटा का आदान-प्रदान किया जाता है (यानी जब आप यूडीएफ और वापस डेटा पास करना चाहते हैं)।
user10938362

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

1
इसके अतिरिक्त क्रमांकन विधियाँ भी समान नहीं हैं। जैसा कि स्पार्क केस दिखाता है कि अच्छा क्रमांकन तरीके उस स्तर पर लागत में कटौती कर सकते हैं जहां यह अब चिंता का विषय नहीं है (देखें तीर के साथ पंडों यूडीएफ) और जब ऐसा होता है, तो अन्य कारक हावी हो सकते हैं (उदाहरण के लिए स्केल विंडो कार्यों और पंडों के साथ उनके समकक्षों के बीच प्रदर्शन की तुलना देखें) यूडीएफ - इस प्रश्न की तुलना में पायथन वहां बहुत अधिक अंतर से जीतता है)।
user10938362

1
और आपकी बात @ जैस्पर-एम है? व्यक्तिगत स्पार्क कार्य आमतौर पर इस से तुलनात्मक कार्यभार के लिए काफी छोटे होते हैं। मुझे गलत तरीके से मत लो, लेकिन अगर आपके पास कोई वास्तविक प्रतिसाद है जो इस या पूरे प्रश्न को अमान्य करता है, तो कृपया इसे पोस्ट करें। मैंने पहले ही नोट किया है कि इस मूल्य के लिए कुछ हद तक माध्यमिक क्रियाएं योगदान देती हैं, लेकिन वे लागत पर हावी नहीं होती हैं। हम सभी इंजीनियर (किसी तरह के) यहाँ हैं - चलो संख्या और कोड, विश्वास नहीं, हम करेंगे?
user10938362

4

स्काला नौकरी में अधिक समय लगता है क्योंकि इसमें गलत धारणा होती है और इसलिए, पायथन और स्काला नौकरियों को असमान संसाधनों के साथ प्रदान किया गया था।

कोड में दो गलतियाँ हैं:

val sc = new SparkContext(config) // LINE #1
sc.setLogLevel("WARN")
sc.hadoopConfiguration.set("fs.s3a.aws.credentials.provider", "org.apache.hadoop.fs.s3a.AnonymousAWSCredentialsProvider")
sc.hadoopConfiguration.set("spark.executor.instances", "4") // LINE #4
sc.hadoopConfiguration.set("spark.executor.cores", "8") // LINE #5
  1. लाइन 1. एक बार लाइन निष्पादित होने के बाद, स्पार्क नौकरी का संसाधन कॉन्फ़िगरेशन पहले से ही स्थापित और तय हो गया है। इस बिंदु से, कुछ भी समायोजित करने का कोई तरीका नहीं है। न तो निष्पादकों की संख्या और न ही प्रति निष्पादक कोर की संख्या।
  2. 4-5 लाइन। sc.hadoopConfigurationकिसी भी स्पार्क कॉन्फ़िगरेशन को सेट करने के लिए एक गलत जगह है। इसे उस configउदाहरण में सेट किया जाना चाहिए जिसे आप पास करते हैं new SparkContext(config)

[जोड़ा] को ध्यान में रखते हुए, मैं स्कैला नौकरी के कोड को बदलने का प्रस्ताव करूंगा

config.set("spark.executor.instances", "4")
config.set("spark.executor.cores", "8")
val sc = new SparkContext(config) // LINE #1
sc.setLogLevel("WARN")
sc.hadoopConfiguration.set("fs.s3a.aws.credentials.provider", "org.apache.hadoop.fs.s3a.AnonymousAWSCredentialsProvider")

और इसे फिर से परीक्षण करें। मैं शर्त लगाता हूं कि स्काला संस्करण अब एक्स गुना तेज होने जा रहा है।


मैंने सत्यापित किया कि दोनों नौकरियां 32 कार्य समानांतर में चलती हैं इसलिए मुझे नहीं लगता कि यह अपराधी है?
maestromusica

संपादन के लिए धन्यवाद, अभी इसका परीक्षण करने की कोशिश करेंगे
maestromusica

hi @maestromusica संसाधन कॉन्फ़िगरेशन में कुछ होना चाहिए क्योंकि, आंतरिक रूप से, पायथन इस विशेष उपयोग के मामले में स्काला को बेहतर नहीं बना सकता है। एक और कारण कुछ असंबंधित यादृच्छिक कारक हो सकते हैं, अर्थात विशेष क्षण और समान पर क्लस्टर का भार। Btw, आप किस मोड का उपयोग करते हैं? स्टैंडअलोन, स्थानीय, यार्न?
एगॉर्डो

हां, मैंने सत्यापित किया है कि यह उत्तर गलत है। रनटाइम एक ही है। मैंने कॉन्फ़िगरेशन को दोनों मामलों में मुद्रित किया है और यह समान है।
maestromusica

1
मुझे लगता है आप सही होंगे। मैंने इस प्रश्न को अन्य सभी संभावनाओं की जांच करने के लिए कहा जैसे कि कोड में गलती या शायद मुझे कुछ गलत लगा। आपके सहयोग के लिए धन्यवाद।
maestromusica
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.