2015-08-31 10 views
19

एक प्रयोग के लिए, मैंने यह छोटा कार्यक्रम बनाया है। यह सिर्फ 10 मिलियन यादृच्छिक तार उत्पन्न करता है और उन्हें एक सरणी सूची में जोड़ता है। ध्यान दें कि सरणीसूची की प्रारंभिक क्षमता नहीं है।ऐरेलिस्ट को प्रारंभिक क्षमता देने पर यह धीमा क्यों है?

// editors note: added the necessary boilerplate to run, 
// and take initial capacity as an optional cmdline arg for easier testing 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Random; 

class ArrayListTest { 
    public static void main(String[] args) 
    { 
     int initsize = -1; 
     if (args.length > 0) { 
      initsize = Integer.parseInt(args[0]); 
     } 

     long startTime = System.currentTimeMillis(); 

     List<String> randNums = initsize>=0 ? new ArrayList<>(initsize) : new ArrayList<>(); 
     // final List<String> randNums = initsize>=0 ? new ArrayList<String>(initsize) : new ArrayList<String>(); 

     Random r = new Random(1); 

     for (int i = 0; i < 10_000_000; i++) { 
      final int randomNum = r.nextInt(); 
      randNums.add(Integer.toString(randomNum)); 
     } 

     System.out.println(System.currentTimeMillis() - startTime); 
    } 
} 

मैं 5 बार यह भाग गया, और एमएस में परिणाम थे:

List<String> randNums = new ArrayList<>(10_000_000); 

मैं फिर से भाग:

8917 
8720 
7814 
8768 
8867 

मैं तो कार्यक्रम ArrayList आरंभिक क्षमता देने के लिए बदल दिया यह 5 गुना, और ये परिणाम थे:

11562 
10454 
10499 
10481 
10415 

यह निश्चित रूप से धीमी गति से धीमा हो गया। मुझे लगता है कि यह विपरीत होगा, क्योंकि एक बड़ा पर्याप्त प्रारंभिक आकार घोषित करके, मैंने अपनी क्षमता बढ़ाने के लिए ArrayList के सभी ओवरहेड को हटा दिया। यह धीमा क्यों था?

अधिक जानकारी: जेआर 1.8.051, 64-बिट (विंडोज़ 10 पर);

+0

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

+0

कौन सा जेवीएम? 64 बिट जावा 'सर्वर' वीएम पर डिफ़ॉल्ट है, जो कि 32 बिट जावा के लिए डिफ़ॉल्ट वीएम की तुलना में जेआईटी-अधिक आक्रामक रूप से संकलित करता है। तो आप कभी-कभी 32 बिट जावा के साथ अजीब perf मतभेद देखते हैं जो एक सभ्य जेआईटी-कंपाइलर ऑप्टिमाइज़ कर देगा। –

+0

जो आप देख रहे हैं वह शायद शोर है। यादृच्छिक पीढ़ी के बजाय हर बार "1" जोड़ने का प्रयास करें। –

उत्तर

19

आपको लगता है कि यह चारों ओर एक और तरीका था, लेकिन इसे कचरा संग्रह के साथ करना है।

मैंने आपके द्वारा किए गए बड़े अंतर को नहीं देखा (3 9 00 बनाम 5100), लेकिन चूंकि यह जीसी से संबंधित है, इसलिए आप शायद कम स्मृति के साथ चल रहे हैं। मैं 64-बिट और -Xmx4g के साथ भाग गया।

जीसी लॉग (-Xloggc:path\to\file.log) चालू किया जा रहा है, मैं आकार के बिना इस मिल:

Java HotSpot(TM) 64-Bit Server VM (25.51-b03) for windows-amd64 JRE (1.8.0_51-b16), built on Jun 8 2015 18:03:07 by "java_re" with MS VC++ 10.0 (VS2010) 
Memory: 4k page, physical 33478384k(25822824k free), swap 33476532k(26121788k free) 
CommandLine flags: -XX:InitialHeapSize=535654144 -XX:MaxHeapSize=4294967296 -XX:+PrintGC -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC 
0.188: [GC (Allocation Failure) 131584K->114906K(502784K), 0.0795857 secs] 
0.358: [GC (Allocation Failure) 246490K->229080K(634368K), 0.0794026 secs] 
0.631: [GC (Allocation Failure) 492248K->452871K(716288K), 0.1389293 secs] 
0.770: [Full GC (Ergonomics)  452871K->451407K(1188864K), 3.3224726 secs] 
4.270: [GC (Allocation Failure) 714575K->714179K(1271808K), 0.2607092 secs] 
4.531: [Full GC (Ergonomics)  714179K->814K(1050624K), 0.0070693 secs] 

और मैं आकार के साथ इस मिल: क्योंकि इसके दूसरे भाग इतना अधिक स्मृति आवंटित

Java HotSpot(TM) 64-Bit Server VM (25.51-b03) for windows-amd64 JRE (1.8.0_51-b16), built on Jun 8 2015 18:03:07 by "java_re" with MS VC++ 10.0 (VS2010) 
Memory: 4k page, physical 33478384k(25818308k free), swap 33476532k(26123684k free) 
CommandLine flags: -XX:InitialHeapSize=535654144 -XX:MaxHeapSize=4294967296 -XX:+PrintGC -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC 
0.171: [GC (Allocation Failure) 131584K->129070K(502784K), 0.0919490 secs] 
0.370: [GC (Allocation Failure) 260654K->261166K(634368K), 0.4433150 secs] 
0.813: [Full GC (Ergonomics)  261166K->260351K(899072K), 1.4135289 secs] 
2.460: [GC (Allocation Failure) 523519K->524487K(899072K), 0.7901689 secs] 
3.250: [Full GC (Ergonomics)  524487K->523517K(1421312K), 2.3177831 secs] 

शुरुआत में, जीसी रन धीमे हो जाते हैं। यह स्पष्ट रूप से सूची का आकार बदलने पर अतिरिक्त सरणी प्रतिलिपि से अधिक है।

+0

प्रारंभिक स्मृति आकार को बढ़ाने के लिए '-Xms2g' का उपयोग करके इसे बड़ी प्रारंभिक क्षमता के साथ या बिना बड़े पैमाने पर गति प्रदान करता है। यह वास्तविक समय ~ = उपयोगकर्ता सीपीयू समय भी बनाता है (कोई जीसी थ्रेड नट्स जा रहा है)। कोई अतिरिक्त JVM तर्क: वास्तविक = 5.5s, उपयोगकर्ता = 12s, sys = 3.3s। '-Xms2g' के साथ: वास्तविक = 1.2s, उपयोगकर्ता = 1.6s, sys = 0.6s। इंटेल एसएनबी i5-2500k (3.8GHz टर्बो), दोहरी चैनल डीडीआर 3 1600 मेगाहर्ट्ज, लिनक्स 3.1 9, 'ओपनजेडीके 64-बिट सर्वर वीएम (25.45-बी 022, मिश्रित मोड का निर्माण) '। –

+1

@ पीटरकॉर्ड्स हाँ, '-Xms' के साथ आप स्मृति को प्रीलोकेट कर रहे हैं, 'जीसी (आवंटन विफलता' रन को खत्म कर सकते हैं, संभवतः सभी जीसी रनों को खत्म कर सकते हैं। यह छोटे परीक्षणों की झुकाव दिखाता है, जहां वीएम ने "गर्म" नहीं किया है। --- यह 'apangin' की टिप्पणी (सवाल करने के लिए) के बारे में था। – Andreas

+0

जीसी स्मृति की मात्रा के बारे में क्यों परवाह करता है? क्या यह केवल वस्तुओं के बारे में परवाह नहीं करना चाहिए? इसे दोनों मामलों में वस्तुओं की एक ही संख्या की जांच करनी है, इसलिए इसे धीमा नहीं होना चाहिए। –

2

सूची 10 लाख तत्वों के साथ रैंडमम्स की सूची लगभग 700 एमबी मेमोरी स्पेस की आवश्यकता है।

-XX:+PrintGC 
-XX:+PrintGCTimeStamps 
-XX:+UseParNewGC 
-XX:+UseConcMarkSweepGC 
-Xmx1000m 
-Xms1000m 
-Xmn999m 
-XX:SurvivorRatio=65535 

काफी बड़ी सभी तत्वों को बचाने के लिए और कोई जीसी बनाने के लिए युवा पीढ़ी बनाने: आदेश जीसी के प्रभाव से बचने के लिए (यकीन है कि के लिए, यह इस परीक्षण में बहुत मायने रखता है), मैं इस तरह हॉटस्पॉट वीएम तर्क सेट तत्व आवंटन के दौरान। मैं युवा पीढ़ी के भीतर तत्वों की प्रतिलिपि से बचने के लिए यंग जनरेशन के ईडन क्षेत्र को बड़ा बनाता हूं।
परिणाम आश्चर्यजनक है!। कुल निष्पादन समय 8 से 0.6 के बीच घटता है!

यहां, मैंने प्रश्नकर्ता के लिए कुछ अतिरिक्त काम किया है, यह जांच कर रहा है कि क्या ऐरेलिस्ट का पूर्व आवंटन समय बचा सकता है और इससे कितना मदद मिलती है।

 long startTime; 
     List<String> randNums; 
     Random r = new Random(1); 

     System.out.println("-----------------------------ArrayList With Enough Capacity Allocated:----------"); 
     for(int loop=0;loop<5;loop++) { 
      startTime = System.currentTimeMillis(); 
      randNums = new ArrayList<String>(SIZE); 
      for (int i = 0; i <SIZE ; i++) { 
       int randomNum = r.nextInt(); 
       randNums.add(Integer.toString(randomNum)); 
      } 
      System.out.println(System.currentTimeMillis() - startTime); //print time of this loop 
      randNums.clear(); 
      System.gc(); 
     } 

     System.out.println("\n-----------------------------ArrayList Auto-Capacity:----------"); 
     for(int loop=0;loop<5;loop++) { 
      startTime = System.currentTimeMillis(); 
      randNums = new ArrayList<String>(); 
      for (int i = 0; i <SIZE ; i++) { 
       int randomNum = r.nextInt(); 
       randNums.add(Integer.toString(randomNum)); 
      } 
      System.out.println(System.currentTimeMillis() - startTime); //print time of this loop 
      randNums.clear(); 
      System.gc(); 
     } 

उत्पादन होता है:
यहाँ मेरी कोड है

-----------------------------ArrayList With Enough Capacity Allocated:---------- 
625 
0.712: [Full GC (System.gc()) 714143K->39628K(1023936K), 0.1450449 secs] 
0.863: [GC (CMS Initial Mark) 98268K(1023936K), 0.0549729 secs] 
545 
1.413: [Full GC (System.gc()) 705185K->564K(1023936K), 0.1239084 secs] 
483 
2.031: [Full GC (System.gc()) 679570K->564K(1023936K), 0.1256323 secs] 
2.160: [GC (CMS Initial Mark) 59357K(1023936K), 0.0274108 secs] 
523 
2.688: [Full GC (System.gc()) 670987K->564K(1023936K), 0.1222910 secs] 
482 
3.302: [Full GC (System.gc()) 673223K->564K(1023936K), 0.1299938 secs] 

-----------------------------ArrayList Auto-Capacity:---------- 
3.432: [GC (CMS Initial Mark) 40961K(1023936K), 0.0003740 secs] 
3.907: [GC (CMS Final Remark) 698381K(1023936K), 0.1091347 secs] 
796 
4.240: [Full GC (System.gc()) 911984K->56183K(1023936K), 0.1719540 secs] 
4.412: [GC (CMS Initial Mark) 56183K(1023936K), 0.0394210 secs] 
4.770: [GC (CMS Final Remark) 528894K(1023936K), 0.0726012 secs] 
690 
5.111: [Full GC (System.gc()) 893818K->2060K(1023936K), 0.1364215 secs] 
5.248: [GC (CMS Initial Mark) 20769K(1023936K), 0.0008902 secs] 
5.750: [GC (CMS Final Remark) 694113K(1023936K), 0.1124856 secs] 
704 
5.962: [Full GC (System.gc()) 808646K->2081K(1023936K), 0.1338328 secs] 
6.096: [GC (CMS Initial Mark) 21137K(1023936K), 0.0010118 secs] 
6.599: [GC (CMS Final Remark) 688155K(1023936K), 0.0899929 secs] 
661 
6.767: [Full GC (System.gc()) 810872K->2081K(1023936K), 0.1287272 secs] 
6.896: [GC (CMS Initial Mark) 21512K(1023936K), 0.0010619 secs] 
7.398: [GC (CMS Final Remark) 691216K(1023936K), 0.1083076 secs] 
681 
7.586: [Full GC (System.gc()) 803590K->2081K(1023936K), 0.1269813 secs] 
7.714: [GC (CMS Initial Mark) 2081K(1023936K), 0.0008965 secs] 

स्ट्राइपिंग जीसी जानकारी है, यह है:

-----------------------------ArrayList With Enough Capacity Allocated:---------- 
615 
540 
480 
511 
480 

-----------------------------ArrayList Auto-Capacity:---------- 
680 
708 
640 
644 
663 

हम का उपयोग करता है प्रत्येक समूह के अंतिम तीन डेटा अनुकूलन की गणना (जेआईटी और वीएम अनुकूलन से बचने के लिए)। उत्तर इस तरह आता है:

(480+511+511)/(640+644+663) = 1502/1947 = 501/639 = 100/128 
संबंधित मुद्दे