जावा की तुलना में स्काला का प्रदर्शन


41

सबसे पहले मैं यह स्पष्ट करना चाहूंगा कि यह भाषा-एक्स-बनाम-भाषा-वाई प्रश्न नहीं है जो यह निर्धारित करने के लिए बेहतर है।

मैं लंबे समय से जावा का उपयोग कर रहा हूं और मैं इसका उपयोग करने का इरादा रखता हूं। इसके समानांतर, मैं वर्तमान में स्काला को बहुत रुचि के साथ सीख रहा हूं: मामूली चीजों के अलावा जो कुछ मेरी आदत के लिए उपयोग हो रहा है वह यह है कि मैं वास्तव में इस भाषा में बहुत अच्छा काम कर सकता हूं।

मेरा सवाल है: स्कैला में लिखे गए सॉफ्टवेयर निष्पादन गति और मेमोरी खपत के मामले में जावा में लिखे गए सॉफ़्टवेयर की तुलना कैसे करते हैं? बेशक, यह सामान्य रूप से जवाब देने के लिए एक कठिन सवाल है, लेकिन मैं उम्मीद करूंगा कि उच्च स्तर के निर्माण जैसे कि पैटर्न मिलान, उच्च-क्रम फ़ंक्शन आदि, कुछ ओवरहेड का परिचय देंगे।

हालांकि, स्काला में मेरा वर्तमान अनुभव कोड की 50 लाइनों के तहत छोटे उदाहरणों तक सीमित है और मैंने अब तक कोई भी बेंचमार्क नहीं चलाया है। इसलिए, मेरे पास कोई वास्तविक डेटा नहीं है।

यदि यह पता चला है कि स्काला में कुछ ओवरहेड wrt जावा है, तो क्या इसका मतलब मिश्रित स्काला / जावा प्रोजेक्ट है, जहां एक स्केल में अधिक जटिल भागों और जावा में प्रदर्शन-महत्वपूर्ण भागों को कोड करता है? क्या यह एक आम बात है?

EDIT 1

मैंने एक छोटा बेंचमार्क चलाया है: पूर्णांकों की एक सूची बनाएं, प्रत्येक पूर्णांक को दो से गुणा करें और इसे एक नई सूची में डालें, परिणामी सूची को प्रिंट करें। मैंने एक जावा कार्यान्वयन (जावा 6) और एक स्काला कार्यान्वयन (स्काला 2.9) लिखा। मैंने Ubuntu 10.04 के तहत ग्रहण इंडिगो पर दोनों चलाए हैं।

परिणाम तुलनात्मक हैं: जावा के लिए 480 एमएस और स्काला के लिए 493 एमएस (100 पुनरावृत्तियों पर औसत)। यहाँ मेरे द्वारा उपयोग किए गए स्निपेट्स हैं।

// Java
public static void main(String[] args)
{
    long total = 0;
    final int maxCount = 100;
    for (int count = 0; count < maxCount; count++)
    {
        final long t1 = System.currentTimeMillis();

        final int max = 20000;
        final List<Integer> list = new ArrayList<Integer>();
        for (int index = 1; index <= max; index++)
        {
            list.add(index);
        }

        final List<Integer> doub = new ArrayList<Integer>();
        for (Integer value : list)
        {
            doub.add(value * 2);
        }

        for (Integer value : doub)
        {
            System.out.println(value);
        }

        final long t2 = System.currentTimeMillis();

        System.out.println("Elapsed milliseconds: " + (t2 - t1));
        total += t2 - t1;
    }

    System.out.println("Average milliseconds: " + (total / maxCount));
}

// Scala
def main(args: Array[String])
{
    var total: Long = 0
    val maxCount    = 100
    for (i <- 1 to maxCount)
    {
        val t1   = System.currentTimeMillis()
        val list = (1 to 20000) toList
        val doub = list map { n: Int => 2 * n }

        doub foreach ( println )

        val t2 = System.currentTimeMillis()

        println("Elapsed milliseconds: " + (t2 - t1))
        total = total + (t2 - t1)
    }

    println("Average milliseconds: " + (total / maxCount))
}

तो, इस मामले में ऐसा लगता है कि स्काला ओवरहेड (रेंज, मैप, लैम्ब्डा का उपयोग करके) वास्तव में न्यूनतम है, जो वर्ल्ड इंजीनियर द्वारा दी गई जानकारी से दूर नहीं है।

हो सकता है कि अन्य स्कैला निर्माण हैं जिन्हें देखभाल के साथ उपयोग किया जाना चाहिए क्योंकि वे विशेष रूप से निष्पादित करने के लिए भारी हैं ?

EDIT 2

आप में से कुछ ने बताया कि प्रिंटल इनर लूप्स को निष्पादन के अधिकांश समय तक ले जाता है। मैंने उन्हें हटा दिया है और सूचियों का आकार 20000 के बजाय 100000 पर सेट कर दिया है। परिणामी औसत जावा के लिए 88 एमएस और स्काला के लिए 49 एमएस था।


5
मैं कल्पना करता हूं कि चूंकि स्काला JVM बाइट कोड के लिए संकलित करता है, इसलिए प्रदर्शन सैद्धांतिक रूप से जावा के बराबर हो सकता है जो कि उसी JVM के तहत चल रहा है जो अन्य सभी चीजों के बराबर है। मुझे लगता है कि अंतर यह है कि स्काला संकलक बाइट कोड कैसे बनाता है और यदि यह इतनी कुशलता से करता है।
maple_shaft

2
@maple_shaft: या शायद स्काला संकलन समय में ओवरहेड है?
19ust में 19

1
@ जियोर्जियो स्काला वस्तुओं और जावा वस्तुओं के बीच कोई रनटाइम भेद नहीं है, वे सभी जेवीएम ऑब्जेक्ट हैं जो बाइट कोड के अनुसार परिभाषित और व्यवहार करते हैं। एक भाषा के रूप में उदाहरण के लिए स्कैला में क्लोजर की अवधारणा है, लेकिन जब इन्हें संकलित किया जाता है तो इन्हें कोड के साथ कई वर्गों के लिए संकलित किया जाता है। सैद्धांतिक रूप से, मैं शारीरिक रूप से जावा कोड लिख सकता था जो सटीक उसी बाइट कोड को संकलित कर सकता था और रनटाइम व्यवहार बिल्कुल वैसा ही होगा।
maple_shaft

2
@maple_shaft: यह बिल्कुल वही है जो मैं लक्ष्य कर रहा हूं: मैं उपरोक्त स्कैला कोड को संबंधित जावा कोड की तुलना में बहुत अधिक संक्षिप्त और पठनीय मानता हूं। मैं सोच रहा था कि क्या जावा में किसी स्काला प्रोजेक्ट के कुछ हिस्सों को प्रदर्शन कारणों से लिखने का कोई मतलब होगा, और वे हिस्से क्या होंगे।
जियोर्जियो

2
रनटाइम बड़े पैमाने पर प्रिंटल कॉल द्वारा कब्जा कर लिया जाएगा। आपको अधिक कम्प्यूट-गहन परीक्षा की आवश्यकता है।
केविनलाइन

जवाबों:


39

वहाँ एक बात है कि आप जावा में संक्षिप्त और कुशलता से कर सकते हैं कि आप स्काला में नहीं कर सकते हैं: गणना। सब कुछ के लिए, यहां तक ​​कि स्केला के पुस्तकालय में धीमी गति से होने वाले निर्माणों के लिए, आप स्काला में काम करने वाले कुशल संस्करण प्राप्त कर सकते हैं।

तो, अधिकांश भाग के लिए, आपको अपने कोड में जावा जोड़ने की आवश्यकता नहीं है। यहां तक ​​कि कोड के लिए जो जावा में एन्यूमरेशन का उपयोग करता है, अक्सर स्कैला में एक समाधान होता है जो पर्याप्त या अच्छा होता है - मैं उन एन्यूमरेशन पर अपवाद रखता हूं जिनमें अतिरिक्त तरीके हैं और जिनके अंतर निरंतर मान का उपयोग किया जाता है।

क्या बाहर देखने के लिए के रूप में, यहाँ कुछ चीजें हैं।

  • यदि आप मेरी लाइब्रेरी पैटर्न को समृद्ध करते हैं, तो हमेशा क्लास में कन्वर्ट करें। उदाहरण के लिए:

    // WRONG -- the implementation uses reflection when calling "isWord"
    implicit def toIsWord(s: String) = new { def isWord = s matches "[A-Za-z]+" }
    
    // RIGHT
    class IsWord(s: String) { def isWord = s matches "[A-Za-z]+" }
    implicit def toIsWord(s: String): IsWord = new IsWord(s)
  • संग्रह विधियों से सावधान रहें - क्योंकि वे अधिकांश भाग के लिए बहुरूपी हैं, जेवीएम उन्हें अनुकूलित नहीं करता है। आपको उनसे बचने की जरूरत नहीं है, लेकिन महत्वपूर्ण खंडों पर इस पर ध्यान दें। विदित हो कि forस्काला को विधि कॉल और अनाम कक्षाओं के माध्यम से कार्यान्वित किया जाता है।

  • यदि जावा क्लास का उपयोग कर रहे हैं, जैसे कि String, Arrayया ऐसी AnyValकक्षाएं जो जावा प्राइमेटी के अनुरूप हैं, तो विकल्प उपलब्ध होने पर जावा द्वारा दिए गए तरीकों को प्राथमिकता दें। उदाहरण के लिए, के बजाय lengthपर Stringऔर का उपयोग करें ।Arraysize

  • अंतर्निहित रूपांतरणों के लापरवाह उपयोग से बचें, क्योंकि आप डिज़ाइन के बजाय गलती से रूपांतरणों का उपयोग करके खुद को पा सकते हैं।

  • लक्षणों के बजाय कक्षाओं का विस्तार करें। उदाहरण के लिए, यदि आप विस्तार कर रहे हैं, तो इसके बजाय Function1विस्तार करें AbstractFunction1

  • -optimiseस्काला का अधिकांश भाग प्राप्त करने के लिए उपयोग और विशेषज्ञता।

  • समझें कि क्या हो रहा है: javapआपका दोस्त है, और इसलिए स्काला के झंडे का एक गुच्छा है जो दिखाता है कि क्या चल रहा है।

  • स्काला मुहावरों को शुद्धता में सुधार और कोड को अधिक संक्षिप्त और बनाए रखने के लिए डिज़ाइन किया गया है। वे गति के लिए डिज़ाइन नहीं किए गए हैं, इसलिए यदि आपको एक महत्वपूर्ण पथ के nullबजाय उपयोग करने की आवश्यकता है Option, तो ऐसा करें! वहाँ एक कारण है कि स्काला बहु-प्रतिमान है।

  • याद रखें कि प्रदर्शन का सही माप कोड चल रहा है। उदाहरण के लिए इस प्रश्न को देखें कि क्या हो सकता है यदि आप उस नियम को अनदेखा करते हैं।


1
+1: बहुत सी उपयोगी जानकारी, यहाँ तक कि उन विषयों पर भी जिन्हें मुझे अभी भी सीखना है, लेकिन मुझे देखने से पहले कुछ संकेत पढ़ना उपयोगी है।
जियोर्जियो

पहले दृष्टिकोण प्रतिबिंब का उपयोग क्यों करता है? यह वैसे भी अनाम वर्ग उत्पन्न करता है, इसलिए प्रतिबिंब के बजाय इसका उपयोग क्यों न करें?
ओलेकेंड्रा.बेजहान

@ Oleksandr.Bezhan बेनामी वर्ग एक जावा अवधारणा है, एक स्काला एक नहीं। यह एक प्रकार का शोधन करता है। एक अनाम वर्ग विधि जो अपने आधार वर्ग को ओवरराइड नहीं करती है उसे बाहर से नहीं पहुँचा जा सकता है। स्काला के प्रकार शोधन के बारे में भी यही सच नहीं है, इसलिए उस तरीके को प्राप्त करने का एकमात्र तरीका प्रतिबिंब के माध्यम से है।
डैनियल सी। सोबराल

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

21

एक सिंगल कोर, 32 बिट सिस्टम के लिए बेंचमार्क गेम के अनुसार , स्काला जावा के समान तेजी से 80% है। प्रदर्शन क्वाड कोर x64 कंप्यूटर के लिए लगभग समान है। यहां तक ​​कि स्मृति उपयोग और कोड घनत्व ज्यादातर मामलों में समान हैं। मैं इन (बल्कि अवैज्ञानिक) विश्लेषणों के आधार पर कहूंगा कि आप यह दावा करने में सही हैं कि स्काला जावा में कुछ ओवरहेड जोड़ देती है। यह ओवरहेड के टन को जोड़ने के लिए प्रकट नहीं होता है, इसलिए मुझे अधिक स्थान / समय लेने वाले उच्च आदेश आइटम के निदान पर संदेह होगा जो सबसे सही है।


2
इस उत्तर के लिए कृपया सीधी तुलना का उपयोग करें जैसा कि मदद पृष्ठ सुझाता है ( shootout.alioth.debian.org/help.php#comparetwo )
igouy

18
  • यदि आप सिर्फ Scala में Java / C-like कोड लिखते हैं तो Scala का प्रदर्शन बहुत ही सभ्य है। संकलक के लिए JVM पुरातन का उपयोग करेगा Int, Charआदि जब यह कर सकते हैं। जबकि लूप्स स्काला में उतने ही कुशल हैं।
  • ध्यान रखें कि लंबोदर अभिव्यक्तियों को Functionकक्षाओं के अनाम उपवर्गों के उदाहरणों के लिए संकलित किया जाता है । यदि आप एक लैम्ब्डा पास करते हैं map, तो अनाम वर्ग को तत्काल करने की आवश्यकता है (और कुछ स्थानीय लोगों को पारित करने की आवश्यकता हो सकती है), और फिर प्रत्येक पुनरावृति में कॉल से अतिरिक्त फ़ंक्शन कॉल ओवरहेड (कुछ पैरामीटर पासिंग के साथ) है apply
  • जैसे कई कक्षाएं scala.util.Randomसिर्फ समान JRE कक्षाओं के आसपास रैपर हैं। अतिरिक्त फ़ंक्शन कॉल थोड़ा बेकार है।
  • प्रदर्शन-महत्वपूर्ण कोड में निहितार्थों के लिए देखें। java.lang.Math.signum(x)की तुलना में अधिक प्रत्यक्ष है x.signum(), जो करने के लिए RichIntऔर वापस धर्मान्तरित ।
  • जावा पर स्काला का मुख्य प्रदर्शन लाभ विशेषज्ञता है। ध्यान रखें कि लाइब्रेरी कोड में स्पेशलाइजेशन का इस्तेमाल बहुत कम किया जाता है।

5
  • क) मेरे सीमित ज्ञान से, मुझे टिप्पणी करनी होगी, स्थैतिक मुख्य विधि में उस कोड को बहुत अच्छे से अनुकूलित नहीं किया जा सकता है। आपको महत्वपूर्ण कोड को किसी भिन्न स्थान पर ले जाना चाहिए।
  • बी) लंबी टिप्पणियों से मैं प्रदर्शन परीक्षण पर भारी आउटपुट न करने की सलाह दूंगा (इसके अलावा यह वही है जो आप अनुकूलन करना चाहते हैं, लेकिन कभी भी 2 मिलियन मूल्यों को पढ़ना चाहिए?)। आप प्रिंटलाइन को माप रहे हैं, जो बहुत दिलचस्प नहीं है। प्रिंटलाइन को अधिकतम के साथ बदलना:
(1 to 20000).toList.map (_ * 2).max

मेरे सिस्टम पर 800 एमएस से 20 तक का समय कम कर देता है।

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

2
मैंने प्रिंटल को लूप से हटा दिया है और वास्तव में स्कैला कोड जावा कोड से तेज है।
जियोर्जियो

सी और असेंबलर के साथ तुलना निम्नलिखित अर्थों में थी: एक उच्च-स्तरीय भाषा में अधिक शक्तिशाली सार होते हैं, लेकिन आपको प्रदर्शन के लिए निचले स्तर की भाषा का उपयोग करने की आवश्यकता हो सकती है। क्या यह समानांतर स्केल को उच्च-स्तरीय और जावा को निम्न-स्तरीय भाषा के रूप में मानता है? शायद नहीं, क्योंकि स्काला जावा के समान प्रदर्शन प्रदान करती है।
जियोर्जियो

मुझे नहीं लगता कि यह क्लोजर या स्काला के लिए ज्यादा मायने रखता है, लेकिन जब मैं jRuby और Jython के साथ खेलता था तो मैंने शायद जावा में अधिक प्रदर्शन महत्वपूर्ण कोड लिखा होगा। उन दोनों के साथ मैंने एक महत्वपूर्ण असमानता देखी, लेकिन अब से कई साल पहले ... बेहतर हो सकता था।
ऋग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.