2014-06-30 14 views
5

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

मेरे मशीन:

:

public static void main(String[] args) { 
    HashMap<Long, Long> hmap = new HashMap<Long, Long>(); 
    long val = 0; 

    //populating the hashmap 
    for (long idx = 0; idx < 10000000; idx++) { 
     hmap.put(idx, idx); 
    } 


    Stopwatch s = Stopwatch.createStarted(); 
    long x = 0; 
    for (long idx = 0; idx < 10000000; idx++) { 
     x = hmap.get(idx); 
    } 
    s.stop(); 
    System.out.println(s); //5.522 s 
    System.out.println(x); //9999999 

    //5.522 seconds/10000000 = 552.2 nanoseconds 
} 

यहाँ इस बेंचमार्क को फिर से लिखने में मेरी प्रयास JMH उपयोग कर रहा है:

Windows 7 64-bit 
Core i7-2760QM @ 2.40 GHz 
8.00 GB Ram 
jdk1.7.0_45 64-bit 

यहाँ ऊपर वर्णित पाशन विधि कोड के नीचे छीन सरल उदाहरण है

package com.test.benchmarks; 

import org.openjdk.jmh.annotations.*; 
import org.openjdk.jmh.runner.Runner; 
import org.openjdk.jmh.runner.RunnerException; 
import org.openjdk.jmh.runner.options.Options; 
import org.openjdk.jmh.runner.options.OptionsBuilder; 

import java.util.HashMap; 
import java.util.concurrent.TimeUnit; 


@State(Scope.Thread) 
public class MyBenchmark { 


    private HashMap<Long, Long> hmap = new HashMap<Long, Long>(); 
    private long key; 

    @Setup(Level.Iteration) 
    public void setup(){ 

     key = 0; 

     for(long i = 0; i < 10000000; i++) { 
      hmap.put(i, i); 
     } 
    } 


    @Benchmark 
    @BenchmarkMode(Mode.SampleTime) 
    @OutputTimeUnit(TimeUnit.NANOSECONDS) 
    public long testGetExistingKey() throws InterruptedException{ 

     if(key >= 10000000) key=0; 
     return hmap.get(key++); 
    } 


    public static void main(String[] args) throws RunnerException { 

     Options opt = new OptionsBuilder() 
       .include(".*" + MyBenchmark.class.getSimpleName() + ".*") 
       .warmupIterations(5) 
       .measurementIterations(25) 
       .forks(1) 
       .build(); 

     new Runner(opt).run(); 

    } 

} 

यहां परिणाम हैं:

Result: 31.163 ±(99.9%) 11.732 ns/op [Average] 
    Statistics: (min, avg, max) = (0.000, 31.163, 939008.000), stdev = 1831.428 
    Confidence interval (99.9%): [19.431, 42.895] 
    Samples, N = 263849 
     mean =  31.163 ±(99.9%) 11.732 ns/op 
     min =  0.000 ns/op 
    p(0.0000) =  0.000 ns/op 
    p(50.0000) =  0.000 ns/op 
    p(90.0000) =  0.000 ns/op 
    p(95.0000) = 427.000 ns/op 
    p(99.0000) = 428.000 ns/op 
    p(99.9000) = 428.000 ns/op 
    p(99.9900) = 856.000 ns/op 
    p(99.9990) = 9198.716 ns/op 
    p(99.9999) = 939008.000 ns/op 
     max = 939008.000 ns/op 


# Run complete. Total time: 00:02:07 

Benchmark        Mode Samples  Score Score error Units 
c.t.b.MyBenchmark.testGetExistingKey sample 263849  31.163  11.732 ns/op 

जहां तक ​​मेरा बता सकते हैं, JMH में एक ही बेंचमार्क hashmap नैनोसेकंड बनाम पाशन परीक्षण के लिए नैनोसेकंड में हो जाता है है। 31 नैनोसेकंड मेरे लिए थोड़ा तेज़ लगता है। Latency Numbers Every Programmer Should Know पर एक मुख्य मेमोरी संदर्भ लगभग 100 नैनोसेकंड है। एल 2 कैश संदर्भ लगभग 7 नैनोसेकंड है, लेकिन हैश मैप 10 मिलियन लंबी चाबियाँ और मानों के साथ अच्छी तरह से एल 2 से अधिक है। इसके अलावा जेएमएच परिणाम मेरे लिए अजीब लगते हैं। 9 0% कॉल कॉल 0.0 नैनोसेकंड लेते हैं?

मुझे लगता है कि यह उपयोगकर्ता त्रुटि है। किसी भी मदद/पॉइंटर्स की सराहना की जाएगी। धन्यवाद।

अद्यतन

यहाँ AverageTime रन करने से परिणाम हैं। यह मेरी अपेक्षाओं के साथ बहुत अधिक है। धन्यवाद @ ओलेग-एस्टेखिन! नीचे दी गई टिप्पणियों में मैंने उल्लेख किया है कि मैंने पहले AverageTime परीक्षण किया था और SampleTime के समान परिणाम थे। मुझे विश्वास है कि उस दौड़ में मैंने बहुत कम प्रविष्टियों के साथ हैश मैप का इस्तेमाल किया था और तेज लुकअप को समझ में आया था।

Result: 266.306 ±(99.9%) 139.359 ns/op [Average] 
    Statistics: (min, avg, max) = (27.266, 266.306, 1917.271), stdev = 410.904 
    Confidence interval (99.9%): [126.947, 405.665] 


# Run complete. Total time: 00:07:17 

Benchmark        Mode Samples  Score Score error Units 
c.t.b.MyBenchmark.testGetExistingKey  avgt  100  266.306  139.359 ns/op 

उत्तर

7

सबसे पहले, लूपिंग परीक्षण उपायों का औसत समय, जबकि आपका जेएमएच कोड नमूना समय के लिए कॉन्फ़िगर किया गया है। Mode.SampleTime से जावाडोक:

नमूना समय: प्रत्येक ऑपरेशन के लिए समय नमूना करता है।

Map.get() के व्यक्तिगत फांसी बहुत बात करने के लिए तेजी से जब अंतर्निहित समय मापन प्रणाली समय माप के विवरण का स्तर (पढ़ने के बारे में अधिक जानकारी के लिए the JMH author द्वारा Nanotrusting the Nanotime ब्लॉग पोस्ट) की वजह से फांसी से कुछ के लिए 0 रिपोर्ट करें।

नमूना मोड में बेंचमार्क अलग-अलग नमूना समय को सरणी में एकत्र करता है और फिर उस सरणी का उपयोग करके औसत और प्रतिशत की गणना करता है।जब अधिक से अधिक सरणी मान शून्य होते हैं (आपके विशेष सेटअप में 90% से अधिक सरणी मान शून्य होते हैं, जैसा कि p(90.0000) = 0.000 ns/op द्वारा इंगित किया गया है) औसत बहुत कम होना चाहिए, लेकिन जब आप p(50) = 0 (और विशेष रूप से p(90) = 0) देखते हैं आपका आउटपुट केवल एकमात्र निष्कर्ष है जिसे आप भरोसेमंद बना सकते हैं कि ये परिणाम कचरे हैं और आपको उस कोड को मापने के लिए एक और तरीका ढूंढना होगा।

  • आप Mode.AverageTime (या Mode.Throughput) बेंचमार्क मोड का उपयोग करना चाहिए। व्यक्तिगत आमंत्रण में पर्याप्त समय लगता है जब स्थितियों के लिए Mode.SampleTime छोड़ दें।

  • आप एक "आधारभूत" मानदंड जिसके if() और key++ कार्यान्वित आदेश समय key बहीखाता और वास्तविक Map.get() समय के लिए आवश्यक को अलग करने में जोड़ सकते हैं, लेकिन आप परिणामों की व्याख्या करने की आवश्यकता होगी (ब्लॉग पोस्ट ऊपर लिंक का वर्णन करता है "वास्तविक" माप से "बेसलाइन" घटाने के साथ नुकसान।

  • आप व्यक्तिगत आमंत्रण के निष्पादन समय को बढ़ाने के लिए Blackhole.consumeCPU() का उपयोग करने का प्रयास कर सकते हैं ("बेसलाइन" और संबंधित नुकसान के बारे में पिछला बिंदु देखें)।

+0

प्रतिक्रिया के लिए धन्यवाद! मैंने आपके द्वारा वर्णित लेख पढ़ा है। वास्तव में अच्छी तरह से लिखा है। मुझे रूसी सीखने की ज़रूरत नहीं है इसलिए मैं अपने अधिक (Aleksey Shipilёv) काम पढ़ सकता हूं। लेख से, विंडोज मशीनों के बारे में बात करते हुए: "ऐसा लगता है कि हमारे पास केवल 370 एनएस परिशुद्धता है"। क्या यह एक संभावित स्पष्टीकरण हो सकता है कि ऐसा क्यों लगता है कि यह समय केवल मेरी मशीन पर दिखाया गया है जो 400ns से अधिक है? मैंने वास्तव में 'औसत समय' मोड में परीक्षण चलाने शुरू कर दिए और इसी तरह के परिणाम थे। 'नमूना समय' मोड क्या हो रहा था में अधिक अंतर्दृष्टि प्रदान करने के लिए प्रतीत होता है। मैं परीक्षण और जवाब दोबारा शुरू करूंगा। – Jon

+1

मेरे काम कंप्यूटर पर नैनोटाइम सटीक 450 एनएस पर भी अधिक है। और निश्चित रूप से यह आपको प्राप्त होने वाले संभावित समय मूल्यों को सीधे प्रभावित करता है। –

+0

क्या मुझे ग्रैन्युलरिटी को शोर में बदलने के लिए बैचमार्क बैच आकार सुविधा का उपयोग करना चाहिए? क्या वह उस सुविधा के लिए वैध उपयोग केस है? जेएमएच नमूने में इसका उपयोग इस प्रकार किया जाता है: "कभी-कभी आपको ऑपरेशन का मूल्यांकन करने की आवश्यकता होती है जिसमें स्थिर स्थिति नहीं होती है। बेंचमार्क किए गए ऑपरेशन की लागत में आमंत्रण से आमंत्रण में काफी भिन्नता हो सकती है।" – Jon

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