लेकिन क्या यह OOP प्रदर्शन के आधार पर सॉफ़्टवेयर के लिए नुकसान का कारण हो सकता है यानी प्रोग्राम कितनी तेज़ी से निष्पादित होता है?
अक्सर हाँ !!! परंतु...
दूसरे शब्दों में, कई अलग-अलग वस्तुओं के बीच कई संदर्भ हो सकते हैं, या कई वर्गों से कई तरीकों का उपयोग करने के परिणामस्वरूप "भारी" कार्यान्वयन हो सकता है?
जरुरी नहीं। यह भाषा / संकलक पर निर्भर करता है। उदाहरण के लिए, एक अनुकूलन C ++ कंपाइलर, बशर्ते कि आप वर्चुअल फ़ंक्शंस का उपयोग न करें, अक्सर आपकी वस्तु ओवरहेड को शून्य पर स्क्वैश कर देगी। आप int
वहां पर एक रैपर लिखने या सादे पुराने पॉइंटर पर एक स्कोप्ड स्मार्ट पॉइंटर लिखने जैसी चीजें कर सकते हैं जो सीधे इन सादे पुराने डेटा प्रकारों का उपयोग करने के साथ ही तेजी से प्रदर्शन करता है।
जावा जैसी अन्य भाषाओं में, किसी वस्तु पर एक ओवरहेड होता है (अक्सर कई मामलों में काफी छोटा होता है, लेकिन वास्तव में नन्हा वस्तुओं के साथ कुछ दुर्लभ मामलों में खगोलीय)। उदाहरण के लिए, की Integer
तुलना में काफी कम कुशल है int
(64-बिट पर 4 के विपरीत 16 बाइट्स लेता है)। फिर भी यह सिर्फ अपव्यय या उस तरह का कुछ भी नहीं है। बदले में, जावा हर एक उपयोगकर्ता-परिभाषित प्रकार पर समान रूप से प्रतिबिंब के साथ-साथ किसी भी फ़ंक्शन को ओवरराइड करने की क्षमता को चिह्नित नहीं करता है final
।
फिर भी चलो सबसे अच्छा मामला लेते हैं: अनुकूलन C ++ कंपाइलर जो शून्य ओवरहेड के लिए ऑब्जेक्ट इंटरफेस को अनुकूलित कर सकता है । फिर भी, OOP अक्सर प्रदर्शन को नीचा दिखाता है और इसे चरम पर पहुंचने से रोकता है। यह एक पूर्ण विरोधाभास की तरह लग सकता है: यह कैसे हो सकता है? समस्या में निहित है:
इंटरफ़ेस डिजाइन और एनकैप्सुलेशन
समस्या यह है कि जब कोई कंपाइलर किसी वस्तु की संरचना को शून्य ओवरहेड तक स्क्वैश कर सकता है (जो कि C ++ कंपाइलर को अनुकूलित करने के लिए कम से कम बहुत बार सच है), ठीक-ठीक ऑब्जेक्ट्स के एन्कैप्सुलेशन और इंटरफ़ेस डिज़ाइन (और निर्भरताएं) जमा होती हैं, जो अक्सर रोकेंगी ऑब्जेक्ट के लिए सबसे इष्टतम डेटा अभ्यावेदन जो जनता द्वारा एकत्र किए जाने का इरादा रखते हैं (जो अक्सर प्रदर्शन-महत्वपूर्ण सॉफ़्टवेयर के लिए होता है)।
इस उदाहरण को लें:
class Particle
{
public:
...
private:
double birth; // 8 bytes
float x; // 4 bytes
float y; // 4 bytes
float z; // 4 bytes
/*padding*/ // 4 bytes of padding
};
Particle particles[1000000]; // 1mil particles (~24 megs)
मान लें कि हमारी मेमोरी एक्सेस पैटर्न केवल इन कणों के माध्यम से क्रमिक रूप से लूप करना है और स्क्रीन के कोनों को बंद करके और फिर परिणाम प्रदान करते हुए उन्हें प्रत्येक फ्रेम के चारों ओर बार-बार स्थानांतरित करना है।
पहले से ही हम एक शानदार 4 बाइट पैडिंग ओवरहेड को देख सकते हैं birth
जब सदस्य को संयुक्ताक्षर रूप से एकत्र किया जाता है, तो सदस्य को ठीक से संरेखित करने के लिए आवश्यक है । पहले से ही ~ 16.7% मेमोरी संरेखण के लिए उपयोग किए गए मृत स्थान के साथ बर्बाद हो जाती है।
यह हमें बहुत बुरा लग सकता है क्योंकि हमारे पास इन दिनों घी की गीगाबाइट्स हैं। फिर भी हमारे पास आज भी सबसे ज्यादा पुरानी मशीनें अक्सर केवल 8 मेगाबाइट की होती हैं, जब यह CPU कैश (L3) के सबसे धीमे और सबसे बड़े क्षेत्र में आता है । जितना कम हम वहां फिट हो सकते हैं, उतना ही हम बार-बार DRAM की पहुंच के मामले में इसके लिए भुगतान करते हैं, और धीमी चीजें मिलती हैं। अचानक, 16.7% मेमोरी बर्बाद करना अब एक तुच्छ सौदे जैसा नहीं लगता।
हम क्षेत्र संरेखण पर किसी भी प्रभाव के बिना इस ओवरहेड को आसानी से समाप्त कर सकते हैं:
class Particle
{
public:
...
private:
float x; // 4 bytes
float y; // 4 bytes
float z; // 4 bytes
};
Particle particles[1000000]; // 1mil particles (~12 megs)
double particle_birth[1000000]; // 1mil particle births (~8 bytes)
अब हमने मेमोरी को 24 मीजी से घटाकर 20 मैग कर दिया है। एक अनुक्रमिक एक्सेस पैटर्न के साथ, मशीन अब इस डेटा का काफी तेजी से उपभोग करेगी।
लेकिन आइए इस birth
क्षेत्र को थोड़ा और करीब से देखें। मान लीजिए कि यह प्रारंभिक समय रिकॉर्ड करता है जब एक कण पैदा होता है (बनाया जाता है)। कल्पना करें कि क्षेत्र केवल तब ही एक्सेस किया जाता है जब एक कण पहली बार बनाया जाता है, और हर 10 सेकंड में यह देखने के लिए कि क्या कण को मरना चाहिए और स्क्रीन पर यादृच्छिक स्थान पर पुनर्जन्म हो सकता है। उस मामले में, birth
एक ठंडा क्षेत्र है। यह हमारे प्रदर्शन-महत्वपूर्ण छोरों में पहुँचा नहीं है।
नतीजतन, वास्तविक प्रदर्शन-महत्वपूर्ण डेटा 20 मेगाबाइट नहीं है, लेकिन वास्तव में 12-मेगाबाइट सन्निहित ब्लॉक है। वास्तविक गर्म स्मृति जिसे हम अक्सर एक्सेस कर रहे हैं, उसका आकार आधा हो गया है ! हमारे मूल, 24-मेगाबाइट समाधान पर महत्वपूर्ण गति-अप की अपेक्षा करें (इसे मापने की आवश्यकता नहीं है - पहले से ही इस तरह का सामान एक हजार बार किया गया है, लेकिन संदेह होने पर स्वतंत्र महसूस करें)।
फिर भी ध्यान दें कि हमने यहां क्या किया। हमने इस कण ऑब्जेक्ट के एनकैप्सुलेशन को पूरी तरह से तोड़ दिया। इसका राज्य अब एक Particle
प्रकार के निजी क्षेत्रों और एक अलग, समानांतर सरणी के बीच विभाजित है । और यह है कि जहां रास्ते में दानेदार वस्तु उन्मुख डिजाइन हो जाता है।
जब हम किसी एकल कण, एक एकल पिक्सेल, एक एकल पिक्सेल, यहां तक कि एक 4-घटक वेक्टर, संभवतः एक भी "प्राणी" एक खेल में ऑब्जेक्ट की तरह, बहुत ही दानेदार वस्तु के इंटरफेस डिजाइन तक सीमित नहीं है, तो हम इष्टतम डेटा प्रतिनिधित्व को व्यक्त नहीं कर सकते , आदि चीता की गति व्यर्थ हो जाएगी यदि यह एक किशोर द्वीप पर खड़ा है जो 2 वर्ग मीटर है, और यह बहुत ही दानेदार वस्तु-उन्मुख डिजाइन है जो अक्सर प्रदर्शन के संदर्भ में करता है। यह उप-इष्टतम प्रकृति के डेटा प्रतिनिधित्व को सीमित करता है।
इसे और आगे ले जाने के लिए, मान लीजिए कि जब हम केवल कणों को घुमा रहे हैं, हम वास्तव में उनके x / y / z क्षेत्रों को तीन अलग-अलग छोरों में एक्सेस कर सकते हैं। उस मामले में, हम एवीएक्स रजिस्टरों के साथ सोए शैली के सिमड इंट्रिंसिक्स से लाभ उठा सकते हैं जो समानांतर में 8 एसपीएफपी संचालन को वेक्टर कर सकते हैं। लेकिन ऐसा करने के लिए, हमें अब इस प्रतिनिधित्व का उपयोग करना चाहिए:
float particle_x[1000000]; // 1mil particle X positions (~4 megs)
float particle_y[1000000]; // 1mil particle Y positions (~4 megs)
float particle_z[1000000]; // 1mil particle Z positions (~4 megs)
double particle_birth[1000000]; // 1mil particle births (~8 bytes)
अब हम कण अनुकरण के साथ उड़ान भर रहे हैं, लेकिन हमारे कण डिजाइन का क्या हुआ। इसे पूरी तरह से ध्वस्त कर दिया गया है, और हम अब 4 समानांतर सरणियों को देख रहे हैं और जो भी उन्हें एकत्र करने के लिए कोई वस्तु नहीं है। हमारा ऑब्जेक्ट ओरिएंटेड Particle
डिज़ाइन साओनारा चला गया है।
यह मेरे लिए कई बार प्रदर्शन-महत्वपूर्ण क्षेत्रों में काम करने के लिए हुआ, जहां उपयोगकर्ता केवल शुद्धता के साथ गति की मांग करते हैं, एक चीज जो वे अधिक मांग करते हैं। इन छोटे नन्हे ऑब्जेक्ट-उन्मुख डिज़ाइनों को ध्वस्त करना पड़ा, और कैस्केडिंग टूटने के लिए अक्सर आवश्यक होता था कि हम तेजी से डिजाइन की दिशा में धीमी गति से बढ़ने वाली रणनीति का उपयोग करें।
समाधान
उपरोक्त परिदृश्य केवल दानेदार वस्तु-उन्मुख डिजाइनों के साथ एक समस्या प्रस्तुत करता है । उन मामलों में, हम अक्सर एसओए प्रतिनिधि, गर्म / ठंडे क्षेत्र विभाजन के परिणामस्वरूप अधिक कुशल अभ्यावेदन व्यक्त करने के लिए संरचना को ध्वस्त करने के लिए समाप्त होते हैं, क्रमिक पहुंच पैटर्न के लिए पैडिंग में कमी (पैडिंग कभी-कभी यादृच्छिक-अभिगम के साथ प्रदर्शन के लिए सहायक होती है एओएस मामलों में पैटर्न, लेकिन लगभग हमेशा अनुक्रमिक एक्सेस पैटर्न के लिए एक बाधा), आदि।
फिर भी हम उस अंतिम निरूपण को ले सकते हैं जिस पर हम बसे थे और अभी भी एक वस्तु-उन्मुख इंटरफ़ेस को मॉडल करते हैं:
// Represents a collection of particles.
class ParticleSystem
{
public:
...
private:
double particle_birth[1000000]; // 1mil particle births (~8 bytes)
float particle_x[1000000]; // 1mil particle X positions (~4 megs)
float particle_y[1000000]; // 1mil particle Y positions (~4 megs)
float particle_z[1000000]; // 1mil particle Z positions (~4 megs)
};
अब हम अच्छे हैं। हम अपनी पसंद की सभी वस्तु-उन्मुख वस्तुएं प्राप्त कर सकते हैं। चीता के पास पूरे देश में जितनी तेजी से दौड़ सकता है, उतनी तेजी से दौड़ने के लिए है। हमारा इंटरफ़ेस अब हमें अड़चन के कोने में नहीं फँसाता है।
ParticleSystem
संभावित रूप से अमूर्त भी हो सकता है और आभासी कार्यों का उपयोग कर सकता है। यह अब बहुत बुरा है, हम प्रति-कण स्तर के बजाय कणों के स्तर के संग्रह में ओवरहेड के लिए भुगतान कर रहे हैं । ओवरहेड 1 / 1,000,000 वां है कि यह अन्यथा क्या होगा यदि हम व्यक्तिगत कण स्तर पर वस्तुओं को मॉडलिंग कर रहे थे।
तो यह सही प्रदर्शन-महत्वपूर्ण क्षेत्रों में समाधान है जो एक भारी भार को संभालता है, और सभी प्रकार की प्रोग्रामिंग भाषाओं के लिए (यह तकनीक सी, सी ++, पायथन, जावा, जावास्क्रिप्ट, लुआ, स्विफ्ट, आदि का लाभ उठाती है)। और इसे आसानी से "समय से पहले अनुकूलन" के रूप में लेबल नहीं किया जा सकता है, क्योंकि यह इंटरफ़ेस डिज़ाइन और वास्तुकला से संबंधित है । हम एक कोडबेड मॉडलिंग नहीं कर सकते एक एकल कण के रूप में एक ग्राहक के बोटलोड के साथ एक वस्तु के लिएParticle's
सार्वजनिक इंटरफ़ेस और फिर बाद में हमारे विचार बदल जाते हैं। मैंने ऐसा किया है, जब विरासत कोडबेस को अनुकूलित करने के लिए बहुत कुछ कहा जा रहा है, और यह कोड के हजारों लाइनों को फिर से लिखने में महीनों का समय ले सकता है। यह आदर्श रूप से प्रभावित करता है कि हम चीजों को कैसे डिजाइन करते हैं बशर्ते कि हम एक भारी भार का अनुमान लगा सकें।
मैं इस उत्तर को किसी न किसी रूप में कई प्रदर्शन प्रश्नों में, और विशेष रूप से ऑब्जेक्ट-ओरिएंटेड डिज़ाइन से संबंधित प्रतिध्वनित करता रहता हूं। ऑब्जेक्ट-ओरिएंटेड डिज़ाइन अभी भी उच्चतम-माँग प्रदर्शन आवश्यकताओं के अनुकूल हो सकता है, लेकिन हमें इसके बारे में सोचने के तरीके को थोड़ा बदलना होगा। हमें उस चीते को कुछ कमरा देना है जितना जल्दी हो सके, और यह अक्सर असंभव है अगर हम छोटी वस्तुओं को डिज़ाइन करते हैं जो किसी भी राज्य को मुश्किल से स्टोर करते हैं।