एकता के साथ गेम बनाते समय मुझे GC के लिए कैसे खाता होना चाहिए?


15

* जहाँ तक मुझे पता है, आईओएस के लिए यूनिटी 3 डी मोनो रनटाइम पर आधारित है और मोनो में केवल जेनरल मार्क और स्वीप जीसी है।

यह जीसी सिस्टम जीसी समय से बच नहीं सकता है जो गेम सिस्टम को रोकता है। इंस्टेंस पूलिंग इसे कम कर सकती है लेकिन पूरी तरह से नहीं, क्योंकि हम CLR की बेस क्लास लाइब्रेरी में होने वाली तात्कालिकता को नियंत्रित नहीं कर सकते। उन छिपे हुए छोटे और लगातार उदाहरणों से अंततः निर्धारक जीसी समय बढ़ेगा। जीसी को समय-समय पर पूरा करने के लिए मजबूर करना प्रदर्शन को बहुत कम कर देगा (क्या मोनो जीसी को पूरा कर सकता है, वास्तव में?)

तो, मैं भारी प्रदर्शन के बिना Unity3D का उपयोग करते समय इस GC समय से कैसे बच सकता हूं?


3
नवीनतम यूनिटी प्रो प्रोफाइलर में कुछ फ़ंक्शन कॉल में कितना कचरा उत्पन्न होता है। आप इस उपकरण का उपयोग अन्य स्थानों को देखने में मदद करने के लिए कर सकते हैं जो कचरा पैदा कर रहे हैं जो तुरंत अन्यथा स्पष्ट नहीं हो सकते हैं।
तेतराड

जवाबों:


18

सौभाग्य से, जैसा कि आपने बताया, COMPACT मोनो एक जेनेशनल GC का उपयोग करता है (माइक्रोसॉफ्ट वालों के विपरीत, जैसे WinMo / WinPhone / XBox, जो सिर्फ एक फ्लैट सूची बनाए रखते हैं)।

यदि आपका गेम सरल है, तो GC को इसे ठीक से संभालना चाहिए, लेकिन यहां कुछ संकेत दिए गए हैं जिन्हें आप देखना चाहते हैं।

समय से पहले अनुकूलन

पहले यह सुनिश्चित कर लें कि इसे ठीक करने की कोशिश करने से पहले यह वास्तव में आपके लिए एक समस्या है।

पूलिंग महंगा संदर्भ प्रकार

आपको उन संदर्भ प्रकारों को पूल करना चाहिए जिन्हें आप या तो अक्सर बनाते हैं, या जिनकी गहरी संरचनाएं हैं। प्रत्येक का एक उदाहरण होगा:

  • अक्सर बनाया गया: बुलेट-नर्क गेमBullet में एक ऑब्जेक्ट ।
  • गहरी संरचना: एक एआई कार्यान्वयन के लिए निर्णय का पेड़।

आपको Stackअपने पूल के रूप में (अधिकांश कार्यान्वयनों के विपरीत Queue) का उपयोग करना चाहिए । इसका कारण यह है क्योंकि Stackयदि आप किसी वस्तु को पूल में वापस करते हैं और कुछ और इसे तुरंत पकड़ लेता है; यह एक सक्रिय पृष्ठ में होने की अधिक संभावना है - या सीपीयू कैश में भी अगर आप भाग्यशाली हैं। यह सिर्फ इतना छोटा सा तेज है। इसके अलावा हमेशा अपने पूलों को आकार-सीमित करें (यदि आपकी सीमा पार हो गई है तो 'चेकइन की अवहेलना करें')।

उन्हें साफ़ करने के लिए नई सूची बनाने से बचें

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

जहाँ संभव हो, स्ट्रक्चर एरेज़ (या सूचियाँ) का उपयोग करें

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

रॉयटी के बाद। कुछ व्यवहार्य और रचनात्मक प्रतिक्रिया प्रदान की मुझे लगता है कि मुझे इस पर और विस्तार करने की आवश्यकता है। आपको केवल इस तकनीक का उपयोग करना चाहिए जब आप भारी मात्रा में संस्थाओं (हजारों से हजारों) के साथ काम कर रहे हों। इसके अलावा यह चाहिए एक struct जो हो नहीं होना चाहिए किसी भी संदर्भ प्रकार क्षेत्र होते हैं और एक स्पष्ट टाइप सरणी में जीना चाहिए। उनकी प्रतिक्रिया के विपरीत, हम इसे एक सरणी में रख रहे हैं, जो एक वर्ग में एक क्षेत्र की संभावना है - जिसका अर्थ है कि यह ढेर तक जा रहा है (हम एक ढेर आवंटन से बचने की कोशिश नहीं कर रहे हैं - केवल जीसी काम से बचना)। हम वास्तव में इस तथ्य की परवाह करते हैं कि यह बहुत सारे मूल्यों के साथ स्मृति का एक सन्निहित हिस्सा है जिसे जीसी केवल एक O(1)ऑपरेशन के बजाय एक O(n)ऑपरेशन में देख सकता है।

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

GC.Collect ()

यह निश्चित रूप से पैर में खुद को गोली मारने का सबसे अच्छा तरीका है (देखें: "प्रदर्शन विचार") एक जनरेशनल जीसी के साथ। आप केवल यह बुलाना चाहिए जब आप एक बनाया है चरम कचरा की राशि - और एक उदाहरण है, जहां कि एक मुद्दा हो सकता है सिर्फ तुम्हारे जाने के बाद एक स्तर के लिए सामग्री भरी हुई है - और फिर भी आपको चाहिए शायद ही कलेक्ट पहली पीढ़ी ( ) तीसरी पीढ़ी में वस्तुओं को बढ़ावा देने से रोकने के लिए।GC.Collect(0);

IDisposable और फील्ड नलिंग

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

निम्नलिखित उदाहरण लें:

MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// G1 Collection
MyNestObject (G2) -> MyFurtherNestedObject (G2)
// G2 Collection
MyFurtherNestedObject (G3)

यदि MyFurtherNestedObjectबहु-मेगाबाइट ऑब्जेक्ट सम्‍मिलित है, तो आप इसकी गारंटी दे सकते हैं कि जीसी काफी लंबे समय तक इसे नहीं देखेगा - क्‍योंकि आपने अनजाने में इसे G3 में प्रचारित कर दिया है। इस उदाहरण के साथ विरोध करें:

MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// Dispose
MyObject (G1)
MyNestedObject (G1)
MyFurtherNestedObject (G1)
// G1 Collection

डिस्पोज़र पैटर्न आपको वस्तुओं को उनके निजी क्षेत्रों को साफ़ करने के लिए कहने के लिए एक पूर्वानुमानित तरीका सेट करने में मदद करता है। उदाहरण के लिए:

public class MyClass : IDisposable
{
    private MyNestedType _nested;

    // A finalizer is only needed IF YOU CONTROL UNMANAGED RESOURCES
    // ~MyClass() { }

    public void Dispose()
    {
       _nested = null;
    }
}

1
WTF आप के बारे में हैं? विंडोज पर .NET फ्रेमवर्क बिलकुल जेनेरिक है। उनकी कक्षा में GetGeneration विधियां भी हैं।
डेडएमजी

2
.NET पर विंडोज एक जेनरेशनल गारबेज कलेक्टर का उपयोग करता है, हालांकि WP7 पर उपयोग किए जाने वाले .NET कॉम्पैक्ट फ्रेमवर्क और Xbox360 में एक अधिक सीमित GC है, पूरी तरह से यह संभवतः एक फ्लैट सूची से अधिक है यह एक पूर्ण पीढ़ी नहीं है।
रॉय टी।

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

1
@RoyT। ऊपर टिप्पणी देखें। मैंने संकेत दिया कि एक सरणी में डेटा की एक बड़ी लाइनर राशि के लिए संरचनाएं अच्छी हैं - एक कण प्रणाली की तरह। यदि आप किसी सरणी में GC संरचना के बारे में चिंतित हैं तो जाने का रास्ता है; कणों की तरह डेटा के लिए।
जोनाथन डिकिंसन

10
@DeadMG भी WTF के लिए बेहद अनसॉल्व्ड है - इस पर विचार करते हुए कि आपने वास्तव में उत्तर को सही से नहीं पढ़ा है। कृपया अपने स्वभाव को शांत करें और आम तौर पर इस तरह के व्यवहार वाले समुदाय में घृणित टिप्पणी लिखने से पहले उत्तर को फिर से पढ़ें।
जोनाथन डिकिंसन

7

बेस लाइब्रेरी के अंदर बहुत कम डायनेमिक इंस्टेंटेशन स्वचालित रूप से होता है, जब तक कि आपको ऐसी किसी चीज की आवश्यकता न हो। स्मृति आबंटन और डीक्लोकेशन का अधिकांश हिस्सा आपके ही कोड से आता है और आप इसे नियंत्रित कर सकते हैं।

जैसा कि मैं इसे समझता हूं, आप इसे पूरी तरह से नहीं टाल सकते हैं - आप यह कर सकते हैं कि आप यह सुनिश्चित कर सकें कि आप जहां संभव हो, वहां रीसायकल और पूल ऑब्जेक्ट्स का उपयोग करें, कक्षाओं के बजाय स्ट्रक्चर्स का उपयोग करें, यूनिटीगीयू प्रणाली से बचें, और आमतौर पर समय के दौरान गतिशील मेमोरी में नई वस्तुओं को बनाने से बचें। जब प्रदर्शन मायने रखता है।

आप निश्चित समय पर कचरा संग्रहण के लिए बाध्य कर सकते हैं, जो मदद कर सकता है या नहीं कर सकता: कुछ सुझाव यहां देखें: http://unity3d.com/support/documentation/Manual/iphone-Optimizing-Scripts.html


2
आपको वास्तव में जीसी को मजबूर करने के निहितार्थ को स्पष्ट करना चाहिए। यह GC हेयुरिस्टिक्स और लेआउट के साथ कहर खेलता है और गलत तरीके से उपयोग किए जाने पर बड़ी मेमोरी समस्याओं को जन्म दे सकता है: XNA / MVP / स्टाफ समुदाय आमतौर पर पोस्ट-लोडिंग-कंटेंट को इकट्ठा करने के लिए केवल उचित समय मानता है। आपको वास्तव में इसका उल्लेख करने से पूरी तरह बचना चाहिए यदि आप केवल एक वाक्य को मामले में समर्पित करते हैं। GC.Collect()= मुफ्त मेमोरी अब लेकिन बाद में बहुत अधिक समस्याएँ।
जोनाथन डिकिंसन

नहीं, आपको जीसी को मजबूर करने के निहितार्थों को स्पष्ट करना चाहिए क्योंकि आप मेरे बारे में अधिक जानते हैं। :) हालांकि, ध्यान रखें कि मोनो जीसी Microsoft जीसी के समान नहीं है, और जोखिम समान नहीं हो सकते हैं।
काइलोटन

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