इकाई / घटक प्रणालियों में संस्थाओं के समवर्ती प्रसंस्करण के लिए पढ़ें / गणना / लिखें चरणों को कुशलतापूर्वक अलग करना


11

सेट अप

मेरे पास एक इकाई-घटक वास्तुकला है जहाँ संस्थाओं में विशेषताओं का एक सेट हो सकता है (जो कि बिना किसी व्यवहार के शुद्ध डेटा हैं) और वहां मौजूद सिस्टम हैं जो इकाई तर्क को चलाते हैं जो उस डेटा पर कार्य करते हैं। अनिवार्य रूप से, कुछ छद्म कोड में:

Entity
{
    id;
    map<id_type, Attribute> attributes;
}

System
{
    update();
    vector<Entity> entities;
}

एक ऐसी प्रणाली जो स्थिर दर पर सभी संस्थाओं के साथ चलती है

MovementSystem extends System
{
   update()
   {
      for each entity in entities
        position = entity.attributes["position"];
        position += vec3(1,1,1);
   }
}

आवश्यक रूप से, मैं यथासंभव अपडेट () को अपडेट करने की कोशिश कर रहा हूं। यह पूरे सिस्टम को समानांतर में चलाकर किया जा सकता है, या एक सिस्टम के प्रत्येक अपडेट () घटकों के एक जोड़े को दे कर अलग-अलग धागे एक ही सिस्टम के अपडेट को निष्पादित कर सकते हैं, लेकिन उस सिस्टम के साथ पंजीकृत संस्थाओं के एक अलग सबसेट के लिए।

मुसीबत

दिखाए गए मूवमेंट सिस्टम के मामले में, समानांतरीकरण तुच्छ है। चूंकि इकाइयाँ एक दूसरे पर निर्भर नहीं होती हैं, और साझा डेटा को संशोधित नहीं करती हैं, इसलिए हम समानांतर में सभी संस्थाओं को स्थानांतरित कर सकते हैं।

हालांकि, इन प्रणालियों को कभी-कभी आवश्यकता होती है कि इकाइयां एक-दूसरे के साथ, कभी-कभी एक ही प्रणाली के भीतर (लेकिन / से डेटा पढ़ें / लिखें) के साथ परस्पर क्रिया करती हैं, लेकिन अक्सर विभिन्न प्रणालियों के बीच होती हैं जो एक-दूसरे पर निर्भर करती हैं।

उदाहरण के लिए, भौतिकी प्रणाली में कभी-कभी संस्थाएं एक-दूसरे के साथ बातचीत कर सकती हैं। दो ऑब्जेक्ट टकराते हैं, उनके स्थान, वेग और अन्य विशेषताओं को उनसे पढ़ा जाता है, अपडेट किया जाता है, और फिर अद्यतन विशेषताओं को दोनों संस्थाओं को वापस लिखा जाता है।

और इससे पहले कि इंजन में रेंडरिंग सिस्टम संस्थाओं को प्रस्तुत करना शुरू कर सके, उसे अन्य प्रणालियों के लिए निष्पादन को पूरा करने के लिए इंतजार करना होगा ताकि यह सुनिश्चित हो सके कि सभी प्रासंगिक विशेषताएँ वे हैं जो उन्हें होना चाहिए।

यदि हम नेत्रहीन रूप से इसे समानांतर करने की कोशिश करते हैं, तो यह शास्त्रीय दौड़ की स्थिति को जन्म देगा जहां विभिन्न प्रणालियां एक ही समय में डेटा को पढ़ और संशोधित कर सकती हैं।

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

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

उपाय?

मेरी सोच में, एक संभावित समाधान एक ऐसी प्रणाली होगी जहां डेटा पढ़ना / अपडेट करना और लिखना अलग किया जाता है, ताकि एक महंगे चरण में, सिस्टम केवल डेटा को पढ़े और गणना करें कि उन्हें क्या गणना करने की आवश्यकता है, किसी तरह परिणाम कैश करें, और फिर सभी लिखें एक अलग लेखन पास में लक्षित संस्थाओं को वापस डेटा बदल दिया। सभी सिस्टम राज्य में डेटा पर कार्य करेंगे कि यह फ्रेम की शुरुआत में था, और फिर फ्रेम के अंत से पहले, जब सभी सिस्टम अपडेट करना समाप्त कर देते हैं, एक क्रमबद्ध लेखन पास होता है जहां सभी अलग-अलग से कैश किए गए परिणाम होते हैं सिस्टम को लक्ष्य संस्थाओं के माध्यम से पुन: लिखा जाता है।

यह (शायद गलत है?) विचार पर आधारित है कि परिणाम के कैशिंग और लेखन पास के लिए आसान समानांतर जीत जीत की लागत (रनटाइम प्रदर्शन दोनों के साथ-साथ एक कोड उपरि) के मामले से आगे निकल सकती है।

प्रश्न

इष्टतम प्रदर्शन को प्राप्त करने के लिए ऐसी प्रणाली कैसे लागू की जा सकती है? ऐसी प्रणाली का कार्यान्वयन विवरण क्या हैं और इस समाधान का उपयोग करने के लिए इच्छुक इकाई-घटक प्रणाली के लिए आवश्यक शर्तें क्या हैं?

जवाबों:


1

----- (संशोधित प्रश्न पर आधारित)

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

एक इकाई से पढ़ें और कुछ गणना करें ... और बाद में एक मध्यवर्ती भंडारण क्षेत्र में कहीं परिणाम पर पकड़? मुझे नहीं लगता कि आप पढ़ सकते हैं + गणना + स्टोर कर सकते हैं जिस तरह से आप सोचते हैं और उम्मीद करते हैं कि यह मध्यवर्ती स्टोर कुछ भी हो लेकिन शुद्ध उपरि हो सकता है।

साथ ही, चूंकि आप निरंतर प्रसंस्करण कर रहे हैं, मुख्य नियम जिसका आप अनुसरण करना चाहते हैं, वह है सीपीयू कोर के प्रति एक धागा। मुझे लगता है कि आप इसे गलत परत पर देख रहे हैं, पूरे सिस्टम को देखने की कोशिश करें, न कि अलग-अलग संस्थाओं को।

अपने सिस्टम के बीच एक निर्भरता ग्राफ बनाएं, पहले सिस्टम के काम से किस सिस्टम को परिणाम चाहिए, इसका एक पेड़। एक बार जब आपके पास वह निर्भरता का पेड़ हो जाता है तो आप आसानी से एक थ्रेड पर प्रोसेस करने के लिए संस्थाओं से भरा पूरा सिस्टम भेज सकते हैं ।

तो चलिए बताते हैं कि आपकी निर्भरता पेड़ brambles और भालू जाल, एक डिजाइन मुद्दा का मोरास है लेकिन हमें जो हमारे पास है उसके साथ काम करना होगा। यहां सबसे अच्छा मामला यह है कि प्रत्येक प्रणाली के अंदर प्रत्येक इकाई उस प्रणाली के अंदर किसी अन्य परिणाम पर निर्भर नहीं करती है। यहां आप आसानी से थ्रेड, 0-99 और 100-199 पर दो थ्रेड्स पर उदाहरण के लिए दो कोर और 200 निकाय के साथ प्रसंस्करण को उपविभाजित करते हैं, जो इस सिस्टम का मालिक है।

या तो मामले में, प्रत्येक चरण में आपको परिणामों के लिए इंतजार करना होगा जो कि अगले चरण पर निर्भर करता है। लेकिन यह ठीक है क्योंकि थोक में संसाधित किए जा रहे डेटा के दस बड़े ब्लॉकों के परिणामों की प्रतीक्षा करना छोटे ब्लॉकों के लिए एक हजार बार सिंक्रनाइज़ करने के लिए बेहतर है।

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

और सीरियल प्रोसेसिंग को केवल प्रत्येक अनुक्रम बिंदु के बीच समानांतर किया जा सकता है, लेकिन समग्र रूप से नहीं। लेकिन आपको इसका एहसास है क्योंकि यह आपकी समस्या का मूल है। यहां तक ​​कि अगर आप कैश डेटा से पढ़ते हैं जो अभी तक नहीं लिखा गया है तो भी आपको उपलब्ध होने के लिए उस कैश पर इंतजार करने की आवश्यकता है।

यदि इस प्रकार की बाधाओं के साथ समानांतर आर्किटेक्चर बनाना आसान या संभव भी था, तो कंप्यूटर विज्ञान बैलेटले पार्क के बाद से समस्या से नहीं जूझ रहा होगा।

एकमात्र वास्तविक समाधान यह होगा कि इन सभी निर्भरताओं को अनुक्रम बिंदुओं को कम से कम किया जाए जितना संभव हो सके। इसमें उप-विभाजन प्रणालियों को अनुक्रमिक प्रसंस्करण चरणों में शामिल किया जा सकता है , जहां प्रत्येक उपप्रणाली के अंदर, धागे के साथ समानांतर जाना तुच्छ हो जाता है।

सर्वश्रेष्ठ मुझे इस समस्या के लिए मिला और यह वास्तव में सिफारिश करने से ज्यादा कुछ नहीं है कि अगर ईंट की दीवार पर अपना सिर मारना दर्द होता है, तो इसे छोटी ईंट की दीवारों में तोड़ दें ताकि आप केवल अपने झटकों को मार सकें।


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

इसके अलावा कोई अपराध का इरादा नहीं है: पी
ट्रैविस

@TravisG अक्सर ऐसी प्रणालियाँ होती हैं जो पैट्रिक द्वारा बताई गई अन्य प्रणालियों पर निर्भर करती हैं। फ़्रेम देरी से बचने के लिए या एक लॉजिक स्टेप के एक भाग के रूप में कई अपडेट पास से बचने के लिए, स्वीकृत समाधान अद्यतन चरण को क्रमबद्ध करना है, जहाँ संभव हो समानांतर में सबसिस्टम चल रहा है, निर्भरता के साथ सबसिस्टम को क्रमबद्ध करते हुए, प्रत्येक के अंदर छोटे अपडेट को बैचते हुए। सबसिस्टम एक समांतर_for () अवधारणा का उपयोग कर रहा है। यह सबसिस्टम अपडेट पास जरूरतों के किसी भी संयोजन के लिए आदर्श है और सबसे अधिक लचीला है।
Naros

0

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

यह विधि दौड़ की स्थितियों को भी हटा देती है क्योंकि सभी प्रणालियाँ एक बासी अवस्था के साथ काम कर रही होंगी जो सिस्टम द्वारा संसाधित होने से पहले / बाद में नहीं बदलेगी।


यह जॉन कार्मैक का ढेर कॉपी ट्रिक है, है ना? मैंने इसके बारे में सोचा है, लेकिन यह अभी भी एक ही समस्या है कि कई धागे एक ही आउटपुट स्थान पर लिख सकते हैं। यह शायद एक अच्छा समाधान है यदि आप सब कुछ "सिंगल-पास" रखते हैं, लेकिन मुझे यकीन नहीं है कि यह कितना संभव है।
ट्रैविसग

GUI रिएक्टिविटी सहित स्क्रीन डिस्प्ले लेटेंसी में इनपुट 1 फ्रेम टाइम तक बढ़ जाएगा। जो क्रिया / समय के खेल या आरटीएस जैसे भारी जीयूआई जोड़तोड़ के लिए महत्वपूर्ण हो सकता है। मैं इसे एक रचनात्मक विचार के रूप में पसंद करता हूं, हालांकि।
पैट्रिक ह्यूजेस

मैंने एक दोस्त से इस बारे में सुना, और यह नहीं पता था कि यह एक कार्मैक चाल थी। रेंडरिंग कैसे की जाती है, इसके आधार पर, घटकों का प्रतिपादन एक फ्रेम के पीछे हो सकता है। आप इसे अपडेट चरण के लिए उपयोग कर सकते हैं, फिर सब कुछ अद्यतित होने के बाद वर्तमान कॉपी से रेंडर करें।
जॉन मैकडोनाल्ड

0

मुझे पता है कि डेटा के समानांतर प्रसंस्करण से निपटने वाले 3 सॉफ़्टवेयर डिज़ाइन:

  1. क्रमिक रूप से डेटा को प्रोसेस करें : हम कई थ्रेड का उपयोग करके डेटा को प्रोसेस करना चाहते हैं, क्योंकि यह अजीब लग सकता है। हालाँकि, अधिकांश परिदृश्यों को कार्य पूरा करने के लिए कई थ्रेड्स की आवश्यकता होती है, जबकि अन्य थ्रेड प्रतीक्षा करते हैं या लंबे समय तक चलने वाले संचालन करते हैं। अधिकांश सामान्य उपयोग यूआई थ्रेड्स हैं जो उपयोगकर्ता इंटरफ़ेस को एक ही थ्रेड में अपडेट करते हैं, जबकि अन्य थ्रेड पृष्ठभूमि में चल सकते हैं, लेकिन यूआई तत्वों को सीधे एक्सेस करने की अनुमति नहीं है। पृष्ठभूमि थ्रेड्स से परिणाम पारित करने के लिए, नौकरी की कतारों का उपयोग किया जाता है जो अगले उचित अवसर पर एकल थ्रेड द्वारा संसाधित किया जाएगा।
  2. सिंक्रनाइज़ डेटा का उपयोग: इस सबसे आम तरीका एक ही डेटा तक पहुँचने से अधिक थ्रेड को संभालने के लिए है। अधिकांश प्रोग्रामिंग भाषाओं में वर्गों और उपकरणों का निर्माण होता है, जहां अनुभागों को लॉक करने के लिए डेटा को पढ़ा जाता है और / या एकाधिक थ्रेड्स द्वारा समवर्ती रूप से लिखा जाता है। हालांकि, संचालन को अवरुद्ध नहीं करने के लिए सावधानी बरतनी चाहिए। दूसरी ओर, इस दृष्टिकोण की वास्तविक समय अनुप्रयोगों में बहुत अधिक लागत आती है।
  3. संभाल समवर्ती संशोधन केवल जब वे होती हैं: इस आशावादी दृष्टिकोण से किया जा सकता है, तो टकराव शायद ही कभी हो। यदि कोई एकाधिक पहुंच नहीं थी, तो डेटा को पढ़ा और संशोधित किया जाएगा, लेकिन एक तंत्र है जो डेटा के समवर्ती रूप से अपडेट किए जाने पर पता लगाता है। यदि ऐसा होता है, तो एक ही गणना को फिर से सफलता मिलने तक क्रियान्वित किया जाएगा।

यहां प्रत्येक दृष्टिकोण के लिए कुछ उदाहरण दिए गए हैं जो एक इकाई प्रणाली में उपयोग किए जा सकते हैं:

  1. एक CollisionSystemपढ़ता है Positionऔर RigidBodyघटकों के बारे में सोचता है और एक अद्यतन करना चाहिए VelocityVelocityसीधे तौर पर हेरफेर करने के CollisionSystemबजाय , वसीयत एक CollisionEventकाम की कतार में डाल देगा EventSystem। इस घटना को तब अन्य अपडेट के साथ क्रमिक रूप से संसाधित किया जाएगा Velocity
  2. एक EntitySystemघटक के एक सेट को परिभाषित करता है जिसे उसे पढ़ने और लिखने की आवश्यकता होती है। प्रत्येक के लिए Entityयह प्रत्येक घटक के लिए एक रीड लॉक को एक्वायर करेगा जिसे वह पढ़ना चाहता है, और प्रत्येक घटक के लिए एक राइट लॉक जिसे वह अपडेट करना चाहता है। इस तरह, हर EntitySystemकोई समसामयिक रूप से पढ़े गए घटकों को अपडेट करते समय सक्षम हो जाएगा।
  3. का उदाहरण लेते हुए MovementSystem, Positionघटक अपरिवर्तनीय है और इसमें एक संशोधन संख्या है। MovementSystemSavely पढ़ता है Positionऔर Velocityघटक और नई गणना करता है Position, पढ़ने के लिए बढ़ाने संशोधन संख्या और अद्यतन करने की कोशिश करता Positionघटक। समवर्ती संशोधन के मामले में, फ्रेमवर्क अपडेट पर यह इंगित करता है और Entityइसे उन संस्थाओं की सूची में वापस रखा जाएगा जिन्हें अपडेट किया जाना है MovementSystem

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

मुझे उम्मीद है कि मैं चर्चा में कुछ विचार जोड़ सकता हूं और कृपया मुझे बताएं कि क्या इसके बारे में कुछ खबरें हैं।

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