मैं जावा में एक सही माइक्रो-बेंचमार्क कैसे लिखूं?


870

आप जावा में एक सही माइक्रो-बेंचमार्क कैसे लिखते हैं (और चलाते हैं)?

मैं कुछ कोड नमूने और टिप्पणियों के बारे में सोचने के लिए विभिन्न चीजों को दिखाता हूं।

उदाहरण: बेंचमार्क समय / पुनरावृत्ति या पुनरावृत्तियों / समय को मापना चाहिए, और क्यों?

संबंधित: स्टॉपवॉच बेंचमार्किंग स्वीकार्य है?


देखें [यह सवाल] [1] कुछ मिनट पहले से कुछ संबंधित जानकारी के लिए। संपादित करें: क्षमा करें, यह उत्तर नहीं माना जाता है। मुझे एक टिप्पणी के रूप में पोस्ट करना चाहिए था। [१]: stackoverflow.com/questions/503877/…
टियागो

यह उस प्रश्न के पोस्टर को इस तरह एक प्रश्न के रूप में संदर्भित करने की योजना के बाद था कि मैंने नोट किया कि यह प्रश्न मौजूद नहीं था। तो यहाँ है, उम्मीद है कि यह समय के साथ कुछ अच्छे सुझावों को इकट्ठा करेगा।
जॉन निल्सन 14

5
जावा 9 माइक्रो-बेंचमार्किंग के लिए कुछ सुविधाएँ प्रदान कर सकता है: openjdk.java.net/jeps/230
Raedwald

1
@Raedwald मुझे लगता है कि JEP का उद्देश्य JDK कोड में कुछ माइक्रो बेंचमार्क जोड़ना है, लेकिन मुझे नहीं लगता कि JDK को JDK में शामिल किया जाएगा ...
assylias

1
@Raedwald भविष्य से नमस्कार। इसने कटौती नहीं की
माइकल

जवाबों:


787

जावा हॉटस्पॉट के रचनाकारों से माइक्रो बेंचमार्क लिखने के बारे में सुझाव :

नियम 0: जेवीएम और माइक्रो-बेंचमार्किंग पर एक प्रतिष्ठित पेपर पढ़ें। एक अच्छा ब्रायन Goetz, 2005 है । माइक्रो-बेंचमार्क से बहुत अधिक उम्मीद न करें; वे जेवीएम प्रदर्शन विशेषताओं की केवल सीमित सीमा को मापते हैं।

नियम 1: हमेशा एक वार्मअप चरण शामिल करें जो आपके परीक्षण कर्नेल को सभी तरह से चलाता है, समय चरण (ओं) से पहले सभी प्रारंभिकताओं और संकलनों को ट्रिगर करने के लिए पर्याप्त है। (वार्मअप चरण में कुछ पुनरावृत्तियाँ ठीक हैं। अंगूठे का नियम कई दसियों हज़ार इनर लूप पुनरावृत्तियों का है।)

नियम 2: हमेशा साथ रखें -XX:+PrintCompilation, -verbose:gcआदि, इसलिए आप यह सत्यापित कर सकते हैं कि आपके समय के चरण के दौरान जेवीएम के कंपाइलर और अन्य हिस्से अप्रत्याशित काम नहीं कर रहे हैं।

नियम 2.1: समय और वार्मअप चरणों की शुरुआत और अंत में संदेश प्रिंट करें, ताकि आप यह सत्यापित कर सकें कि समय चरण के दौरान नियम 2 से कोई आउटपुट नहीं है।

नियम 3: के बीच अंतर के प्रति सचेत रहें -clientऔर -server, और OSR और नियमित रूप से संकलन। -XX:+PrintCompilationझंडा एक पर हस्ताक्षर के साथ OSR संकलन रिपोर्ट उदाहरण के लिए, गैर प्रारंभिक प्रवेश बिंदु को निरूपित करने के: Trouble$1::run @ 2 (41 bytes)। क्लाइंट के लिए सर्वर को प्राथमिकता दें, और यदि आप सर्वश्रेष्ठ प्रदर्शन के बाद भी OSR के लिए नियमित हैं।

नियम 4: आरंभिक प्रभावों से अवगत रहें। अपने लोडिंग चरण के दौरान पहली बार प्रिंट न करें, क्योंकि लोडिंग और कक्षाएं शुरू करता है। वार्मअप चरण (या अंतिम रिपोर्टिंग चरण) के बाहर नई कक्षाएं लोड न करें, जब तक कि आप विशेष रूप से वर्ग लोडिंग का परीक्षण नहीं कर रहे हैं (और उस स्थिति में केवल परीक्षण कक्षाएं लोड करें)। नियम 2 ऐसे प्रभावों के खिलाफ रक्षा की आपकी पहली पंक्ति है।

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

नियम 6: संकलक के दिमाग को पढ़ने के लिए उपयुक्त उपकरणों का उपयोग करें, और इसके द्वारा उत्पादित कोड से आश्चर्यचकित होने की उम्मीद करें। कुछ तेज या धीमा करने के बारे में सिद्धांतों को बनाने से पहले कोड का स्वयं निरीक्षण करें।

नियम 7: अपने माप में शोर को कम करें। एक शांत मशीन पर अपने बेंचमार्क को चलाएं, और कई बार इसे चलाएं, आउटलेर्स को त्याग दें। -Xbatchअनुप्रयोग के साथ संकलक को क्रमबद्ध करने के लिए उपयोग करें , और -XX:CICompilerCount=1संकलक को अपने साथ समानांतर में चलने से रोकने के लिए सेटिंग पर विचार करें । जीसी ओवरहेड को कम करने के लिए अपना सर्वश्रेष्ठ प्रयास करें, सेट Xmx(बड़े पर्याप्त) के बराबर है Xmsऔर UseEpsilonGCअगर यह उपलब्ध है तो उपयोग करें ।

नियम 8: अपने बेंचमार्क के लिए एक पुस्तकालय का उपयोग करें क्योंकि यह संभवतः अधिक कुशल है और पहले से ही इस एकमात्र उद्देश्य के लिए डिबग किया गया था। जैसे जावा के लिए जेएमएच , कैलिपर या बिल और पॉल की उत्कृष्ट यूसीएसडी बेंचमार्क


5
यह भी एक दिलचस्प लेख था: ibm.com/developerworks/java/library/j-jtp12214
जॉन निल्सन

142
इसके अलावा, System.currentTimeMillis () का उपयोग कभी न करें जब तक कि आप + या - 15 एमएस सटीकता के साथ ठीक न हों, जो कि अधिकांश OS + JVM संयोजनों पर विशिष्ट है। इसके बजाय System.nanoTime () का उपयोग करें।
स्कॉट केरी

5

93
यह ध्यान दिया जाना चाहिए कि System.nanoTime()इसकी तुलना में अधिक सटीक होने की गारंटी नहीं है System.currentTimeMillis()। यह केवल कम से कम सटीक होने की गारंटी है। यह आमतौर पर बहुत अधिक सटीक है, हालांकि।
ग्रेविटी

41
मुख्य कारण एक का उपयोग करना चाहिए System.nanoTime()बजाय System.currentTimeMillis()कि पूर्व होगा- बढ़ती होने की गारंटी है। दो currentTimeMillisइनवोकेशन लौटाए गए मानों को घटाकर वास्तव में नकारात्मक परिणाम दे सकते हैं, संभवतः क्योंकि सिस्टम का समय कुछ NTP डेमन द्वारा समायोजित किया गया था।
वाल्डेइन्ज़

239

मुझे पता है कि इस प्रश्न को उत्तर के रूप में चिह्नित किया गया है, लेकिन मैं दो पुस्तकालयों का उल्लेख करना चाहता था जो हमें माइक्रो बेंचमार्क लिखने में मदद करते हैं

गूगल से कैलिपर

ट्यूटोरियल शुरू करना

  1. http://codingjunkie.net/micro-benchmarking-with-caliper/
  2. http://vertexlabs.co.uk/blog/caliper

OpenJDK से जेएमएच

ट्यूटोरियल शुरू करना

  1. जेवीएम पर बेंचमार्किंग के नुकसान से बचना
  2. http://nitschinger.at/Using-JMH-for-Java-Microbenchmarking
  3. http://java-performance.info/jmh/

37
+1 इसे स्वीकृत उत्तर के नियम 8 के रूप में जोड़ा जा सकता है: नियम 8: क्योंकि इतनी सारी चीजें गलत हो सकती हैं, आपको शायद खुद को करने की कोशिश करने के बजाय मौजूदा लाइब्रेरी का उपयोग करना चाहिए!
23

8
@Pangea JMH आजकल, यह भी देखें शायद कैलिपर से बेहतर है: groups.google.com/forum/#!msg/mechanical-sympathy/m4opvy4xq3U/...
assylias

86

जावा बेंचमार्क के लिए महत्वपूर्ण चीजें हैं:

  • कोड कई बार चलाकर पहले JIT वार्म अप समय से पहले यह
  • सुनिश्चित करें कि आप इसे लंबे समय तक चलाने के लिए सेकंड या (बेहतर) दसियों सेकंड में परिणामों को मापने में सक्षम हो
  • जब आप System.gc()पुनरावृत्तियों के बीच कॉल नहीं कर सकते हैं , तो इसे परीक्षणों के बीच चलाने के लिए एक अच्छा विचार है, ताकि प्रत्येक परीक्षण को उम्मीद के साथ काम करने के लिए "स्वच्छ" मेमोरी स्पेस मिल सके। (हाँ, gc()एक गारंटी से अधिक संकेत है, लेकिन यह बहुत संभावना है कि यह वास्तव में मेरे अनुभव में कचरा इकट्ठा करेगा)
  • मैं पुनरावृत्तियों और समय को प्रदर्शित करना पसंद करता हूं, और समय / पुनरावृत्ति का एक स्कोर जिसे इस तरह से स्केल किया जा सकता है कि "सर्वश्रेष्ठ" एल्गोरिथ्म को 1.0 का स्कोर मिलता है और अन्य को एक रिश्तेदार फैशन में स्कोर किया जाता है। इसका अर्थ है कि आप सभी एल्गोरिदम को लंबे समय तक चला सकते हैं , पुनरावृत्तियों और समय दोनों को अलग-अलग कर सकते हैं, लेकिन फिर भी तुलनीय परिणाम प्राप्त कर सकते हैं।

मैं सिर्फ .NET में बेंचमार्किंग फ्रेमवर्क के डिज़ाइन के बारे में ब्लॉगिंग की प्रक्रिया में हूँ। मैं एक मिल गया है जोड़ी के पहले पदों जो आप कुछ विचार देने में सक्षम हो सकते हैं - सब कुछ, उचित होगा ज़ाहिर है, लेकिन इसके बारे में कुछ हो सकता है।


3
माइनर नाइटपिक: आईएमओ "ताकि प्रत्येक परीक्षा" हो "होनी चाहिए ताकि प्रत्येक परीक्षण प्राप्त हो सके" क्योंकि पूर्व यह धारणा देता है कि कॉलिंग gc हमेशा अप्रयुक्त मेमोरी को मुक्त करती है।
संजय टी। शर्मा

@ संजय.शर्मा: ठीक है, आशय यह है कि यह वास्तव में करता है। हालांकि इसकी कड़ाई से गारंटी नहीं है, यह वास्तव में एक बहुत मजबूत संकेत है। साफ करने के लिए संपादित करेंगे।
जॉन स्कीट

1
मैं System.gc () को कॉल करने से सहमत नहीं हूं। यह एक संकेत है, बस इतना ही। नहीं भी "यह उम्मीद है कि कुछ कर देगा"। आपको कभी भी इसे कॉल नहीं करना चाहिए। यह प्रोग्रामिंग है, कला नहीं।
ग्योरग्यबहारम

13
@gyabraham: हाँ, यह एक संकेत है - लेकिन यह एक है जिसे मैंने आमतौर पर देखा है। इसलिए यदि आप उपयोग करना पसंद नहीं करते हैं System.gc(), तो आप पिछले परीक्षणों में बनाई गई वस्तुओं के कारण एक परीक्षण में कचरा संग्रह को कम करने का प्रस्ताव कैसे करते हैं? मैं व्यावहारिक हूं, हठधर्मी नहीं।
जॉन स्कीट

9
@gyabraham: मुझे नहीं पता कि "महान पतन" से आपका क्या मतलब है। क्या आप विस्तृत और फिर से बता सकते हैं - क्या आपके पास बेहतर परिणाम देने का प्रस्ताव है? मैंने स्पष्ट रूप से कहा कि यह कोई गारंटी नहीं है ...
जॉन स्कीट

48

jmh OpenJDK का एक हालिया जोड़ है और इसे Oracle के कुछ प्रदर्शन इंजीनियरों द्वारा लिखा गया है। निश्चित रूप से देखने लायक।

जेएमएच जावा और अन्य भाषाओं में JVM को लक्षित करने वाली नैनो / माइक्रो / मैक्रो बेंचमार्क के निर्माण, चलाने और विश्लेषण के लिए जावा हार्नेस है।

नमूना परीक्षण टिप्पणियों में दफन जानकारी के बहुत दिलचस्प टुकड़े ।

यह सभी देखें:


1
यह ब्लॉग पोस्ट भी देखें: psy-lob-saw.blogspot.com/2013/04/… के लिए MHH के साथ शुरुआत करने पर विवरण।
निट्सन वकार्ट

FYI करें, JEP 230: माइक्रोबेन्चमार्क सुइट एक OpenJDK प्रस्ताव है जो इस जावा माइक्रोबेनमार्क हार्नेस (JMH) परियोजना पर आधारित है । जावा 9 के लिए कटौती नहीं की, लेकिन बाद में जोड़ा जा सकता है।
बासिल बोर्के

23

बेंचमार्क समय / पुनरावृत्ति या पुनरावृत्तियों / समय को मापना चाहिए, और क्यों?

यह इस बात पर निर्भर करता है कि आप क्या परीक्षण करना चाहते हैं।

यदि आप विलंबता में रुचि रखते हैं , तो समय / पुनरावृत्ति का उपयोग करें और यदि आप थ्रूपुट में रुचि रखते हैं , तो पुनरावृत्तियों / समय का उपयोग करें।


16

यदि आप दो एल्गोरिदम की तुलना करने की कोशिश कर रहे हैं, तो ऑर्डर को बारी-बारी से प्रत्येक के लिए कम से कम दो बेंचमार्क करें। अर्थात:

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

मैंने अलग-अलग पासों में एक ही एल्गोरिदम के क्रम में कुछ ध्यान देने योग्य अंतर (5-10% कभी-कभी) पाया है।

इसके अलावा, सुनिश्चित करें कि n बहुत बड़ा है, ताकि प्रत्येक लूप का रनटाइम बहुत कम 10 सेकंड या इतने पर हो। अधिक पुनरावृत्तियों, आपके बेंचमार्क समय में अधिक महत्वपूर्ण आंकड़े और डेटा जितना अधिक विश्वसनीय है।


5
स्वाभाविक रूप से क्रम बदलने से रनटाइम प्रभावित होता है। जेवीएम-अनुकूलन और कैशिंग-प्रभाव यहां काम करने जा रहे हैं। बेहतर है कि JVM- ऑप्टिमाइज़ेशन, वार्म मल्टीपल रन और बेंचमार्क हर टेस्ट को अलग JVM में 'वार्म अप' किया जाए।
Mnementh

15

सुनिश्चित करें कि आप किसी ऐसे परिणाम का उपयोग करते हैं जो बेंचमार्क कोड में गणना की जाती है। अन्यथा आपका कोड दूर अनुकूलित किया जा सकता है।


13

जावा में माइक्रो-बेंचमार्क लिखने के लिए कई संभावित नुकसान हैं।

पहला: आपको सभी प्रकार की घटनाओं की गणना करनी होगी जो समय को कम या अधिक यादृच्छिक रूप से लेती हैं: कचरा संग्रह, कैशिंग प्रभाव (फाइलों के लिए ओएस और मेमोरी के लिए सीपीयू), आईओ आदि।

दूसरा: आप बहुत कम अंतराल के लिए मापा समय की सटीकता पर भरोसा नहीं कर सकते।

तीसरा: JVM निष्पादित करते समय आपके कोड का अनुकूलन करता है। एक ही JVM- उदाहरण में अलग-अलग रन तेज और तेज हो जाएंगे।

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


8

यह भी ध्यान दिया जाना चाहिए कि विभिन्न कार्यान्वयनों की तुलना करते समय माइक्रो बेंचमार्क के परिणामों का विश्लेषण करना भी महत्वपूर्ण हो सकता है। इसलिए एक महत्त्वपूर्ण परीक्षण किया जाना चाहिए।

ऐसा इसलिए है क्योंकि कार्यान्वयन की Aतुलना में बेंचमार्क के अधिकांश रन के दौरान कार्यान्वयन तेज हो सकता है B। लेकिन Aइसका उच्च प्रसार भी हो सकता है, इसलिए Aजब तुलना की जाती है तो मापा प्रदर्शन लाभ किसी भी महत्व का नहीं होगा B

इसलिए माइक्रो बेंचमार्क को सही ढंग से लिखना और चलाना भी महत्वपूर्ण है, लेकिन इसका सही विश्लेषण भी करना है।


8

अन्य उत्कृष्ट सलाह में जोड़ने के लिए, मैं निम्नलिखित बातों पर भी ध्यान रखूंगा:

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

शायद एक अधिक मौलिक रूप से महत्वपूर्ण पहलू जिस पर आपका सीधा नियंत्रण है: सुनिश्चित करें कि आप सही चीज़ को माप रहे हैं! उदाहरण के लिए, यदि आप System.nanoTime()किसी विशेष कोड को बेंचमार्क करने के लिए उपयोग कर रहे हैं, तो कॉल को उन स्थानों पर असाइनमेंट में रखें, जो उन चीजों को मापने से बचते हैं, जिनमें आप रुचि नहीं रखते हैं। उदाहरण के लिए, ऐसा न करें:

long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");

समस्या यह है कि कोड समाप्त होने पर आपको तुरंत अंतिम समय नहीं मिल रहा है। इसके बजाय, निम्नलिखित प्रयास करें:

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");

हां, समयबद्ध क्षेत्र के अंदर असंबंधित काम नहीं करना महत्वपूर्ण है, लेकिन आपका पहला उदाहरण अभी भी ठीक है। केवल एक ही कॉल है println, न कि एक अलग हेडर लाइन या कुछ और, और उस कॉल के लिए स्ट्रिंग आर्ग के निर्माण में पहले चरण के System.nanoTime()रूप में मूल्यांकन किया जाना है । कुछ भी नहीं है एक संकलक पहले के साथ कर सकता है कि वे दूसरे के साथ नहीं कर सकते हैं, और न ही एक भी उन्हें रोकने के समय से पहले अतिरिक्त काम करने के लिए प्रोत्साहित कर रहा है।
पीटर कॉर्ड्स

7

http://opt.sourceforge.net/ जावा माइक्रो बेंचमार्क - विभिन्न प्लेटफार्मों पर कंप्यूटर सिस्टम की तुलनात्मक प्रदर्शन विशेषताओं को निर्धारित करने के लिए आवश्यक नियंत्रण कार्य। अनुकूलन निर्णयों को निर्देशित करने और विभिन्न जावा कार्यान्वयन की तुलना करने के लिए उपयोग किया जा सकता है।


2
सिर्फ JVM + हार्डवेयर को बेंचमार्क लगता है, जावा कोड का एक मनमाना टुकड़ा नहीं।
स्टेफन एल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.