2016-10-20 7 views
15

नीचे दिया गया कोड दो साधारण कार्यों को 10 बिलियन बार कॉल करता है।दो स्ट्रिंग तर्कों को एक सूची तर्क से अधिक कुशल क्यों गुजर रहा है

public class PerfTest { 
    private static long l = 0; 

    public static void main(String[] args) { 
     List<String> list = Arrays.asList("a", "b"); 
     long time1 = System.currentTimeMillis(); 
     for (long i = 0; i < 1E10; i++) { 
      func1("a", "b"); 
     } 
     long time2 = System.currentTimeMillis(); 
     for (long i = 0; i < 1E10; i++) { 
      func2(list); 
     } 
     System.out.println((time2 - time1) + "/" + (System.currentTimeMillis() - time2)); 
    } 

    private static void func1(String s1, String s2) { l++; } 
    private static void func2(List<String> sl) { l++; } 
} 

मेरी धारणा यह थी कि इन दो कॉलों का प्रदर्शन समान के करीब होगा। अगर कुछ भी मैंने अनुमान लगाया होगा कि दो तर्क पारित करने से एक गुजरने से थोड़ा धीमा होगा। सभी तर्कों को देखते हुए ऑब्जेक्ट संदर्भ हैं, मैं इस तथ्य की अपेक्षा नहीं कर रहा था कि कोई भी कोई फर्क नहीं पड़ता।

मैंने कई बार परीक्षण चलाया है और एक सामान्य परिणाम "12781/30536" है। दूसरे शब्दों में, दो तारों का उपयोग करने वाले कॉल में 13 सेकंड लगते हैं और सूची का उपयोग करके कॉल 30 सेकंड लेता है।

प्रदर्शन में इस अंतर के लिए स्पष्टीकरण क्या है? या यह एक अनुचित परीक्षण है? मैंने दो कॉलों को स्विच करने का प्रयास किया है (यदि यह स्टार्टअप प्रभाव के कारण था) लेकिन परिणाम समान हैं।

अद्यतन

यह कई कारणों के लिए एक उचित परीक्षण नहीं है। हालांकि यह जावा कंपाइलर के असली व्यवहार का प्रदर्शन करता है। नोट निम्नलिखित दो अतिरिक्त इस प्रदर्शन करने के लिए:

  • भाव जोड़ना s1.getClass() और sl.getClass() कार्यों के लिए दो फ़ंक्शन कॉल perfom ही
  • रनिंग -XX:-TieredCompilation साथ परीक्षण भी बनाता है दो कार्यों कॉल एक ही
  • प्रदर्शन करता है

इस व्यवहार के लिए स्पष्टीकरण नीचे दिए गए स्वीकृत उत्तर में है। @ Apangin के उत्तर का संक्षिप्त सारांश यह है कि func2 हॉटस्पॉट कंपाइलर द्वारा रेखांकित नहीं है क्योंकि इसकी तर्क (यानी List) का हल नहीं किया गया है। कक्षा के संकल्प को मजबूर करना (उदाहरण के लिए getClass का उपयोग करके) इसे रेखांकित किया जाता है जो इसके प्रदर्शन में काफी सुधार करता है। जैसा कि उत्तर में बताया गया है, अनसुलझे वर्ग वास्तविक कोड में होने की संभावना नहीं है जो इस कोड को एक अवास्तविक बढ़त मामला बनाता है।

+0

क्या आप अपनी अपेक्षाओं को जोड़ सकते हैं और क्यों? – ChiefTwoPencils

+1

@ChiefTwoPencils ने उस पर एक पैरा जोड़ा है। – sprinter

+0

मैंने बंद करने के लिए वोट नहीं दिया, लेकिन जब तक कोई विशिष्ट संकलन अनुकूलन को देखने के लिए रनटाइम को अलग करने के लिए तैयार नहीं होता है, तो अधिकांश प्रदर्शन प्रश्न वास्तव में बहुत उपयोगी नहीं होते हैं (हालांकि वे मनोरंजक/रोचक हो सकते हैं) - और उत्तर कर सकते हैं रिलीज से रिलीज में बदलें। इस मामले में मैं बस मानता हूं कि जेवीएम को सरणी कॉल की तुलना में दो पैरामीटर कॉल को संकलित या याद रखना आसान लगता है, लेकिन गंभीरता से - जो भी सबसे ज्यादा पठनीय है उसे लिखें! यह भी ध्यान रखें, सबसे पठनीय संस्करण प्रायः वह है जिसे JVM सर्वोत्तम अनुकूलित करता है। –

उत्तर

19

बेंचमार्क unfair है, हालांकि, यह एक दिलचस्प प्रभाव प्रकट हुआ है।

जैसा कि सॉटिरियोस डेलिमैनोलिस ने देखा है, प्रदर्शन अंतर इस तथ्य के कारण होता है कि func1 हॉटस्पॉट कंपाइलर द्वारा रेखांकित किया गया है, जबकि func2 नहीं है। कारण func2 प्रकार List का तर्क है, जो कक्षा बेंचमार्क के निष्पादन के दौरान हल नहीं हुई है।

नहीं वास्तव में इस्तेमाल किया

ध्यान दें कि List वर्ग है: कोई सूची तरीकों कहा जाता है, प्रकार सूची का कोई क्षेत्रों की घोषणा की, कोई वर्ग डाले और कोई अन्य कार्रवाइयां की गई है कि आम तौर वर्ग resolution कारण। यदि आप कोड में कहीं भी List कक्षा का उपयोग जोड़ते हैं, तो func2 को रेखांकित किया जाएगा।

अन्य परिसंचरण जो संकलन रणनीति को प्रभावित करता है वह विधि की सादगी है। यह इतना आसान है कि जेवीएम ने इसे टायर 1 (सी 1 में आगे संकल्प के साथ) में संकलित करने का निर्णय लिया है। यदि इसे सी 2 के साथ संकलित किया गया था, List कक्षा का समाधान किया जाएगा। -XX:-TieredCompilation के साथ चलने का प्रयास करें, और आप देखेंगे कि func2 सफलतापूर्वक रेखांकित है, और func1 के रूप में तेज़ प्रदर्शन करता है।

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

+0

मैंने यह सुनिश्चित करने के लिए 'सूची' का समाधान किया है और 'TieredCompliation' विकल्प के साथ भी प्रयास करने के लिए दोनों कार्यों में कोड जोड़ने का प्रयास किया है। जैसा कि आपने भविष्यवाणी की है कि 'func2' दोनों' func1' के रूप में तेज़ी से प्रदर्शन करें। स्पष्ट स्पष्टीकरण के लिए धन्यवाद। भविष्य में पाठकों को यह जानकारी स्पष्ट करने के लिए मैं प्रश्न अपडेट करूंगा। – sprinter

+0

सवाल पूछने के बाद से मैंने माइक्रोबेंचमार्क पर काफी कुछ शोध किया है और http://stackoverflow.com/questions/504103/how-do-i-write-a-correct पर निम्नलिखित सुझावों के साथ जेएमएच की कोशिश की है। माइक्रो-बेंचमार्क में जावा। कई प्रयासों के बाद मेरा निष्कर्ष यह है कि जब तक आप परिशुद्धता नहीं चाहते हैं और परीक्षण पर्याप्त लंबी अवधि में है तो स्टॉपवॉच आधारित परीक्षण बिल्कुल एक ही निष्कर्ष तक पहुंचते हैं। आपकी राय में निष्पक्ष परीक्षणों के बारे में सभी चीजें वास्तव में उचित हैं या क्या यह लटकन है? – sprinter

+0

@sprinter मेरे पास ** कई ** उदाहरण हैं जब आदिम स्टॉपवॉच-आधारित बेंचमार्क ने भ्रामक परिणाम प्रस्तुत किए। यहां SO से कुछ उदाहरण दिए गए हैं: [1] (http://stackoverflow.com/a/24889503/3448419), [2] (http: // stackoverflow।कॉम/ए/30745078/3448419), [3] (http://stackoverflow.com/a/38578777/3448419), [4] (http://stackoverflow.com/a/33858960/3448419), [5] (http://stackoverflow.com/a/24344114/3448419), [6] (http://stackoverflow.com/a/33193452/3448419), [7] (http: // stackoverflow। कॉम/ए/25242358/3448419), [8] (http://stackoverflow.com/a/39907607/3448419), [9] (http://stackoverflow.com/a/35671374/3448419) – apangin

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