আরটিটিআই কত ব্যয়বহুল?


152

আমি বুঝতে পারি যে আরটিটিআই ব্যবহার করে কোনও সংস্থান রয়েছে তবে এটি কত বড়? আমি যেখানেই দেখেছি কেবল সেখানে বলেছে যে "আরটিটিআই ব্যয়বহুল," তবে এগুলির কেউই মেমরি, প্রসেসরের সময় বা গতি নিয়মিত কোনও মানদণ্ড বা পরিমাণগত ডেটা দেয় না।

সুতরাং, আরটিটিআই কত ব্যয়বহুল? আমি এটি এম্বেড থাকা সিস্টেমে ব্যবহার করতে পারি যেখানে আমার কাছে কেবল 4MB র্যাম রয়েছে, তাই প্রতিটি বিট গণনা করে।

সম্পাদনা: এস লটের উত্তর অনুসারে , আমি আসলে যা করছি তা যদি আমি অন্তর্ভুক্ত করি তবে ভাল হয়। আমি বিভিন্ন দৈর্ঘ্যের ডেটা পাস করার জন্য একটি ক্লাস ব্যবহার করছি এবং এটি বিভিন্ন ক্রিয়াকলাপ করতে পারে, সুতরাং কেবল ভার্চুয়াল ফাংশন ব্যবহার করে এটি করা কঠিন হবে। দেখে মনে হচ্ছে যে কয়েকটি dynamic_castগুলি ব্যবহার করে বিভিন্ন উত্স প্রাপ্ত শ্রেণিকে বিভিন্ন স্তরের মধ্য দিয়ে যাওয়ার পরেও তাদের সম্পূর্ণ ভিন্নভাবে কাজ করার অনুমতি দিয়ে সমস্যার সমাধান করতে পারে।

আমার বোঝাপড়া থেকে, dynamic_castআরটিটিআই ব্যবহার করে, তাই আমি ভাবছিলাম যে সীমিত সিস্টেমে এটি ব্যবহার করা কতটা সম্ভব।


1
আপনার সম্পাদনা থেকে অনুসরণ করা - খুব প্রায়ই যখন আমি নিজেকে বেশ কয়েকটি গতিশীল কাস্ট করতে দেখি তখন আমি বুঝতে পারি যে ভিজিটর প্যাটার্নটি ব্যবহার করে জিনিসগুলি আবার আটকানো হয়। এটা কি আপনার পক্ষে কাজ করতে পারে?
ফিলসকেয়ার

4
আমি এটি এইভাবে রাখব - আমি সবেমাত্র dynamic_castসি ++ এ ব্যবহার শুরু করেছি এবং এখন, 10 বারের মধ্যে 9 বার যখন আমি ডিবাগারটির সাথে প্রোগ্রামটি "ব্রেক" করি, তখন এটি অভ্যন্তরীণ গতিশীল-কাস্ট ফাংশনের ভিতরে ভেঙে যায়। এটা ধীর।
ব্যবহারকারী541686

3
আরটিটিআই = "রান টাইম ধরণের তথ্য", যাইহোক।
নওমেনন

উত্তর:


115

সংকলক নির্বিশেষে, আপনি যদি সামর্থ্য করতে পারেন তবে সর্বদা রানটাইম এ সংরক্ষণ করতে পারেন

if (typeid(a) == typeid(b)) {
  B* ba = static_cast<B*>(&a);
  etc;
}

পরিবর্তে

B* ba = dynamic_cast<B*>(&a);
if (ba) {
  etc;
}

প্রাক্তনটির একটি মাত্র তুলনা জড়িত std::type_info; পরবর্তীকালে অগত্যা একটি উত্তরাধিকার গাছের সাথে আরও তুলনাগুলি জড়িত।

অতীতে যা ... সবাই বলেছে, সংস্থান ব্যবহার নির্দিষ্টকরণের জন্য নির্দিষ্ট specific

আমি প্রত্যেকের মন্তব্যের সাথে একমত যে জমা দেওয়ার জন্য ডিজাইনের কারণে আরটিটিআই এড়ানো উচিত। যাইহোক, হয় (প্রধানত বুস্ট :: কোনো কারণ) RTTI ব্যবহার করতে ভাল কারণ। মনে রাখবেন, সাধারণ বাস্তবায়নের ক্ষেত্রে এর আসল সংস্থান ব্যবহারটি জানা দরকারী।

আমি সম্প্রতি জিসিসিতে আরটিটিআই নিয়ে একগুচ্ছ গবেষণা করেছি।

tl; dr: জিসিসিতে আরটিটিআই নগণ্য স্থান ব্যবহার করে এবং typeid(a) == typeid(b)খুব দ্রুত, অনেক প্ল্যাটফর্মে (লিনাক্স, বিএসডি এবং সম্ভবত এমবেডেড প্ল্যাটফর্মগুলি, তবে মিংডব্লিউ 32 নয়)। আপনি যদি জানেন যে আপনি সর্বদা একটি আশীর্বাদী প্ল্যাটফর্মে থাকবেন, আরটিটিআই নিখরচায় খুব কাছে।

কৌতুকপূর্ণ বিবরণ:

জিসিসি একটি নির্দিষ্ট "বিক্রেতা-নিরপেক্ষ" সি ++ এবিআই [1] ব্যবহার করতে পছন্দ করে এবং সর্বদা লিনাক্স এবং বিএসডি লক্ষ্য [2] এর জন্য এই এবিআই ব্যবহার করে। প্ল্যাটফর্মগুলির জন্য যা এই এবিআই সমর্থন করে এবং দুর্বল সংযোগ typeid(), এমনকি গতিশীল সংযোগ সীমানা জুড়ে, প্রতিটি ধরণের জন্য একটি সামঞ্জস্যপূর্ণ এবং অনন্য বস্তু প্রদান করে। আপনি পরীক্ষা করতে পারেন &typeid(a) == &typeid(b), বা কেবল নির্ভরযোগ্য যে পোর্টেবল পরীক্ষাটি typeid(a) == typeid(b)কেবলমাত্র একটি পয়েন্টারকে অভ্যন্তরীণ সাথে তুলনা করে।

জিসিসির পছন্দের এবিআইতে, একটি শ্রেণি ভিটিবেল সর্বদা প্রতি-টাইপ আরটিটিআই কাঠামোর কাছে একটি পয়েন্টার রাখে, যদিও এটি ব্যবহার নাও করা যেতে পারে। সুতরাং একটি typeid()কল নিজেই করা উচিত শুধুমাত্র অন্য কোন vtable লুকআপ (একটি ভার্চুয়াল সদস্য ফাংশন কলিং হিসাবে একই) যতটা খরচ, এবং RTTI সমর্থন করা উচিত নয় প্রতিটি বস্তুর জন্য কোনো অতিরিক্ত স্থান ব্যবহার করুন।

আমি যা করতে পারি তা থেকে, জিসিসি দ্বারা ব্যবহৃত আরটিটিআই কাঠামো (এগুলি সমস্ত সাবক্লাস std::type_info) কেবল নাম বাদে প্রতিটি ধরণের জন্য কয়েকটি বাইট রাখে। নামগুলি এমনকি আউটপুট কোডে উপস্থিত কিনা তা আমার কাছে পরিষ্কার নয় -fno-rtti। যেভাবেই হোক, সংকলিত বাইনারি আকারের পরিবর্তনটি রানটাইম মেমরির ব্যবহারের পরিবর্তনকে প্রতিবিম্বিত করা উচিত।

একটি দ্রুত পরীক্ষা শো যে (উবুন্টু 10.04 64 বিট উপর জিসিসি 4.4.3 ব্যবহার করে) -fno-rttiআসলে বৃদ্ধি কয়েকশ বাইট একটি সহজ পরীক্ষা প্রোগ্রামের বাইনারি আকার। এটি -gএবং এর সংমিশ্রণগুলিতে ধারাবাহিকভাবে ঘটে -O3। আমি নিশ্চিত না কেন আকার বাড়বে; একটি সম্ভাবনা হ'ল জিসিসির এসটিএল কোডটি আরটিটিআই ছাড়াই আলাদা আচরণ করে (যেহেতু ব্যতিক্রমগুলি কাজ করবে না)।

[1] ইটানিয়াম সি ++ এবিআই নামে পরিচিত, http://www.codesourcery.com/public/cxx-abi/abi.html এ নথিভুক্ত । নামগুলি মারাত্মকভাবে বিভ্রান্তিকর: নামটি মূল বিকাশের আর্কিটেকচারকে বোঝায়, যদিও আইবিআই স্পেসিফিকেশন আই 686 / x86_64 সহ প্রচুর আর্কিটেকচারে কাজ করে। জিসিসির অভ্যন্তরীণ উত্স এবং এসটিএল কোডের মন্তব্যগুলি ইটানিয়ামকে "নতুন" এবিআই হিসাবে উল্লেখ করেছে তারা আগের "পুরানো" ব্যবহারকারীর বিপরীতে। সবচেয়ে খারাপ, "নতুন" / ইটানিয়াম এবিআই এর মাধ্যমে উপলব্ধ সমস্ত সংস্করণ বোঝায় -fabi-version; "পুরানো" এবিআই এই সংস্করণটির পূর্বাভাস দিয়েছে। জিসিসি ৩.০ সংস্করণে ইটানিয়াম / সংস্করণযুক্ত / "নতুন" এবিআই গ্রহণ করেছে; "পুরানো" এবিআই ২.৯৯ সালে ব্যবহৃত হয়েছিল এবং এর আগে, যদি আমি তাদের চেঞ্জলগগুলি সঠিকভাবে পড়ছি।

[2] std::type_infoপ্ল্যাটফর্মের মাধ্যমে কোনও সংস্থান তালিকাভুক্ত অবজেক্টের স্থায়িত্ব খুঁজে পাইনি । কম্পাইলার আমি অ্যাক্সেস ছিল, আমি নিম্নলিখিত ব্যবহৃত: echo "#include <typeinfo>" | gcc -E -dM -x c++ -c - | grep GXX_MERGED_TYPEINFO_NAMES। এই ম্যাক্রো নিয়ন্ত্রণ আচরণকে operator==জন্য std::type_infoজিসিসি এর STL এ, জিসিসি 3.0 হিসাবে। আমি খুঁজে পেয়েছি যে মিংডব্লু 32-জিসিসি উইন্ডোজ সি ++ এবিআই মান্য করে, যেখানে std::type_infoডিএলএল জুড়ে কোনও ধরণের জন্য অবজেক্টগুলি অনন্য নয়; কভার অধীন typeid(a) == typeid(b)কল strcmp। আমি অনুমান করি যে AVR এর মতো একক-প্রোগ্রাম এম্বেড করা লক্ষ্যবস্তুগুলিতে, যেখানে লিঙ্ক দেওয়ার কোনও কোড নেই, std::type_infoবস্তু সর্বদা স্থিতিশীল থাকে।


6
আরটিটিআই ছাড়া ব্যতিক্রমগুলি কাজ করে। (আপনাকে একটি নিক্ষেপ করার অনুমতি দেওয়া হয়েছে intএবং এতে কোনও ভ্যাটেবল নেই :))
বিলি ওনিল

3
@ প্রতিলিপি: এবং তবুও, যখন আমি আমার সংকলকটিতে আরটিটিআই বন্ধ করি, তারা ঠিকঠাক কাজ করে। তোমাকে হতাশ করার জন্য দুঃখিত.
বিলি ওনিল

5
ব্যতিক্রম হ্যান্ডলিং মেকানিজমকে অবশ্যই কয়েকটি প্রাথমিক প্রয়োজনীয়তার সাথে কোনও ধরণের ফুলফিলিংয়ের সাথে কাজ করতে সক্ষম হতে হবে। আপনি আরটিটিআই ছাড়াই মডিউল সীমানা জুড়ে স্বেচ্ছাসেবীর ধরণের ব্যতিক্রম ছোঁড়া এবং ধরা কীভাবে পরিচালনা করবেন তা আপনি নির্দ্বিধায়। দয়া করে বিবেচনা করুন যে আপ এবং ডাউন-কাস্টিং প্রয়োজনীয়।
উত্সাহক

15
typid (a) == typid (b) B * ba = গতিশীল_কাস্ট <বি *> (& ক) এর মত নয়। উদ্ভূত শ্রেণীর গাছের এলোমেলো স্তর হিসাবে একাধিক উত্তরাধিকারযুক্ত বস্তুগুলিতে এটি ব্যবহার করে দেখুন এবং আপনি টাইপড () == টাইপিড () পাবেন না এটি একটি ধনাত্মক ফল দেয়। বাস্তবের জন্য উত্তরাধিকার গাছটি অনুসন্ধানের একমাত্র উপায় ডায়নামিক_কাস্ট। আরটিটিআই অক্ষম করে কেবল এটি ব্যবহারের মাধ্যমে সম্ভাব্য সঞ্চয় সম্পর্কে চিন্তাভাবনা বন্ধ করুন। আপনি যদি সক্ষমতা ছাড়িয়ে যান তবে আপনার কোড ব্লাটকে অনুকূলিত করুন। অভ্যন্তরীণ লুপগুলি বা অন্য কোনও পারফরম্যান্স সমালোচনামূলক কোডের ভিতরে ডায়নামিক_কাস্ট ব্যবহার না করার চেষ্টা করুন এবং আপনি ভাল থাকবেন।
মাইস্টিকোডার

3
@ এমকোডার এজন্য নিবন্ধটি স্পষ্টভাবে জানিয়েছে যে the latter necessarily involves traversing an inheritance tree plus comparisons। @ কোরিবি আপনি যখন পুরো উত্তরাধিকার গাছ থেকে castালাই সমর্থন করার প্রয়োজন হয় না তখন আপনি এটি করতে "সামর্থ" করতে পারেন। উদাহরণস্বরূপ, যদি আপনি কোনও সংগ্রহে X টাইপের সমস্ত আইটেম সন্ধান করতে চান তবে এটি এক্স থেকে উদ্ভূত নয় তবে আপনার কী ব্যবহার করা উচিত তা হ'ল এটি পূর্বের। আপনার যদি সমস্ত উদ্ভূত দৃষ্টান্তগুলি সন্ধান করতে হয় তবে আপনাকে পরে ব্যবহার করতে হবে।
এডিয়াকাপি

48

সম্ভবত এই পরিসংখ্যান সাহায্য করবে।

আমি এটি ব্যবহার করে একটি দ্রুত পরীক্ষা করছিলাম:

  • জিসিসি ক্লক () + এক্সকোডের প্রোফাইলার।
  • 100,000,000 লুপ পুনরাবৃত্তি।
  • 2 এক্স 2.66 গিগাহার্টজ ডুয়াল-কোর ইন্টেল শিওন।
  • প্রশ্নে ক্লাসটি একটি একক বেস শ্রেণীর থেকে নেওয়া।
  • টাইপড ()। নাম () "N12 ব্রেকফাস্টdelegate13FastDelegate1IivEE" প্রদান করে

5 টি মামলা পরীক্ষা করা হয়েছিল:

1) dynamic_cast< FireType* >( mDelegate )
2) typeid( *iDelegate ) == typeid( *mDelegate )
3) typeid( *iDelegate ).name() == typeid( *mDelegate ).name()
4) &typeid( *iDelegate ) == &typeid( *mDelegate )
5) { 
       fastdelegate::FastDelegateBase *iDelegate;
       iDelegate = new fastdelegate::FastDelegate1< t1 >;
       typeid( *iDelegate ) == typeid( *mDelegate )
   }

5 হ'ল আমার আসল কোড, যেহেতু আমার কাছে ইতিমধ্যে এটির অনুরূপ কিনা তা যাচাই করার আগে আমার সেই ধরণের একটি অবজেক্ট তৈরি করা দরকার ছিল।

অপ্টিমাইজেশন ছাড়াই

যার ফলস্বরূপ ছিল (আমার কয়েকটি রান গড় হয়েছে):

1)  1,840,000 Ticks (~2  Seconds) - dynamic_cast
2)    870,000 Ticks (~1  Second)  - typeid()
3)    890,000 Ticks (~1  Second)  - typeid().name()
4)    615,000 Ticks (~1  Second)  - &typeid()
5) 14,261,000 Ticks (~23 Seconds) - typeid() with extra variable allocations.

সুতরাং উপসংহারটি হবে:

  • অপ্টিমাইজেশন ছাড়াই সাধারণ castালাইয়ের ক্ষেত্রেগুলির typeid()চেয়ে দ্বিগুণের বেশি দ্রুত dyncamic_cast
  • একটি আধুনিক মেশিনে উভয়ের মধ্যে পার্থক্য প্রায় 1 ন্যানোসেকেন্ড (মিলিসেকেন্ডের মিলিয়নতম)।

অপ্টিমাইজেশন (-ও) সহ

1)  1,356,000 Ticks - dynamic_cast
2)     76,000 Ticks - typeid()
3)     76,000 Ticks - typeid().name()
4)     75,000 Ticks - &typeid()
5)     75,000 Ticks - typeid() with extra variable allocations.

সুতরাং উপসংহারটি হবে:

  • অপ্টিমাইজেশান সহ সহজ কাস্ট মামলার ক্ষেত্রে typeid()তুলনায় প্রায় x20 দ্রুত dyncamic_cast

তালিকা

এখানে চিত্র বর্ণনা লিখুন

কোড

মন্তব্যে অনুরোধ করা হিসাবে, কোডটি নীচে রয়েছে (কিছুটা অগোছালো, তবে কাজ করে)। 'FastDelegate.h' এখান থেকে উপলব্ধ ।

#include <iostream>
#include "FastDelegate.h"
#include "cycle.h"
#include "time.h"

// Undefine for typeid checks
#define CAST

class ZoomManager
{
public:
    template < class Observer, class t1 >
    void Subscribe( void *aObj, void (Observer::*func )( t1 a1 ) )
    {
        mDelegate = new fastdelegate::FastDelegate1< t1 >;
        
        std::cout << "Subscribe\n";
        Fire( true );
    }
    
    template< class t1 >
    void Fire( t1 a1 )
    {
        fastdelegate::FastDelegateBase *iDelegate;
        iDelegate = new fastdelegate::FastDelegate1< t1 >;
        
        int t = 0;
        ticks start = getticks();
        
        clock_t iStart, iEnd;
        
        iStart = clock();
        
        typedef fastdelegate::FastDelegate1< t1 > FireType;
        
        for ( int i = 0; i < 100000000; i++ ) {
        
#ifdef CAST
                if ( dynamic_cast< FireType* >( mDelegate ) )
#else
                // Change this line for comparisons .name() and & comparisons
                if ( typeid( *iDelegate ) == typeid( *mDelegate ) )
#endif
                {
                    t++;
                } else {
                    t--;
                }
        }
        
        iEnd = clock();
        printf("Clock ticks: %i,\n", iEnd - iStart );
        
        std::cout << typeid( *mDelegate ).name()<<"\n";
        
        ticks end = getticks();
        double e = elapsed(start, end);
        std::cout << "Elasped: " << e;
    }
    
    template< class t1, class t2 >
    void Fire( t1 a1, t2 a2 )
    {
        std::cout << "Fire\n";
    }
    
    fastdelegate::FastDelegateBase *mDelegate;
};

class Scaler
{
public:
    Scaler( ZoomManager *aZoomManager ) :
        mZoomManager( aZoomManager ) { }
    
    void Sub()
    {
        mZoomManager->Subscribe( this, &Scaler::OnSizeChanged );
    }
    
    void OnSizeChanged( int X  )
    {
        std::cout << "Yey!\n";        
    }
private:
    ZoomManager *mZoomManager;
};

int main(int argc, const char * argv[])
{
    ZoomManager *iZoomManager = new ZoomManager();
    
    Scaler iScaler( iZoomManager );
    iScaler.Sub();
        
    delete iZoomManager;

    return 0;
}

1
অবশ্যই, গতিশীল castালাই আরও সাধারণ - এটি আইটেমটি আরও উদ্ভূত করা হলে এটি কাজ করে। যেমন class a {}; class b : public a {}; class c : public b {};যখন লক্ষ্য একটি দৃষ্টান্ত হল cযখন বর্গ জন্য পরীক্ষা ইচ্ছার কাজ জরিমানা bদিয়ে dynamic_castকিন্তু নয় typeidসমাধান। এখনও যুক্তিসঙ্গত হলেও, +1
বিলি ওনিল

34
এই মানদণ্ডটি সম্পূর্ণরূপে অপ্টিমাইজেশনের সাথে জালিয়াতিযুক্ত : টাইপড চেকটি লুপ-ইনগ্রেন্ট এবং লুপের বাইরে চলে যায় moved এটি মোটেও আকর্ষণীয় নয়, এটি নং-এর একটি মৌলিক মানদণ্ড।
মনিকা পুনরায়

3
@ কুবা: তারপরে মাপদণ্ডটি জাল। এটি অপটিমাইজেশন বন্ধ করে দিয়ে বেঞ্চমার্ক করার কোনও কারণ নয়; এটি আরও ভাল বেঞ্চমার্ক লেখার কারণ।
বিলি ওনিল

3
তবুও আবার এটি ব্যর্থতা। "অপ্টিমাইজেশনের সাধারণ কাস্ট মামলার ক্ষেত্রে টাইপড () ডায়ান্সামিক_কাস্টের চেয়ে প্রায় x20 দ্রুত।" তারা একই জিনিস না। ডায়ামিক_কাস্ট ধীর হওয়ার কারণ রয়েছে।
মাইস্টিকোডার

1
@ কুবা ওবার: মোট +1 এটি খুব ক্লাসিক। এবং এটি ঘটেছে এমন চক্রের সংখ্যার চেহারা থেকে স্পষ্ট হওয়া উচিত।
v.oddou

38

এটি জিনিসগুলির স্কেলের উপর নির্ভর করে। বেশিরভাগ অংশের জন্য এটি কেবল কয়েকটি চেক এবং কয়েকটি পয়েন্টার ডিरेফারেন্স। বেশিরভাগ বাস্তবায়নে, ভার্চুয়াল ফাংশনযুক্ত প্রতিটি বস্তুর শীর্ষে, একটি ভিটিবেলের কাছে একটি পয়েন্টার থাকে যা সেই শ্রেণীর ভার্চুয়াল ফাংশনের সমস্ত বাস্তবায়নের জন্য পয়েন্টারগুলির একটি তালিকা রাখে। আমি অনুমান করব যে বেশিরভাগ বাস্তবায়নগুলি ক্লাসের জন্য টাইপ_ইনফো কাঠামোর জন্য অন্য পয়েন্টার সংরক্ষণ করতে এটি ব্যবহার করবে।

সিউডো-সি ++ এর উদাহরণস্বরূপ:

struct Base
{
    virtual ~Base() {}
};

struct Derived
{
    virtual ~Derived() {}
};


int main()
{
    Base *d = new Derived();
    const char *name = typeid(*d).name(); // C++ way

    // faked up way (this won't actually work, but gives an idea of what might be happening in some implementations).
    const vtable *vt = reinterpret_cast<vtable *>(d);
    type_info *ti = vt->typeinfo;
    const char *name = ProcessRawName(ti->name);       
}

সাধারণভাবে আরটিটিআই-এর বিরুদ্ধে আসল যুক্তি হ'ল প্রতিবারই আপনি যখন কোনও নতুন উত্পন্ন ক্লাস যুক্ত করেন তখন কোডটি সর্বত্র সংশোধন করার অভাবমুক্ততা। বিবরণগুলি যে কোনও জায়গায় স্যুইচ করার পরিবর্তে সেগুলিকে ভার্চুয়াল ফাংশনে রূপান্তর করুন। এটি ক্লাসের মধ্যে পৃথক সমস্ত কোডকে নিজেরাই ক্লাসে স্থানান্তরিত করে, যাতে একটি নতুন ডেরাইভেশনটি পুরোপুরি কার্যকরী শ্রেণিতে পরিণত হওয়ার জন্য সমস্ত ভার্চুয়াল ফাংশনকে ওভাররাইড করে। আপনি যখনই কখনও কোনও ক্লাসের ধরণটি পরীক্ষা করে কিছু আলাদা করে পরীক্ষা করে দেখেছেন এমন কোনও বড় কোড বেসের সন্ধান করতে হয়েছিল, আপনি দ্রুত প্রোগ্রামিংয়ের সেই স্টাইল থেকে দূরে থাকতে শিখবেন।

যদি আপনার সংকলক আপনাকে আরটিটিআই সম্পূর্ণরূপে বন্ধ করতে দেয় তবে চূড়ান্ত ফলাফলের কোড আকার সাশ্রয় যদিও এ জাতীয় একটি ছোট র‌্যাম স্থানের সাথে তাত্পর্যপূর্ণ হতে পারে। সংকলকটির ভার্চুয়াল ফাংশন সহ প্রতিটি একক শ্রেণীর জন্য একটি টাইপ_ইনফো কাঠামো তৈরি করা দরকার। আপনি আরটিটিআই বন্ধ করে দিলে এই সমস্ত কাঠামোকে এক্সিকিউটেবল ছবিতে অন্তর্ভুক্ত করার দরকার নেই।


4
আরটিটিআই ব্যবহার করা কেন খারাপ ডিজাইনের সিদ্ধান্ত হিসাবে বিবেচিত হয় তা বোঝানোর জন্য +1, এটি আমার কাছে আগে খুব স্পষ্ট ছিল না।
আগুয়াজলেস

6
এই উত্তরটি সি ++ এর পাওয়ার সম্পর্কে নিম্ন স্তরের উপলব্ধি। "সাধারণভাবে" এবং "বেশিরভাগ বাস্তবায়নে" উদারপন্থী ব্যবহারের অর্থ আপনি ভাষাগুলির বৈশিষ্ট্যগুলি কীভাবে ভালভাবে ব্যবহার করবেন সে সম্পর্কে আপনি ভাবছেন না। ভার্চুয়াল ফাংশন এবং আরটিটিআই-র পুনরায় প্রয়োগকরণ এর উত্তর নয়। আরটিটিআই এর উত্তর। কখনও কখনও আপনি কেবল জানতে চান যে কোনও বস্তু একটি নির্দিষ্ট ধরণের কিনা। সে কারণেই ওখানে! সুতরাং আপনি কিছু টাইপ_ইনফো স্ট্রোকের জন্য কয়েক কেবি র‌্যাম হারিয়ে ফেলেন।
জিৎ

16

ঠিক আছে, প্রোফাইলার কখনও মিথ্যা বলে না।

যেহেতু আমার 18-2 টি ধরণের একটি স্থিতিশীল শ্রেণিবিন্যাস রয়েছে যা খুব বেশি পরিবর্তন হচ্ছে না, তাই আমি ভাবছিলাম যে যদি কেবল একজন সাধারণ এনাম'ড সদস্য ব্যবহার করে কৌশলটি সম্পাদন করা যায় এবং আরটিটিআইয়ের "উচ্চ" ব্যয়টি এড়ানো যায়। আরটিটিআই আসলে যে ifবিবৃতি দিয়েছিল তার চেয়ে বেশি ব্যয়বহুল হলে আমার সন্দেহ হয়েছিল। ছেলে ওহে ছেলে, তাই না।

এটা পরিনত হয় যে RTTI হয় দামী আরো অনেক কিছু একটি সমতুল্য চেয়ে ব্যয়বহুল ifবিবৃতি অথবা একটি সহজ switchসি ++ একটি আদিম পরিবর্তনশীল উপর। সুতরাং S.Lott এর উত্তর সম্পূর্ণরূপে সঠিক নয়, সেখানে হয় RTTI জন্য অতিরিক্ত খরচ নেই, এবং এটা না শুধু কারণে একটি থাকার ifবিবৃতি মিশ্রণ মধ্যে। এটি আরটিটিআইয়ের জন্য খুব ব্যয়বহুল due

এই পরীক্ষাটি অ্যাপল এলএলভিএম 5.0 সংকলকটিতে স্টক অপ্টিমাইজেশন চালু (ডিফল্ট রিলিজ মোড সেটিংস) দিয়ে করা হয়েছিল।

সুতরাং, আমার 2 টিরও কম ফাংশন রয়েছে, যার প্রতিটিই 1) আরটিটিআই বা 2) কোনও সাধারণ স্যুইচ এর মাধ্যমে কোনও সামগ্রীর কংক্রিটের ধরণের চিত্র বের করে। এটি 50,000,000 বার করে। আরও অ্যাডো না করে, আমি আপনার কাছে 50,000,000 রানের আপেক্ষিক রানটাইম উপস্থাপন করছি।

এখানে চিত্র বর্ণনা লিখুন

এটা ঠিক, রানটাইমের 94% সময় dynamicCastsনিয়েছে । যদিও ব্লক শুধুমাত্র গ্রহণ 3.3% regularSwitch

দীর্ঘ গল্পের সংক্ষিপ্তসার: যদি আপনি enumনীচের মতো হুক-ইন করার জন্য শক্তির ঝাঁকুনি দিতে পারেন তবে আমি সম্ভবত এটির পরামর্শ দিই, যদি আপনার আরটিটিআই করার দরকার হয় এবং অভিনয়টি সবচেয়ে গুরুত্বপূর্ণ। এটি কেবল একবার সদস্য নির্ধারণ করে নেয় ( সমস্ত নির্মাতার মাধ্যমে এটি নিশ্চিত করে নিন ), এবং এটি পরে কখনও না লিখে নিশ্চিত হন।

এটি বলেছে যে এটি করা আপনার ওওপি অনুশীলনগুলিকে বিশৃঙ্খলা করবে না .. এটি কেবল তখনই ব্যবহৃত হয় যখন প্রকারের তথ্য কেবল সহজলভ্য না হয় এবং আপনি আরটিটিআই ব্যবহার করে নিজেকে কোণঠাসা করে দেখেন।

#include <stdio.h>
#include <vector>
using namespace std;

enum AnimalClassTypeTag
{
  TypeAnimal=1,
  TypeCat=1<<2,TypeBigCat=1<<3,TypeDog=1<<4
} ;

struct Animal
{
  int typeTag ;// really AnimalClassTypeTag, but it will complain at the |= if
               // at the |='s if not int
  Animal() {
    typeTag=TypeAnimal; // start just base Animal.
    // subclass ctors will |= in other types
  }
  virtual ~Animal(){}//make it polymorphic too
} ;

struct Cat : public Animal
{
  Cat(){
    typeTag|=TypeCat; //bitwise OR in the type
  }
} ;

struct BigCat : public Cat
{
  BigCat(){
    typeTag|=TypeBigCat;
  }
} ;

struct Dog : public Animal
{
  Dog(){
    typeTag|=TypeDog;
  }
} ;

typedef unsigned long long ULONGLONG;

void dynamicCasts(vector<Animal*> &zoo, ULONGLONG tests)
{
  ULONGLONG animals=0,cats=0,bigcats=0,dogs=0;
  for( ULONGLONG i = 0 ; i < tests ; i++ )
  {
    for( Animal* an : zoo )
    {
      if( dynamic_cast<Dog*>( an ) )
        dogs++;
      else if( dynamic_cast<BigCat*>( an ) )
        bigcats++;
      else if( dynamic_cast<Cat*>( an ) )
        cats++;
      else //if( dynamic_cast<Animal*>( an ) )
        animals++;
    }
  }

  printf( "%lld animals, %lld cats, %lld bigcats, %lld dogs\n", animals,cats,bigcats,dogs ) ;

}

//*NOTE: I changed from switch to if/else if chain
void regularSwitch(vector<Animal*> &zoo, ULONGLONG tests)
{
  ULONGLONG animals=0,cats=0,bigcats=0,dogs=0;
  for( ULONGLONG i = 0 ; i < tests ; i++ )
  {
    for( Animal* an : zoo )
    {
      if( an->typeTag & TypeDog )
        dogs++;
      else if( an->typeTag & TypeBigCat )
        bigcats++;
      else if( an->typeTag & TypeCat )
        cats++;
      else
        animals++;
    }
  }
  printf( "%lld animals, %lld cats, %lld bigcats, %lld dogs\n", animals,cats,bigcats,dogs ) ;  

}

int main(int argc, const char * argv[])
{
  vector<Animal*> zoo ;

  zoo.push_back( new Animal ) ;
  zoo.push_back( new Cat ) ;
  zoo.push_back( new BigCat ) ;
  zoo.push_back( new Dog ) ;

  ULONGLONG tests=50000000;

  dynamicCasts( zoo, tests ) ;
  regularSwitch( zoo, tests ) ;
}

13

মানক উপায়:

cout << (typeid(Base) == typeid(Derived)) << endl;

স্ট্যান্ডার্ড আরটিটিআই ব্যয়বহুল কারণ এটি অন্তর্নিহিত স্ট্রিং তুলনা করার উপর নির্ভর করে এবং এভাবে আরটিটিআই-এর গতি শ্রেণীর নামের দৈর্ঘ্যের উপর নির্ভর করে পরিবর্তিত হতে পারে।

স্ট্রিংয়ের তুলনা কেন ব্যবহৃত হয় তা হল এটি লাইব্রেরি / ডিএলএল সীমানা জুড়ে ধারাবাহিকভাবে কাজ করা work আপনি যদি আপনার অ্যাপ্লিকেশনটি স্থিতিশীলভাবে তৈরি করেন এবং / অথবা আপনি নির্দিষ্ট সংকলক ব্যবহার করেন তবে আপনি সম্ভবত ব্যবহার করতে পারেন:

cout << (typeid(Base).name() == typeid(Derived).name()) << endl;

যা কাজের গ্যারান্টিযুক্ত নয় (কখনই কোনও মিথ্যা ধনাত্মক দেয় না, তবে মিথ্যা sণাত্মক দিতে পারে) তবে এটি 15 গুণ দ্রুত হতে পারে। এটি নির্দিষ্ট উপায়ে কাজ করার জন্য টাইপিড () প্রয়োগের উপর নির্ভর করে এবং আপনি যা করছেন তা অভ্যন্তরীণ চর পয়েন্টারের সাথে তুলনা করে। এটি কখনও কখনও সমতুল্য:

cout << (&typeid(Base) == &typeid(Derived)) << endl;

তবে আপনি নিরাপদে একটি হাইব্রিড ব্যবহার করতে পারেন যা ধরণের সাথে মেলে খুব দ্রুত হবে এবং তুলনাহীন প্রকারের জন্য সবচেয়ে খারাপ পরিস্থিতি হবে:

cout << ( typeid(Base).name() == typeid(Derived).name() || 
          typeid(Base) == typeid(Derived) ) << endl;

আপনার এটিকে অপ্টিমাইজ করতে হবে কিনা তা বোঝার জন্য আপনাকে প্যাকেটটি প্রসেস করতে যে সময় লাগে তার তুলনায় আপনি নতুন প্যাকেট পেতে আপনার কতটা সময় ব্যয় করেছেন তা দেখতে হবে। বেশিরভাগ ক্ষেত্রে একটি স্ট্রিং তুলনা সম্ভবত বড় ওভারহেড হবে না। (আপনার শ্রেণি বা নেমস্পেসের উপর নির্ভর করে :: শ্রেণীর নামের দৈর্ঘ্য)

এটি অনুকূলিতকরণের সবচেয়ে নিরাপদতম উপায়টি হল আপনার নিজস্ব টাইপডকে আপনার বেস শ্রেণীর অংশ হিসাবে একটি int (বা এনাম টাইপ: ইন্ট) হিসাবে প্রয়োগ করা এবং শ্রেণীর ধরণ নির্ধারণ করতে এটি ব্যবহার করুন এবং তারপরে কেবল স্ট্যাটিক_কাস্ট <> বা পুনরায় ব্যাখ্যা_কাস্ট <ব্যবহার করুন >

আমার জন্য পার্থক্যটি আনমোমাইটিভ এমএস ভিএস 2005 সি ++ এসপি 1 এ প্রায় 15 বার।


2
"স্ট্যান্ডার্ড আরটিটিআই ব্যয়বহুল কারণ এটি অন্তর্নিহিত স্ট্রিং তুলনা করার উপর নির্ভর করে" - না, এ সম্পর্কে "স্ট্যান্ডার্ড" কিছুই নেই; আপনার বাস্তবায়নের typeid::operatorকাজটি এটি ঠিক । একটি সমর্থিত প্ল্যাটফর্মের জিসিসি, উদাহরণস্বরূপ, ইতিমধ্যে char *আমাদের এর জন্য জোর করে ছাড়াই এর তুলনা ব্যবহার করে - gcc.gnu.org/onlinesocs/gcc-4.6.3/libstdc++/api/… । অবশ্যই, আপনার উপায় এমএসভিসিটিকে আপনার প্ল্যাটফর্মের ডিফল্টের চেয়ে অনেক বেশি ভাল আচরণ করে, তাই কুডোস, এবং আমি জানি না যে "কিছু লক্ষ্য" যা পয়েন্টারগুলি স্থানীয়ভাবে ব্যবহার করে ... তবে আমার বক্তব্যটি এমএসভিসির আচরণ কোনওভাবেই নয় "স্ট্যান্ডার্ড"।
আন্ডারস্কোর_ডি

7

একটি সাধারণ চেকের জন্য, আরটিটিআই পয়েন্টার তুলনা হিসাবে সস্তা হতে পারে। উত্তরাধিকার যাচাইয়ের জন্য, strcmpউত্তরাধিকার গাছের প্রতিটি প্রকারের জন্য এটি যতটা ব্যয়বহুল হতে পারে যদি আপনি dynamic_castউপরের দিক থেকে নীচে নীচে এক বাস্তবায়িত হয়ে থাকেন।

আপনি dynamic_castটাইপড (...) == & টাইপড (টাইপ) এর মাধ্যমে স্পষ্টভাবে পরীক্ষা না করে ওভারহেড হ্রাস করতে পারেন the যদিও এটি অগত্যা .dlls বা অন্যান্য গতিযুক্ত লোড কোডের জন্য কাজ করে না, স্থিতিযুক্ত লিঙ্কযুক্ত জিনিসগুলির জন্য এটি বেশ দ্রুত হতে পারে।

যদিও এই মুহুর্তে এটি একটি স্যুইচ স্টেটমেন্ট ব্যবহার করার মতো, তাই আপনি সেখানে যান।


1
আপনার কি আরআরসিএমপি সংস্করণটির জন্য কোনও রেফারেন্স রয়েছে? টাইপ চেকের জন্য স্ট্রিম্প ব্যবহার করতে এটি অত্যন্ত অদক্ষ এবং ভুল বলে মনে হচ্ছে।
জেয়ার্ডপাড়

দুর্বল বাস্তবায়নের ক্ষেত্রে প্রতি টাইপে একাধিক টাইপ_ইনফো অবজেক্ট থাকতে পারে, এটি বুল টাইপ_ইনফো :: অপারেটর == (কনস্টেন্ট টাইপ_ইনফো এবং এক্স) কনস্টকে "! Strcmp (নাম (), x.name ())" হিসাবে প্রয়োগ করতে পারে
গ্রেগ রজার্স

3
এমএসভিসির জন্য ডায়নামিক_কাস্ট বা টাইপিড () অপারেটর == এর অপ্রয়োজনীয় পদক্ষেপে পদক্ষেপ এবং আপনি সেখানে একটি স্ট্রিম্পকে আঘাত করবেন। আমি সেখানে ধরে নিয়েছি এমন এক ভয়াবহ মামলার জন্য যেখানে আপনি অন্য কোনও .dll তে সংকলিত কোনও প্রকারের সাথে তুলনা করছেন। এবং এটি ম্যাঙ্গেলড নামটি ব্যবহার করে, তাই কমপক্ষে একই সংকলকটি দেওয়া হলেও এটি সঠিক।
এমএসএন

1
আপনি "টাইপিড (...) == টাইপড (টাইপ)" করার কথা এবং ঠিকানার সাথে তুলনা না করার কথা
জোহানেস স্কাউব - lit 20

1
আমার বক্তব্যটি হ'ল আপনি খুব শীঘ্রই টাইপড (...) == & টাইপড (ব্লাহ) করতে পারেন এবং নিরাপদ থাকবেন। এটি স্ট্যাকের ভিত্তিতে টাইপড (...) তৈরি হতে পারে তবে এটি কার্যকরভাবে কিছু করতে পারে না, তবে যদি তাদের ঠিকানাগুলি সমান হয়, তবে তাদের প্রকারগুলি সমান।
এমএসএন 17

6

জিনিসগুলি পরিমাপ করা সর্বদা সেরা। নিম্নলিখিত কোডে, জি ++ এর অধীনে, হ্যান্ড কোডেড ধরণের সনাক্তকরণের ব্যবহারটি আরটিটিআইয়ের চেয়ে প্রায় তিনগুণ দ্রুত বলে মনে হচ্ছে। আমি নিশ্চিত যে চরগুলির পরিবর্তে স্ট্রিং ব্যবহার করে আরও বাস্তবসম্মত হাতের কোডড প্রয়োগকরণ ধীর হবে এবং সময়কে একসাথে এনে দেবে ..

#include <iostream>
using namespace std;

struct Base {
    virtual ~Base() {}
    virtual char Type() const = 0;
};

struct A : public Base {
    char Type() const {
        return 'A';
    }
};

struct B : public Base {;
    char Type() const {
        return 'B';
    }
};

int main() {
    Base * bp = new A;
    int n = 0;
    for ( int i = 0; i < 10000000; i++ ) {
#ifdef RTTI
        if ( A * a = dynamic_cast <A*> ( bp ) ) {
            n++;
        }
#else
        if ( bp->Type() == 'A' ) {
            A * a = static_cast <A*>(bp);
            n++;
        }
#endif
    }
    cout << n << endl;
}

1
এটি ডায়নামিক_কাস্ট দিয়ে নয়, টাইপড দিয়ে করার চেষ্টা করুন। এটি পারফরম্যান্সকে গতিতে পারে।
জোহানেস স্কাউব -

1
কিন্তু dynamic_cast ব্যবহার, আরো বাস্তবসম্মত অন্তত আমার কোড দিকে তাকিয়ে

2
এটি একটি ভিন্ন জিনিস করে: এটি বিপি এ থেকে প্রাপ্ত কোনও প্রকারের দিকে নির্দেশ করে কিনা তাও পরীক্ষা করে আপনার == 'এ' এটি 'এ' তে ঠিক নির্দেশ করে কিনা তা পরীক্ষা করে। আমি আরও মনে করি পরীক্ষাটি কিছুটা অন্যায্য: কম্পাইলার সহজেই দেখতে পারে বিপি এ এর ​​চেয়ে আলাদা কিছুতে ইঙ্গিত করতে পারে না তবে আমি মনে করি এটি এখানে অনুকূলিত হয় না।
জোহানেস স্কাউব -

যাইহোক, আমি আপনার কোড পরীক্ষা করেছি। এবং এটি আমাকে আরটিটিআইয়ের জন্য "0.016s" এবং ভার্চুয়াল ফাংশন কলগুলির জন্য "0.044s" দেয়। (-O2 ব্যবহার করে)
জোহানেস স্কাউব - লিটব

যদিও এটি টাইপড ব্যবহার করতে এটি পরিবর্তন করা এখানে কোনও পার্থক্য করে না (এখনও 0.016s)
জোহানেস স্কাউব - litb

4

কিছুক্ষণ আগে আমি এমটিএসভিসি এবং জিসিসির নির্দিষ্ট ক্ষেত্রে একটি 3 জিওজেড পাওয়ারপিসির জন্য আরটিটিআইয়ের সময়ের ব্যয় পরিমাপ করেছি। পরীক্ষাগুলিতে আমি দৌড়েছি (একটি গভীর শ্রেণীর গাছের সাথে মোটামুটি বড় সি ++ অ্যাপ্লিকেশন), dynamic_cast<>এটির আঘাত বা মিস হয়েছে কিনা তার উপর নির্ভর করে প্রতিটি খরচ 0.8μ থেকে 2 between এর মধ্যে হয়।


2

সুতরাং, আরটিটিআই কত ব্যয়বহুল?

এটি সম্পূর্ণরূপে আপনি যে সংকলকটি ব্যবহার করছেন তার উপর নির্ভর করে। আমি বুঝতে পারি যে কিছু স্ট্রিং তুলনা ব্যবহার করে, এবং অন্যরা আসল অ্যালগরিদম ব্যবহার করে।

আপনার একমাত্র আশা একটি নমুনা প্রোগ্রাম লিখুন এবং আপনার সংকলকটি কী করবে তা দেখুন (বা কমপক্ষে এক মিলিয়ন dynamic_castsবা এক মিলিয়ন typeidগুলি কার্যকর করতে কত সময় লাগে তা নির্ধারণ করুন )।


1

আরটিটিআই সস্তা হতে পারে এবং অগত্যা কোনও আরআরসিএমপি প্রয়োজন হয় না। সংকলকটি বিপরীত ক্রমে প্রকৃত শ্রেণিবিন্যাস সম্পাদন করতে পরীক্ষা সীমাবদ্ধ করে। সুতরাং আপনার যদি ক্লাস সি রয়েছে যা ক্লাস বি এর একটি শিশু যা ক্লাস এ এর ​​একটি শিশু, ডায়ামিক_কাস্ট একটি এন্ট্রি থেকে পি সিআর পিটিআর কেবল একটি পয়েন্টার তুলনা করে এবং দুটি নয় (বিটিডাব্লু, কেবল ভিটিপিআর টেবিল পয়েন্টার) তুলনা)। পরীক্ষাটি "যদি (vptr_of_obj == vptr_of_C) রিটার্ন (সি *) আপত্তি মত"

আর একটি উদাহরণ, যদি আমরা এ * থেকে বি * তে ডায়নামিক_কাস্ট করার চেষ্টা করি। সেক্ষেত্রে সংকলক উভয় কেস পরীক্ষা করবে (আপত্তি একজন সি, এবং বি হিসাবে আপত্তি বিযুক্ত)। এটি একটি একক পরীক্ষায়ও বেশিরভাগ ক্ষেত্রে সরল করা যেতে পারে (বেশিরভাগ সময়), কারণ ভার্চুয়াল ফাংশন টেবিলটি সমষ্টি হিসাবে তৈরি হয়, তাই পরীক্ষাটি "যদি (অফসেট_ফ (vptr_of_obj, বি) == vptr_of_B)" দিয়ে শুরু হয়

অফসেট_ও = রিটার্ন সাইজের (vptr_table)> = সাইজফ (vptr_of_B)? vptr_of_ নতুন_মোথডস_ ইন_বি: 0

এর স্মৃতি বিন্যাস

vptr_of_C = [ vptr_of_A | vptr_of_new_methods_in_B | vptr_of_new_methods_in_C ]

সংকলনের সময় সংকলক এটি কীভাবে অপ্টিমাইজ করতে পারে?

সংকলনের সময়, সংকলকটি বস্তুর বর্তমান শ্রেণিবিন্যাস জানেন, তাই এটি বিভিন্ন ধরণের শ্রেণিবিন্যাস ডায়নামিক_কাস্টিং সংকলন করতে অস্বীকার করে। তারপরে এটি কেবল শ্রেণিবিন্যাসের গভীরতা পরিচালনা করতে হবে এবং এ জাতীয় গভীরতার সাথে মেলে পরীক্ষার বিপরীত পরিমাণ যুক্ত করতে হবে।

উদাহরণস্বরূপ, এটি সংকলন করে না:

void * something = [...]; 
// Compile time error: Can't convert from something to MyClass, no hierarchy relation
MyClass * c = dynamic_cast<MyClass*>(something);  

-5

আরটিটিআই "ব্যয়বহুল" হতে পারে কারণ আপনি প্রতিবার আরটিটিআই তুলনা করলে আপনি আইফ-স্টেটমেন্ট যুক্ত করেছেন। গভীরভাবে নেস্টেড পুনরাবৃত্তিতে, এটি দামি হতে পারে। এমন কোনও ক্ষেত্রে যা কখনই লুপে কার্যকর হয় না এটি মূলত বিনামূল্যে।

পছন্দটি হ'ল আইফ-স্টেটমেন্টটি সরিয়ে সঠিক পলিমারফিক ডিজাইন ব্যবহার করা। গভীরভাবে নেস্ট করা লুপগুলিতে, এটি কার্য সম্পাদনের জন্য প্রয়োজনীয়। অন্যথায়, এটি খুব বেশি কিছু আসে না।

আরটিটিআইও ব্যয়বহুল কারণ এটি সাবক্লাস হায়ারার্কিকে অস্পষ্ট করতে পারে (এমনকি যদি এটি একও থাকে)। "অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং" থেকে "অবজেক্ট ওরিয়েন্টেড" সরিয়ে ফেলতে এর পার্শ্ব-প্রতিক্রিয়া থাকতে পারে।


2
অগত্যা নয় - আমি ডায়নামিক_কাস্টের মাধ্যমে এটি অপ্রত্যক্ষভাবে ব্যবহার করতে যাচ্ছিলাম, এবং স্থানক্রমকে স্থানে রেখেছি, কারণ আমাকে ডাউন কাস্ট করতে হবে কারণ প্রতিটি উপ-টাইপের আলাদা আলাদা (ভেরিয়েবল আকারের) ডেটা থাকতে হবে যা আলাদাভাবে প্রয়োগ করতে হবে, তাই ডায়নামিক_কাস্ট।
ক্রিশ্চিয়ান রোমো

1
@ ক্রিস্টিয়ান রোমো: দয়া করে এই প্রশ্নটি নতুন তথ্য দিয়ে আপডেট করুন। ডায়নামিক_কাস্ট হ'ল (মাঝে মাঝে) সি ++ তে প্রয়োজনীয় অশুভ। আরটিটিআই পারফরম্যান্স সম্পর্কে জিজ্ঞাসা করা যখন আপনি এটি করতে বাধ্য হন তখন খুব বেশি অর্থ হয় না।
এস .লট

@ এসলট: আপডেট হয়েছে। বিভ্রান্তির জন্য দুঃখিত।
ক্রিশ্চিয়ান রোমো

1
আমি এখনই এই সম্পর্কে একটি পরীক্ষা করেছি - এটি প্রমাণ করে যে ifআপনি যখন এইভাবে রানটাইম টাইপ তথ্য পরীক্ষা করেন তখন আরটিটিআই আপনি যে বক্তব্যটি প্রবর্তন করেন তার চেয়ে উল্লেখযোগ্যভাবে ব্যয়বহুল ।
bobobobo
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.