2012-06-26 24 views
8

जेवीएम स्ट्रिंग्स के आधार पर स्ट्रिंग्स के निर्माण के तरीके से संबंधित एक प्रश्न के बाद, मैंने उल्लेख किया है कि जब कोई [char] नई स्ट्रिंग के इंटीरियर पर कॉपी हो जाता है तो कोई पुनरावृत्ति नहीं होती है , क्योंकि System.arraycopy को अंततः बुलाया जाता है, जो किसी मूल, कार्यान्वयन-निर्भर स्तर (the original question) पर memcpy जैसे फ़ंक्शन का उपयोग करके वांछित स्मृति की प्रतिलिपि बनाता है।System.arraycopy के OpenJDK कार्यान्वयन

मैं इसे अपने लिए जांचना चाहता था, इसलिए मैंने ओपनजेड 7 स्रोत कोड डाउनलोड किया और इसे ब्राउज़ करना शुरू कर दिया। मैं OpenJDK सी ++ स्रोत कोड में System.arraycopy के कार्यान्वयन, openjdx/hotspot/src/share/vm/oops/objArrayKlass.cpp में पाया:

if (stype == bound || Klass::cast(stype)->is_subtype_of(bound)) { 
    // elements are guaranteed to be subtypes, so no check necessary 
    bs->write_ref_array_pre(dst, length); 
    Copy::conjoint_oops_atomic(src, dst, length); 
} else { 
    // slow case: need individual subtype checks 

तत्वों कोई प्रकार चेक (कि उदाहरण, आदिम डेटा प्रकार सरणियों के लिए, के साथ मामला है), कॉपी की जरूरत है: : conjoin_oops_atomic बुलाया जाता है।

Copy::conjoint_oops_atomic समारोह 'copy.hpp' में रहता है:

// overloaded for UseCompressedOops 
static void conjoint_oops_atomic(narrowOop* from, narrowOop* to, size_t count) { 
    assert(sizeof(narrowOop) == sizeof(jint), "this cast is wrong"); 
    assert_params_ok(from, to, LogBytesPerInt); 
    pd_conjoint_jints_atomic((jint*)from, (jint*)to, count); 
} 

अब हम मंच निर्भर कर रहे हैं, के रूप में प्रति आपरेशन एक अलग कार्यान्वयन, ओएस/वास्तुकला पर आधारित है। मैं विंडोज के साथ एक उदाहरण के रूप में जाऊंगा। openjdk\hotspot\src\os_cpu\windows_x86\vm\copy_windows_x86.inline.hpp:

static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { 
// Do better than this: inline memmove body NEEDS CLEANUP 
if (from > to) { 
    while (count-- > 0) { 
    // Copy forwards 
    *to++ = *from++; 
    } 
} else { 
    from += count - 1; 
    to += count - 1; 
    while (count-- > 0) { 
    // Copy backwards 
    *to-- = *from--; 
    } 
} 
} 

और ... मेरे आश्चर्य करने के लिए, यह तत्व (OOP मान) के माध्यम से iterates, उनमें से एक (प्रतीत होता है) के बाद एक को कॉपी। क्या कोई समझा सकता है कि सरणी में तत्वों के माध्यम से, मूल स्तर पर, प्रतिलिपि क्यों की जाती है?

उत्तर

4

jint सबसे निकट int जो सबसे निकट पुराने हार्डवेयर वास्तुकला WORD, जो मूल रूप से डेटा बस की चौड़ाई के रूप में एक ही आकार है करने के लिए नक्शे के लिए नक्शे क्योंकि।

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

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

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

शायद वे एमएमएक्स दिनचर्या शामिल कर सकते थे, लेकिन उन्होंने मुझे यह नहीं बताया कि सूर्य ने नहीं सोचा था कि यह आधुनिक हार्डवेयर पर इसके प्रदर्शन के लिए पर्याप्त प्रदर्शन था।

+0

वाह, धन्यवाद! यह पहली बार ओपनजेडीके कार्यान्वयन के माध्यम से थोड़ा उलझन में था, इसलिए मुझे कुछ गलत होने की उम्मीद थी। : पी तो आप कैसे सोचते हैं कि यह अनुकूलन होता है? मैंने कुछ परीक्षण किए, और सिस्टम।नियमित जावा मार्ग की तुलना में 10000 इंच की प्रतिलिपि बनाने में एरेकॉपी दो गुना तेज है। सी ++ में एक समान कार्य अभी भी काफी तेज़ है, हालांकि परिणाम विभिन्न संकलक अनुकूलन से प्रभावित हो सकते हैं। –

+0

एक C++ प्रति एक कचरा कलेक्टर एक अलग थ्रेड पर चल भी नहीं है। यहां तक ​​कि यदि आप कचरा उत्पन्न नहीं करते हैं, तो कलेक्टर को यह सत्यापित करने के लिए कुछ चक्र चुरा लेना पड़ता है कि इसका कोई काम नहीं है। मुझे यकीन है कि अगर संकलक arraycopy पाश unrolling है या यदि हार्डवेयर कैश में सरणी के पूरे ब्लॉक प्रीफ़ेचिंग है नहीं कर रहा हूँ। वास्तव में, माइक्रोकोड अनुकूलन के साथ, यह ज्ञान की मेरी गहराई से परे है। यही कारण है कि प्रोफाइलिंग इतना महत्वपूर्ण है, यह परीक्षण है कि यह साबित होता है कि अनुकूलन सार्थक था। –