वाष्पशील महंगा है?


111

पढ़ने के बाद कंपाइलर राइटर्स के लिए JSR-133 कुकबुक को वाष्पशील के कार्यान्वयन के बारे में , विशेष रूप से "परमाणु निर्देशों के साथ सहभागिता" खंड मैं मानता हूं कि इसे अद्यतन किए बिना एक वाष्पशील चर को पढ़ने के लिए लोडलोड या लोडस्टेर बैरियर की आवश्यकता होती है। पृष्ठ के नीचे मैं देख रहा हूं कि लोडलॉड और लोडस्टोर प्रभावी रूप से एक्स 86 सीपीयू पर नहीं हैं। क्या इसका मतलब यह है कि अस्थिर पढ़ने के संचालन को x 86 पर स्पष्ट कैश अमान्य के बिना किया जा सकता है, और एक सामान्य चर पढ़ने के रूप में तेजी से होता है (अस्थिर के पुन: व्यवस्थित बाधाओं की उपेक्षा)?

मेरा मानना ​​है कि मैं इसे सही तरीके से नहीं समझता। क्या कोई मेरी देखभाल कर सकता है?

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

पुनश्च: इस बारे में अधिक जानने के लिए अपने तरीके पर मैंने निम्नलिखित महान लेखों के बारे में ठोकर खाई, और चूंकि यह प्रश्न दूसरों के लिए दिलचस्प हो सकता है, इसलिए मैं अपने लिंक यहां साझा करूंगा:


1
आप कई CPU के साथ विन्यास के बारे में मेरे संपादन को पढ़ सकते हैं। ऐसा हो सकता है कि एक अल्पकालिक संदर्भ के लिए मल्टी सीपीयू सिस्टम पर, फिर कोई भी मुख्य मेमोरी में पढ़ने / लिखने के लिए जगह नहीं लेगा।
जॉन विंट

2
अस्थिर वाचन स्वयं महंगा नहीं है। मुख्य लागत यह है कि यह अनुकूलन को कैसे रोकता है। व्यवहार में, औसतन लागत बहुत अधिक नहीं है, जब तक कि अस्थिर का उपयोग एक तंग लूप में नहीं किया जाता है।
अपरिवर्तनीय

2
Infoq ( infoq.com/articles/memory_barरियर_jvm_concurrency ) पर यह लेख भी आपको दिलचस्पी ले सकता है, यह विभिन्न आर्किटेक्चर के लिए उत्पन्न कोड पर अस्थिर और सिंक्रनाइज़ के प्रभावों को दर्शाता है। यह भी एक मामला है जहां jvm समय संकलक के आगे की तुलना में बेहतर प्रदर्शन कर सकता है, क्योंकि यह जानता है कि क्या यह एक यूनिप्रोसेसर सिस्टम पर चल रहा है और कुछ मेमोरी बाधाओं को छोड़ सकता है।
जोर्न होर्स्टमन

जवाबों:


123

इंटेल पर संयुक्त राष्ट्र का एक वाष्पशील वाचन काफी सस्ता है। यदि हम निम्नलिखित सरल मामले पर विचार करते हैं:

public static long l;

public static void run() {        
    if (l == -1)
        System.exit(-1);

    if (l == -2)
        System.exit(-1);
}

असेंबली कोड को प्रिंट करने की जावा 7 की क्षमता का उपयोग करके रन विधि कुछ इस तरह दिखाई देती है:

# {method} 'run2' '()V' in 'Test2'
#           [sp+0x10]  (sp of caller)
0xb396ce80: mov    %eax,-0x3000(%esp)
0xb396ce87: push   %ebp
0xb396ce88: sub    $0x8,%esp          ;*synchronization entry
                                    ; - Test2::run2@-1 (line 33)
0xb396ce8e: mov    $0xffffffff,%ecx
0xb396ce93: mov    $0xffffffff,%ebx
0xb396ce98: mov    $0x6fa2b2f0,%esi   ;   {oop('Test2')}
0xb396ce9d: mov    0x150(%esi),%ebp
0xb396cea3: mov    0x154(%esi),%edi   ;*getstatic l
                                    ; - Test2::run@0 (line 33)
0xb396cea9: cmp    %ecx,%ebp
0xb396ceab: jne    0xb396ceaf
0xb396cead: cmp    %ebx,%edi
0xb396ceaf: je     0xb396cece         ;*getstatic l
                                    ; - Test2::run@14 (line 37)
0xb396ceb1: mov    $0xfffffffe,%ecx
0xb396ceb6: mov    $0xffffffff,%ebx
0xb396cebb: cmp    %ecx,%ebp
0xb396cebd: jne    0xb396cec1
0xb396cebf: cmp    %ebx,%edi
0xb396cec1: je     0xb396ceeb         ;*return
                                    ; - Test2::run@28 (line 40)
0xb396cec3: add    $0x8,%esp
0xb396cec6: pop    %ebp
0xb396cec7: test   %eax,0xb7732000    ;   {poll_return}
;... lines removed

यदि आप गैस्टेटिक के 2 संदर्भों को देखते हैं, तो पहले में मेमोरी से लोड शामिल है, दूसरा लोड को रोक देता है क्योंकि मूल्य को रजिस्टर से पुन: उपयोग किया जाता है (यह पहले से ही 64 बिट पर लोड है और मेरे 32 बिट लैपटॉप पर है) यह 2 रजिस्टरों का उपयोग करता है)।

यदि हम l वैरिएबल को अस्थिर बनाते हैं तो परिणामस्वरूप असेंबली अलग है।

# {method} 'run2' '()V' in 'Test2'
#           [sp+0x10]  (sp of caller)
0xb3ab9340: mov    %eax,-0x3000(%esp)
0xb3ab9347: push   %ebp
0xb3ab9348: sub    $0x8,%esp          ;*synchronization entry
                                    ; - Test2::run2@-1 (line 32)
0xb3ab934e: mov    $0xffffffff,%ecx
0xb3ab9353: mov    $0xffffffff,%ebx
0xb3ab9358: mov    $0x150,%ebp
0xb3ab935d: movsd  0x6fb7b2f0(%ebp),%xmm0  ;   {oop('Test2')}
0xb3ab9365: movd   %xmm0,%eax
0xb3ab9369: psrlq  $0x20,%xmm0
0xb3ab936e: movd   %xmm0,%edx         ;*getstatic l
                                    ; - Test2::run@0 (line 32)
0xb3ab9372: cmp    %ecx,%eax
0xb3ab9374: jne    0xb3ab9378
0xb3ab9376: cmp    %ebx,%edx
0xb3ab9378: je     0xb3ab93ac
0xb3ab937a: mov    $0xfffffffe,%ecx
0xb3ab937f: mov    $0xffffffff,%ebx
0xb3ab9384: movsd  0x6fb7b2f0(%ebp),%xmm0  ;   {oop('Test2')}
0xb3ab938c: movd   %xmm0,%ebp
0xb3ab9390: psrlq  $0x20,%xmm0
0xb3ab9395: movd   %xmm0,%edi         ;*getstatic l
                                    ; - Test2::run@14 (line 36)
0xb3ab9399: cmp    %ecx,%ebp
0xb3ab939b: jne    0xb3ab939f
0xb3ab939d: cmp    %ebx,%edi
0xb3ab939f: je     0xb3ab93ba         ;*return
;... lines removed

इस स्थिति में चर l के लिए दोनों संदर्भात्मक संदर्भों में मेमोरी से लोड शामिल होता है, अर्थात मान को कई वाष्पशील रीडों में एक रजिस्टर में नहीं रखा जा सकता है। यह सुनिश्चित करने के लिए कि एक परमाणु पढ़ा गया है मूल्य मुख्य मेमोरी से एमएमएक्स रजिस्टर में पढ़ा जाता हैmovsd 0x6fb7b2f0(%ebp),%xmm0 में पढ़ा जाता है, रीड ऑपरेशन को एक ही निर्देश बनाता है (पिछले उदाहरण से हमने देखा कि 64 बिट वैल्यू को सामान्य रूप से 32 बिट सिस्टम पर दो 32 बिट रीड की आवश्यकता होगी)।

तो वाष्पशील रीड की कुल लागत लगभग एक मेमोरी लोड के बराबर होगी और L1 कैश एक्सेस के रूप में सस्ती हो सकती है। हालाँकि यदि कोई अन्य कोर वाष्पशील चर को लिख रहा है, तो कैश-लाइन को मुख्य मेमोरी या शायद L3 कैश एक्सेस की आवश्यकता के कारण अमान्य कर दिया जाएगा। वास्तविक लागत सीपीयू वास्तुकला पर बहुत अधिक निर्भर करेगी। इंटेल और एएमडी के बीच भी कैश सुसंगतता प्रोटोकॉल अलग हैं।


साइड नोट, java 6 में असेंबली दिखाने की समान क्षमता है (यह हॉटस्पॉट है जो इसे करता है)
bestss

+1 JDK5 में वाष्पशील को किसी भी रीड / राइट (जो उदाहरण के लिए डबल-चेक लॉकिंग को ठीक करता है) के संबंध में पुन: व्यवस्थित नहीं किया जा सकता है । क्या इसका मतलब यह है कि यह भी प्रभावित करेगा कि गैर-वाष्पशील क्षेत्र कैसे हेरफेर किए जाते हैं? यह दिलचस्प होगा कि अस्थिर और गैर-वाष्पशील क्षेत्रों तक पहुंच को मिलाया जाए।
ewernli

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

20

सामान्यतया, अधिकांश आधुनिक प्रोसेसर पर एक वाष्पशील भार एक सामान्य भार के बराबर होता है। एक अस्थिर स्टोर एक मोंटियोर-एंटर / मॉनीटर-एग्जिट का समय लगभग 1/3 है। यह उन प्रणालियों पर देखा जाता है जो कैश सुसंगत हैं।

ओपी के सवाल का जवाब देने के लिए, वाष्पशील लेखन महंगे हैं जबकि आमतौर पर रीड्स नहीं होते हैं।

क्या इसका मतलब यह है कि अस्थिर पढ़ने के संचालन को x 86 पर एक स्पष्ट कैश अमान्य के बिना किया जा सकता है, और एक सामान्य चर पढ़ने के रूप में तेज़ है (अस्थिर के पुन: व्यवस्थित विरोधाभासों की उपेक्षा)?

हां, कभी-कभी किसी क्षेत्र को मान्य करते समय सीपीयू मुख्य मेमोरी को हिट भी नहीं कर सकता है, इसके बजाय अन्य थ्रेड कैश पर जासूसी करता है और वहां से मूल्य प्राप्त करता है (बहुत सामान्य स्पष्टीकरण)।

हालाँकि, मैंने नील का सुझाव दिया है कि यदि आपके पास कई थ्रेड्स द्वारा पहुँचा गया फ़ील्ड है, तो आप इसे AtomicReference के रूप में लपेटते हैं। AtomicReference होने के नाते यह रीड / राइट्स के लिए लगभग एक ही थ्रूपुट निष्पादित करता है, लेकिन यह भी अधिक स्पष्ट है कि फ़ील्ड को कई थ्रेड द्वारा एक्सेस और संशोधित किया जाएगा।

ओपी का जवाब देने के लिए संपादित करें:

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

इसमें थोड़ा बहुत प्रोटोकॉल शामिल है लेकिन यह इस बात का अंदाजा देता है कि क्या चल रहा है। अपने अन्य प्रश्न का उत्तर देने के लिए, कई प्रोसेसर की अनुपस्थिति के साथ, वाष्पशील रीड / राइट वास्तव में कई प्रोसेसर के साथ तेज हो सकता है। वहाँ कुछ अनुप्रयोगों है कि वास्तव में एक सीपीयू के साथ तेजी से चल रहे हैं तो कई होगा।


5
एक AtomicReference एक वाष्पशील क्षेत्र के लिए एक आवरण है जिसमें अतिरिक्त मूल कार्य मिलते हैं, जैसे कि getAndSet, ComparAndSet इत्यादि। लेकिन मुझे आश्चर्य है कि आप यहां ओएस का संदर्भ क्यों देते हैं? सीपीयू ऑपकोड में कार्यक्षमता को सीधे लागू किया जाता है। और क्या इसका मतलब यह है कि मल्टी प्रोसेसर सिस्टम पर, जहां एक सीपीयू को अन्य सीपीयू की कैश सामग्री के बारे में कोई जानकारी नहीं होती है कि वाष्पशील धीमी होती हैं क्योंकि सीपीयू को हमेशा मुख्य मेमोरी को हिट करना पड़ता है?
डैनियल

Youre right मुझे याद है OS के बारे में बोला जाना चाहिए कि उसने CPU लिखा है, जो अब ठीक कर रहा है। और हाँ, मुझे पता है कि एटॉमिकरेंस केवल वाष्पशील क्षेत्रों के लिए एक आवरण है, लेकिन यह एक प्रकार के प्रलेखन के रूप में भी जोड़ता है कि क्षेत्र स्वयं कई थ्रेड द्वारा पहुंच जाएगा।
जॉन विंट

@ जॉन, आप एक परमाणु अप्रसार के माध्यम से एक और अप्रत्यक्ष क्यों जोड़ेंगे? यदि आपको CAS की आवश्यकता है - ठीक है, लेकिन AtomicUpdater एक बेहतर विकल्प हो सकता है। जहां तक ​​मुझे याद है कि एटॉमिक रिफरेंस के बारे में कोई इंट्रेंस नहीं है।
बेस्टसेल्स

@bestsss सभी सामान्य purpouses के लिए, आप सही AtomicReference.set / get और वाष्पशील लोड और स्टोर के बीच कोई अंतर नहीं है। कहा जा रहा है कि मेरे पास एक ही भावना थी (और कुछ हद तक) कि कब कौन सा उपयोग करना है। यह प्रतिक्रिया इसे थोड़ा stackoverflow.com/questions/3964317/… का विवरण दे सकती है । या तो उपयोग करना एक प्राथमिकता के रूप में अधिक है, एक साधारण अस्थिर पर परमाणु परमाणु का उपयोग करने के लिए मेरा एकमात्र तर्क स्पष्ट प्रलेखन के लिए है - जो कि खुद को सबसे बड़ा तर्क नहीं देता है या तो मैं समझता हूं
जॉन विन

एक तरफ ध्यान दें कि एक अस्थिर क्षेत्र / एटॉमिकरिएशन (सीएएस की आवश्यकता के बिना) का उपयोग करते हुए कुछ तर्क देते हैं कि बुग्गी
जॉन

12

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

चूँकि कुछ अनुकूलन उपलब्ध नहीं हैं, परिणामी कोड आवश्यक रूप से धीमा है जो कि होता है, हालाँकि यह बहुत अधिक नहीं है।

फिर भी आपको एक चर नहीं बनाना चाहिए volatileजब तक कि आपको पता न हो कि यह synchronizedब्लॉक के बाहर कई थ्रेड से एक्सेस किया जाएगा । फिर भी आपको विचार करना चाहिए कि क्या अस्थिर बनाम सबसे अच्छा विकल्प है synchronized, AtomicReferenceऔर उसके दोस्त, स्पष्ट Lockकक्षाएं, आदि।


4

एक अस्थिर चर तक पहुंचना कई मायनों में समतुल्य ब्लॉक में एक साधारण चर तक पहुंच को लपेटने के समान है। उदाहरण के लिए, एक अस्थिर चर तक पहुंच सीपीयू को उपयोग से पहले और बाद में निर्देशों को फिर से आदेश देने से रोकती है, और यह आम तौर पर निष्पादन को धीमा कर देती है (हालांकि मैं कितना नहीं कह सकता)।

अधिक आम तौर पर, एक मल्टी-प्रोसेसर सिस्टम पर मैं यह नहीं देखता कि दंड के बिना एक अस्थिर चर तक पहुंच कैसे हो सकती है - प्रोसेसर ए पर लिखने को सुनिश्चित करने के लिए कुछ तरीका होना चाहिए जो प्रोसेसर बी पर एक पढ़ने के लिए सिंक्रनाइज़ होगा।


4
वाष्पशील चर को पढ़ना, मॉनिटर-एंटर करने की तुलना में एक ही जुर्माना है, निर्देशों की पुन: व्यवस्थित करने की संभावनाओं के बारे में, जबकि वाष्पशील चर लिखना मॉनिटर-निकास के बराबर है। एक अंतर यह हो सकता है कि कौन से चर (जैसे प्रोसेसर कैश) फ्लश या अमान्य हो जाते हैं। जबकि सिंक्रनाइज़ फ्लश या सब कुछ अमान्य है, अस्थिर चर तक पहुंच हमेशा कैश-अनदेखी होनी चाहिए।
डैनियल

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

@MichaelBarker: क्या आप सुनिश्चित हैं कि सभी मॉनिटर को कर्नेल द्वारा संरक्षित किया जाना चाहिए न कि ऐप द्वारा?
डैनियल

@ डैनियल: यदि आप एक सिंक्रनाइज़ ब्लॉक या लॉक का उपयोग करके मॉनिटर का प्रतिनिधित्व करते हैं, तो हाँ, लेकिन केवल अगर मॉनिटर संतुष्ट है। कर्नेल मध्यस्थता के बिना ऐसा करने का एकमात्र तरीका एक ही तर्क का उपयोग करना है, लेकिन थ्रेड को पार्क करने के बजाय व्यस्त स्पिन।
माइकल बार्कर

@MichaelBarker: ओके, संतोषप्रद तालों के लिए मैं इसे समझता हूं।
डैनियल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.