65 तत्वों की सरणी घोषित करने की तुलना में 64 तत्वों 1000 गुना तेजी के साथ कई सरणियों की घोषणा


91

हाल ही में मैंने देखा कि 65 तत्वों वाले एक सरणी को घोषित करने से 65 तत्वों के साथ एक ही प्रकार की सरणी घोषित करने की तुलना में बहुत तेज (> 1000 गुना) है।

यहाँ वह कोड है जो मैंने इसका परीक्षण किया था:

public class Tests{
    public static void main(String args[]){
        double start = System.nanoTime();
        int job = 100000000;//100 million
        for(int i = 0; i < job; i++){
            double[] test = new double[64];
        }
        double end = System.nanoTime();
        System.out.println("Total runtime = " + (end-start)/1000000 + " ms");
    }
}

यह लगभग 6 एमएस में चलता है, अगर मैं इसके new double[64]साथ प्रतिस्थापित करता हूं तो new double[65]लगभग 7 सेकंड लगते हैं। यदि यह समस्या अधिक से अधिक थ्रेड्स में फैली हुई है, तो यह समस्या और अधिक गंभीर हो जाती है, जहां से मेरी समस्या उत्पन्न होती है।

यह समस्या विभिन्न प्रकार के सरणियों जैसे int[65]या के साथ भी होती है String[65]। यह समस्या बड़े तारों के साथ नहीं होती है: String test = "many characters";लेकिन जब यह बदल जाता है तो यह होने लगता हैString test = i + "";

मैं सोच रहा था कि ऐसा क्यों है और अगर इस समस्या को दरकिनार करना संभव है।


3
ऑफ-नोट: बेंचमार्किंग के लिए System.nanoTime()अधिक पसंद किया जाना चाहिए System.currentTimeMillis()
रॉकेटबॉय

4
मैं बस उत्सुक हूँ ? क्या आप लिनक्स के तहत हैं? क्या OS के साथ व्यवहार बदलता है?
bsd

9
कैसे पृथ्वी पर इस सवाल का एक downvote मिला ??
रोहित जैन

2
अगर मैं इस कोड को byteइसके बजाय चलाता हूं, तो FWIW, मुझे समान प्रदर्शन विसंगतियां दिखाई देती हैं double
ओलिवर चार्ल्सवर्थ

3
@ThomasJungblut: तो ओपी के प्रयोग में विसंगति क्या है?
ओलिवर चार्ल्सवर्थ

जवाबों:


88

आप एक व्यवहार देख रहे हैं जो आपके जावा वीएम के JIT कंपाइलर द्वारा किए गए अनुकूलन के कारण होता है । यह व्यवहार 64 तत्वों तक के स्केलर सरणियों के साथ प्रजनन योग्य है, और 64 से बड़े सरणियों के साथ ट्रिगर नहीं किया गया है।

विवरण में जाने से पहले, हम लूप के शरीर पर एक करीब से नज़र डालते हैं:

double[] test = new double[64];

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

बेंचमार्क के लिए आपको कम से कम निम्नलिखित दो दिशानिर्देशों का पालन करना चाहिए। यदि आपने ऐसा किया होता, तो अंतर काफी छोटा होता।

  • कई बार बेंचमार्क निष्पादित करके JIT कंपाइलर (और ऑप्टिमाइज़र) को वार्म-अप करें।
  • हर अभिव्यक्ति के परिणाम का उपयोग करें और इसे बेंचमार्क के अंत में प्रिंट करें।

अब विवरण में चलते हैं। आश्चर्य की बात नहीं है कि एक अनुकूलन है जो स्केलर सरणियों के लिए ट्रिगर होता है जो 64 तत्वों से बड़ा नहीं है। अनुकूलन एस्केप विश्लेषण का हिस्सा है । यह ढेर पर छोटी वस्तुओं और छोटे सरणियों को ढेर पर आवंटित करने के बजाय डालता है - या इससे भी बेहतर उन्हें पूरी तरह से अनुकूलित करता है। आप इसके बारे में कुछ जानकारी ब्रायन गोएत्ज़ द्वारा 2005 में लिखे गए लेख में पा सकते हैं:

अनुकूलन को कमांड लाइन विकल्प के साथ अक्षम किया जा सकता है -XX:-DoEscapeAnalysis। स्केलर सरणियों के लिए जादुई मूल्य 64 को कमांड लाइन पर भी बदला जा सकता है। यदि आप अपने कार्यक्रम को निम्नानुसार निष्पादित करते हैं, तो 64 और 65 तत्वों के साथ सरणियों के बीच कोई अंतर नहीं होगा:

java -XX:EliminateAllocationArraySizeLimit=65 Tests

ऐसा कहने के बाद, मैं ऐसे कमांड लाइन विकल्पों का उपयोग करके दृढ़ता से हतोत्साहित करता हूं। मुझे संदेह है कि यह एक यथार्थवादी अनुप्रयोग में एक बड़ा अंतर बनाता है। मैं केवल इसका उपयोग करूंगा, अगर मैं आवश्यकता के बारे में पूरी तरह से आश्वस्त हो जाऊंगा - और कुछ छद्म बेंचमार्क के परिणामों के आधार पर नहीं।


9
लेकिन
आशावादी

10
@ एनोसिड: जबकि ओपी का कोड यथार्थवादी नहीं हो सकता है, यह स्पष्ट रूप से जेवीएम में एक दिलचस्प / अप्रत्याशित व्यवहार को ट्रिगर कर रहा है, जिसका अन्य स्थितियों में निहितार्थ हो सकता है। मुझे लगता है कि यह पूछना वैध है कि ऐसा क्यों हो रहा है।
ओलिवर चार्ल्सवर्थ

1
@ThomasJungblut मुझे नहीं लगता कि लूप हटा दिया जाता है। आप लूप के बाहर "इंट टोटल" जोड़ सकते हैं और "टोटल + = टेस्ट [0];" ऊपर के उदाहरण के लिए। फिर परिणाम को प्रिंट करते हुए आप देखेंगे कि कुल = 100 मिलियन और यह कम से कम एक सेकंड में चलता है।
सिपको

1
स्टैक प्रतिस्थापन पर ढेर आवंटन के साथ ढेर आवंटन की जगह, मक्खी पर संकलित कोड के साथ व्याख्या कोड की जगह के बारे में है। EliminateAllocationArraySizeLimit एरे की सीमा का आकार है जिसे भागने के विश्लेषण में स्केलर बदली माना जाता है। इसलिए मुख्य बिंदु जो संकलक अनुकूलन के कारण प्रभाव सही है, लेकिन यह स्टैक आवंटन के कारण नहीं है, लेकिन भागने के विश्लेषण चरण के कारण आवंटन को नोटिस करने की आवश्यकता नहीं है।
kiheru

2
@ सिपको: आप लिख रहे हैं कि एप्लिकेशन थ्रेड्स की संख्या के साथ स्केलिंग नहीं कर रहा है। यह एक संकेत है, कि समस्या उस माइक्रो ऑप्टिमाइज़ेशन से संबंधित नहीं है जिसके बारे में आप पूछ रहे हैं। मैं छोटे हिस्सों के बजाय बड़ी तस्वीर को देखने की सलाह देता हूं।
nosid

2

किसी भी वस्तु के आकार के आधार पर अंतर करने की कोई भी संख्या हो सकती है।

जैसा कि नोसिड ने कहा, जेआईटीसी (सबसे अधिक संभावना है) स्टैक पर छोटी "स्थानीय" वस्तुओं को आवंटित कर सकता है, और "छोटे" सरणियों के लिए आकार का कटऑफ 64 तत्वों पर हो सकता है।

ढेर पर आवंटित करना ढेर में आवंटित करने की तुलना में काफी तेज है, और, बिंदु से अधिक, ढेर को कचरा एकत्र करने की आवश्यकता नहीं है, इसलिए जीसी ओवरहेड बहुत कम हो जाता है। (और इस परीक्षण मामले के लिए जीसी ओवरहेड कुल निष्पादन समय का 80-90% होने की संभावना है।)

इसके अलावा, एक बार जब मूल्य को स्टैक-आबंटित किया जाता है तो JITC "डेड कोड एलिमिनेशन" कर सकता है, यह निर्धारित करता है कि का परिणाम new उपयोग कहीं भी नहीं किया गया है, और, आश्वासन देने के बाद कोई साइड-इफ़ेक्ट नहीं है जो खो जाएगा, पूरे newऑपरेशन को खत्म कर देगा , और फिर (अब खाली) लूप ही।

यहां तक ​​कि अगर JITC स्टैक आवंटन नहीं करता है, तो यह पूरी तरह से संभव है कि एक निश्चित आकार से छोटी वस्तुओं को बड़े ऑब्जेक्ट्स की तुलना में एक अलग तरीके से (उदाहरण के लिए, एक अलग "स्पेस") में आवंटित किया जाए। (आम तौर पर यह बहुत नाटकीय समय मतभेद पैदा नहीं करेगा, हालांकि।)


इस धागे को देर से। ढेर पर आवंटन से अधिक तेजी से क्यों आवंटित किया जा रहा है? कुछ लेखों के अनुसार, ढेर पर आवंटित करने में ~ 12 निर्देश लगते हैं। सुधार की बहुत गुंजाइश नहीं है।
भंवर

@ वोर्टेक्स - स्टैक को आवंटित करने में 1-2 निर्देश लगते हैं। लेकिन यह एक पूरे स्टैक फ्रेम को आवंटित करना है। स्टैक फ्रेम को रूटीन के लिए रजिस्टर सेव एरिया होने के लिए वैसे भी आवंटित किया जाना चाहिए, इसलिए एक ही समय में आवंटित किए गए किसी भी अन्य चर "निशुल्क" हैं। और जैसा कि मैंने कहा, स्टैक को जीसी की आवश्यकता नहीं है। एक ढेर आइटम के लिए जीसी ओवरहेड ढेर आवंटन ऑपरेशन की लागत से कहीं अधिक बड़ा है।
हॉट लिक्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.