2 डी शहर के बिल्डर में महंगे कार्यों के लिए प्रदर्शन में सुधार कैसे करें


9

मैंने पहले से ही उत्तर खोज लिए हैं, लेकिन मैं महंगे कार्यों / गणनाओं को संभालने के लिए सबसे अच्छा तरीका जानने में सक्षम नहीं था।

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

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

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

    // Go through all buildings affected by erasing this road tile.
    foreach(var affectedBuilding in affectedBuildings) {
        // Get buildings within radius.
        foreach(var radiusTile in affectedBuilding.RadiusTiles) {
            // Get all buildings on Map within this radius (which is technially another foreach).
            var buildingsInRadius = TileMap.Buildings.Where(b => b.TileIndex == radiusTile.TileIndex);  
    
            // Do stuff.
        }
    }

यह सब एक सेकंड के लिए 60 से लगभग 10 तक मेरे एफपीएस को तोड़ देता है ।

तो क्या मैं कर सकता था। मेरे विचार होंगे:

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

अंतिम दृष्टिकोण का उपयोग करते हुए मैं इसके बदले में एक घोंसले के शिकार को हटा सकता था:

// Go through all buildings affected by erasing this road tile.
foreach(var affectedBuilding in affectedBuildings) {
    // Go through buildings within radius.
    foreach(var buildingInRadius in affectedBuilding.BuildingsInRadius) {
        // Do stuff.
    }
}

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

मुझे आपके विचारों और टिप्पणियों की प्रतीक्षा है!

आपका बहुत बहुत धन्यवाद!


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

विस्तृत जानकारी के लिए धन्यवाद। मुझे निर्भरता का विचार पसंद है! मैं उस एक पर एक नजर डालूंगा!
येहकी

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

मुझे खुशी है कि आपने इसे उपयोगी पाया: D
Exaila

जवाबों:


3

कैशिंग बिल्डिंग कवरेज

सूचनाओं के निर्माण पर विचार करने का विचार है कि कौन-सी इमारतें एक प्रभावकार-निर्माण की श्रेणी में हैं (जो आप या तो प्रभावकार से या प्रभावित में कैश कर सकते हैं) निश्चित रूप से एक अच्छा विचार है। इमारतें (आमतौर पर) चलती नहीं हैं, इसलिए इन महंगी गणनाओं को फिर से करने का कोई कारण नहीं है। "यह इमारत क्या प्रभावित करती है" और "इस इमारत को क्या प्रभावित करता है" कुछ ऐसा है जिसे आपको केवल यह देखना होगा कि किसी भवन का निर्माण किया जाए या हटाया जाए।

यह मेमोरी के लिए सीपीयू साइकिल का एक क्लासिक एक्सचेंज है।

क्षेत्र द्वारा कवरेज की जानकारी को संभालना

यदि यह पता चलता है कि आप इस जानकारी का ट्रैक रखने के लिए बहुत अधिक मेमोरी का उपयोग कर रहे हैं, तो देखें कि क्या आप मानचित्र क्षेत्रों द्वारा ऐसी जानकारी को संभाल सकते हैं। अपने मानचित्र को वर्ग क्षेत्रों में विभाजित करें n*nटाइल्स। यदि कोई क्षेत्र पूरी तरह से अग्निशमन विभाग द्वारा कवर किया जाता है, तो उस क्षेत्र की सभी इमारतें भी कवर हो जाती हैं। इसलिए आपको केवल क्षेत्र द्वारा कवरेज की जानकारी संग्रहीत करने की आवश्यकता है, न कि व्यक्तिगत भवन द्वारा। यदि कोई क्षेत्र केवल आंशिक रूप से कवर किया गया है, तो आपको उस क्षेत्र में उप-निर्माण कनेक्शन को संभालने के लिए वापस गिरना होगा। तो आपके भवनों का अपडेट-फंक्शन सबसे पहले यह जाँच करेगा कि "क्या यह इमारत अग्निशमन विभाग द्वारा कवर की गई है?" और यदि नहीं "क्या यह भवन व्यक्तिगत रूप से अग्निशमन विभाग द्वारा कवर किया गया है?"। यह अपडेट को गति भी देता है, क्योंकि जब एक अग्निशमन विभाग को हटा दिया जाता है, तो आपको अब 2000 इमारतों के कवरेज राज्यों को अपडेट करने की आवश्यकता नहीं है, आपको केवल 100 इमारतों और 25 क्षेत्रों को अपडेट करने की आवश्यकता है।

विलंबित अद्यतन

एक और अनुकूलन जो आप कर सकते हैं वह तुरंत सब कुछ अपडेट नहीं कर रहा है और एक ही समय में सब कुछ अपडेट नहीं कर रहा है।

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

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

मल्टीथ्रेडिंग के बारे में

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


यह बताता है कि SNES पर SimCity को फिर से जोड़ने / कनेक्ट करने में शक्ति के लिए कुछ समय क्यों लगता है, मुझे लगता है कि यह इसके अन्य क्षेत्र-व्यापी प्रभावों के साथ भी होता है।
लोजपाज

आपकी उपयोगी टिप्पणी के लिए धन्यवाद! मुझे यह भी लगता है कि स्मृति में अधिक जानकारी रखने से मेरे खेल में तेजी आ सकती है। मुझे टाइलपाइप को क्षेत्रों में विभाजित करने का विचार भी पसंद है लेकिन मुझे नहीं पता कि यह दृष्टिकोण मेरी प्रारंभिक समस्या से छुटकारा पाने के लिए पर्याप्त है या नहीं। देरी से अद्यतन करने से संबंधित एक प्रश्न। मान लें कि मेरे पास एक फ़ंक्शन है जो 60 से 45 तक मेरी एफपीएस बूँदें बनाता है। सीपीयू को संभालने में सक्षम राशि को संभालने के लिए गणना को विभाजित करने के लिए सबसे अच्छा तरीका क्या है?
येहकी

@ येशेकी इसके लिए कोई सार्वभौमिक रूप से लागू समाधान नहीं है, क्योंकि यह अत्यधिक स्थिति पर निर्भर है जो गणना में आप देरी कर सकते हैं, जो आप नहीं कर सकते हैं और गणना की समझदार इकाई है।
फिलिप

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

1
@ यायेकी जैसा कि मैंने कहा, कोई सार्वभौमिक रूप से लागू समाधान नहीं है। मैं आमतौर पर (उस क्रम में) क्या कोशिश करूंगा: 1. देखें कि क्या आप अधिक उपयुक्त एल्गोरिदम और / या डेटा संरचनाओं का उपयोग करके उस गणना को अनुकूलित कर सकते हैं। 2. देखें कि क्या आप इसे उप-कार्यों में विभाजित कर सकते हैं आप व्यक्तिगत रूप से देरी कर सकते हैं। 3. देखें कि क्या आप इसे एक अलग खतरे में डाल सकते हैं। 4. गेम-मैकेनिक से छुटकारा पाएं, जिसे उस गणना की आवश्यकता है और देखें कि क्या आप इसे कम कम्प्यूटेशनल रूप से महंगी के साथ बदल सकते हैं।
फिलीपींस

3

1. काम की नकल

तुम्हारी affectedBuildings निश्चित रूप से एक दूसरे के करीब हैं, इसलिए अलग-अलग राडली ओवरलैप होंगे। जिन इमारतों को अद्यतन करने की आवश्यकता है, उन्हें चिह्नित करें, फिर उन्हें अपडेट करें।

var toBeUpdated = new HashSet<Tiles>();
foreach(var affectedBuilding in affectedBuildings) {
    foreach(var radiusTile in affectedBuilding.RadiusTiles) {
         toBeUpdated.Add(radiusTile);

}
foreach (var tile in toBeUpdated)
{
    var buildingsInTile = TileMap.Buildings.Where(b => b.TileIndex == radiusTile.TileIndex);
    // Do stuff.
}

2. अनुपयुक्त डाटासट्रक्चर।

var buildingsInTile = TileMap.Buildings.Where(b => b.TileIndex == radiusTile.TileIndex);

स्पष्ट रूप से होना चाहिए

var buildingsInRadius = tile.Buildings;

जहां भवन IEnumerableनिरंतर पुनरावृत्ति समय (उदाहरण के लिए List<Building>) के साथ है


अच्छी बात! मुझे लगता है कि मैंने MoreLINQ का उपयोग करते हुए उस पर एक डिस्टिंक्ट () का उपयोग करने की कोशिश की, लेकिन मैं मानता हूं कि यह चेक डुप्लिकेट की तुलना में तेज़ हो सकता है।
येहकी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.