সাধারণভাবে, ব্রাঞ্চিং এড়াতে ভার্চুয়াল ফাংশনগুলি ব্যবহার করা কি মূল্য?


21

ব্রাঞ্চ মিস ভার্চুয়াল ফাংশনগুলির দামের সমান হ'ল নির্দেশাবলীর মোটামুটি সমপরিমাণ বলে মনে হয়:

  • নির্দেশনা বনাম তথ্য ক্যাশে মিস
  • অপ্টিমাইজেশন বাধা

আপনি যদি কিছু তাকান:

if (x==1) {
   p->do1();
}
else if (x==2) {
   p->do2();
}
else if (x==3) {
   p->do3();
}
...

আপনার একটি সদস্য ফাংশন অ্যারে থাকতে পারে, বা যদি অনেকগুলি একই শ্রেণিবিন্যাসের উপর নির্ভর করে বা আরও জটিল শ্রেণিবিন্যাস বিদ্যমান থাকে তবে ভার্চুয়াল ফাংশনগুলি ব্যবহার করুন:

p->do()

তবে, সাধারণভাবে, ভার্চুয়াল ফাংশনগুলি ভার্চুয়াল ফাংশনগুলি কতটা ব্যয়বহুল তা সাধারণ করার জন্য পর্যাপ্ত প্ল্যাটফর্মগুলিতে পরীক্ষা করা শক্ত, তাই আমি ভাবছিলাম যে কারও কাছে থাম্বের মোটামুটি নিয়ম রয়েছে কিনা (সুন্দর যদি এটি 4 ifটির মতো সাধারণ ছিল তবে ব্রেকআপপয়েন্ট)

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


12
আচ্ছা, আপনার পারফরম্যান্সের প্রয়োজনীয়তাগুলি কী কী? আপনার কি হার্ড নম্বর রয়েছে যা আপনি আঘাত করতে পারেন, বা আপনি অকাল অপটিমাইজেশনে নিযুক্ত আছেন? ব্রাঞ্চিং এবং ভার্চুয়াল পদ্ধতি উভয় জিনিসগুলির বৃহত্ স্কিম (যেমন খারাপ অ্যালগরিদমের সাথে তুলনা করে, I / O, বা গাদা বরাদ্দ) অত্যন্ত সস্তা।
আমন

4
ভবিষ্যতের পরিবর্তনের পথে আরও পঠনযোগ্য / নমনীয় / পাওয়ার সম্ভাবনা যা-ই করুন এবং আপনার এটি কাজ করার পরে প্রোফাইলিং করুন এবং দেখুন এটি আসলে গুরুত্বপূর্ণ কিনা। সাধারণত তা হয় না।
Ixrec

1
প্রশ্ন: "তবে, সাধারণভাবে ভার্চুয়াল ফাংশনগুলি কতটা ব্যয়বহুল ..." উত্তর: পরোক্ষ শাখা (উইকিপিডিয়া)
রওয়ং

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

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

উত্তর:


21

আমি ইতিমধ্যে এই চমত্কার উত্তরের মধ্যে এখানে ঝাঁপিয়ে পড়তে চেয়েছিলাম এবং স্বীকার করতে পেরেছি যে আমি পলিমারফিক কোড পরিবর্তনের বিরোধী প্যাটার্নে switchesবা if/elseপরিমাপযুক্ত লাভের সাথে শাখার পিছনে পিছনে কাজ করার কুৎসিত পদ্ধতি গ্রহণ করেছি taken তবে আমি এই পাইকারিটি করিনি, কেবলমাত্র সবচেয়ে জটিল পথে। এটি এত কালো এবং সাদা হতে হবে না।

অস্বীকৃতি হিসাবে, আমি রেয়েট্রাকিংয়ের মতো অঞ্চলে কাজ করি যেখানে যথার্থতা অর্জন করা এতটা কঠিন নয় (এবং প্রায়শই ঝাপসা এবং প্রায় হয়) তবে গতি প্রায়শই সর্বাধিক প্রতিযোগিতামূলক গুণাবলীর সন্ধান করা হয়। রেন্ডার বার হ্রাস হ'ল প্রায়শই সাধারণ ব্যবহারকারীর অনুরোধগুলির মধ্যে একটি হ'ল আমাদের ক্রমাগত আমাদের মাথা চুলকানো এবং কীভাবে সবচেয়ে গুরুত্বপূর্ণ পরিমাপযোগ্য পথগুলির জন্য এটি অর্জন করা যায় তা নির্ধারণ করা।

শর্তগুলির বহুমুখী রিফ্যাক্টরিং

প্রথমত, শর্তসাপেক্ষ শাখা ( switchবা if/elseবিবৃতিতে একগুচ্ছ ) এর চেয়ে বহনযোগ্যতার দিক থেকে পলিমারফিজম কেন ভাল can এখানে প্রধান সুবিধা হ'ল এক্সটেনসিবিলিটি

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

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

অপ্টিমাইজেশন বাধা

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

এই দুটি কৌশলগুলির মধ্যে যেটির চেয়ে বেশি পার্থক্য রয়েছে তা হ'ল অপটিমাইজারের আগাম পরিমাণ পরিমাণ তথ্যের পরিমাণ। পরিচিত একটি ফাংশন কল অনেক বেশি তথ্য সরবরাহ করে, একটি পরোক্ষ ফাংশন কল যা সংকলন সময়ে অজানা ফাংশনটিকে কল করে একটি অপ্টিমাইজেশন বাধা দেয়।

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

অপ্রত্যক্ষ ফাংশন কলের ক্ষেত্রে যেমন আমরা মিশ্রণে সংকলন-কালীন অজানা পরিচয় করিয়ে দেওয়া শুরু করি তখন সেগুলির কিছুটি হ্রাস পায় এবং শর্তসাপেক্ষ শাখাগুলি সম্ভবত একটি প্রান্ত প্রস্তাব করতে পারে।

মেমরি অপটিমাইজেশন

একটি ভিডিও গেমের উদাহরণ নিন যা বারবার শক্ত লুপে প্রাণীর ক্রম প্রক্রিয়াজাত করে। এই জাতীয় ক্ষেত্রে, আমাদের কিছু পলিমারফিক পাত্রে থাকতে পারে:

vector<Creature*> creatures;

দ্রষ্টব্য: সরলতার জন্য আমি unique_ptrএখানে এড়ানো হয়েছে।

... Creatureপলিমারফিক বেসের ধরণটি কোথায় । এক্ষেত্রে পলিমারফিক পাত্রে একটি অসুবিধা হ'ল তারা প্রায়শই প্রতিটি উপ-টাইপের জন্য আলাদাভাবে / স্বতন্ত্রভাবে মেমরি বরাদ্দ করতে চান (উদা: operator newপ্রতিটি পৃথক প্রাণীর জন্য ডিফল্ট নিক্ষেপ ব্যবহার করে)।

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

ডেটা স্ট্রাকচার এবং লুপগুলির আংশিক ডেভर्चুয়ালাইজেশন

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

তবুও পরবর্তী পদক্ষেপটি (এবং সর্বদা আমাদের পরিবর্তনগুলি ব্যাক করার ইচ্ছুকতার সাথে এটি যদি কিছু না দেয় তবে) ম্যানুয়াল ডেভ্যাচুয়ালাইজেশন হতে পারে।

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

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

vector<Human> humans;               // common case
vector<Creature*> other_creatures;  // additional rare-case creatures

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

vector<Human> humans;               // common case
vector<Creature*> other_creatures;  // additional rare-case creatures
vector<Creature*> creatures;        // contains humans and other creatures

... যদি আমরা এটি সামর্থ্য করি তবে কম সমালোচনামূলক পথগুলি যেমন রয়েছে তেমন থাকতে পারে এবং সমস্ত প্রাণী প্রকারের বিমূর্তভাবে প্রক্রিয়াজাত করে। সমালোচনামূলক পাথগুলি humansএকটি লুপ এবং other_creaturesদ্বিতীয় লুপে প্রক্রিয়া করতে পারে ।

আমরা প্রয়োজন হিসাবে এই কৌশলটি প্রসারিত করতে পারি এবং সম্ভাব্যভাবে কিছু উপায়ে এইভাবে পিষতে পারি, তবুও এটি প্রক্রিয়াতে আমরা রক্ষণাবেক্ষণযোগ্যতা কতটা হ্রাস করছি তা লক্ষ করার মতো worth এখানে ফাংশন টেমপ্লেটগুলি ব্যবহার করে ম্যানুয়ালি যুক্তিকে নকল না করেই মানুষ এবং প্রাণী উভয়ের জন্য কোড তৈরি করতে সহায়তা করতে পারে।

ক্লাসগুলির আংশিক ডেভर्चুয়ালাইজেশন

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

switch (obj->type())
{
   case id_common_type:
       static_cast<CommonType*>(obj)->non_virtual_do_something();
       break;
   ...
   default:
       obj->virtual_do_something();
       break;
}

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

হোলসেল ডিভ্যাচুয়ালাইজেশন

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

তাত্ত্বিকভাবে, এখানে তাত্ক্ষণিক সুবিধাগুলি এই অপ্টিমাইজেশন বাধাগুলি সম্পূর্ণরূপে নির্মূল করার পাশাপাশি ভার্চুয়াল পয়েন্টার (প্রাক্তন: 256 অনন্য প্রকার বা তার কম রয়েছে) এই ধারণার প্রতিশ্রুতি দিতে পারলে একক বাইট সম্ভবত কোনও ধরণের সনাক্তকরণের সম্ভাব্য ছোট উপায় হতে পারে The ।

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

আমি সাধারণত এটি খুব দক্ষতা-সমালোচনামূলক মানসিকতার সাথে সুপারিশ করব না যতক্ষণ না এটি বজায় রাখা সহজ হয় না। "বজায় রাখা সহজ" দুটি প্রভাবশালী কারণের উপর জড়িয়ে পড়বে:

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

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

ভার্চুয়াল ফাংশন বনাম ফাংশন পয়েন্টারগুলি

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

এটি এখানে পাল্টা স্বজ্ঞাত যেহেতু আমরা মেমরি শ্রেণিবদ্ধের গতিশীলতার দিকে মনোযোগ না দিয়ে নির্দেশাবলীর ক্ষেত্রে মূল্য পরিমাপ করতে অভ্যস্ত যা যার ফলে আরও অনেক তাত্পর্যপূর্ণ প্রভাব পড়ে।

যদি আমরা class20 ভার্চুয়াল ফাংশনগুলির সাথে একটি তুলনা করি structযা একটি 20 ফাংশন পয়েন্টার সঞ্চয় করে এবং উভয়ই একাধিকবার classইনস্ট্যান্ট করা হয় তবে এই ক্ষেত্রে প্রতিটি ক্ষেত্রে মেমরির ওভারহেড 64-বিট মেশিনে ভার্চুয়াল পয়েন্টারটির জন্য 8 বাইট রয়েছে, যখন স্মৃতি এর ওভারহেড struct160 বাইট

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

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

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

class Functionoid
{
public:
    virtual ~Functionoid() {}
    virtual void operator()() = 0;
};

... যেখানে ক্লাসটি একটি একক পরিমাপের ওভারডেয়েবল ফাংশন (যেখানে আমরা ভার্চুয়াল ডেস্ট্রাক্টর গণনা করি তারা দু'জন) সংরক্ষণ করে। এই ক্ষেত্রে, এটি অবশ্যই এটিকে গুরুত্বপূর্ণ রূপান্তরিত করতে গুরুত্বপূর্ণ পথে সহায়তা করতে পারে:

void (*func_ptr)(void* instance_data);

... আদর্শভাবে / থেকে বিপজ্জনক কাস্টগুলি লুকানোর জন্য একটি টাইপ-নিরাপদ ইন্টারফেসের পিছনে void*

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

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

উপসংহার

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


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

@ রবার্টবারন: আপনি যেমন বলেছিলেন তেমন ভার্চুয়াল ফাংশনগুলি কখনই বাস্তবায়িত হতে দেখিনি (= শ্রেণিবদ্ধের মধ্য দিয়ে একটি চেইন সন্ধান সহ)। সাধারণত সংকলকগুলি সমস্ত সঠিক ফাংশন পয়েন্টার সহ প্রতিটি কংক্রিট ধরণের জন্য কেবল একটি "সমতল" ভিটিবেল তৈরি করে এবং রানটাইমের সময় কলটি একটি একক স্ট্রেইট টেবিল দেখার সাথে সমাধান করা হয়; গভীর উত্তরাধিকারের শ্রেণিবিন্যাসের জন্য কোনও জরিমানা দেওয়া হয় না।
মাত্তেও ইটালিয়া

মাত্তিও, এটি বহু বছর আগে একটি প্রযুক্তিগত নেতৃত্ব আমাকে দিয়েছিল explanation মঞ্জুর, এটি সি ++ এর জন্য ছিল, তাই তিনি একাধিক উত্তরাধিকারের প্রভাবগুলি বিবেচনা করছেন। কীভাবে vtables অপ্টিমাইজ করা হয় তা সম্পর্কে আমার বোঝার ব্যাখ্যাটি দেওয়ার জন্য আপনাকে ধন্যবাদ।
রবার্ট ব্যারন

ভাল উত্তর (+1) জন্য ধন্যবাদ। আমি আশ্চর্য হয়েছি যে এর মধ্যে কতটা অভিন্নভাবে ভার্চুয়াল ফাংশনগুলির পরিবর্তে স্ট্যান্ড :: ভিজিটের জন্য প্রযোজ্য।
ডেভফার

13

পর্যবেক্ষণ:

  • বহু ক্ষেত্রে, ভার্চুয়াল ফাংশনগুলি দ্রুত হয় কারণ ভেটেবল লুকআপ একটি O(1)অপারেশন যখন else if()মই O(n)অপারেশন। তবে এটি কেবল সত্য যদি মামলাগুলির বিতরণ সমতল হয় true

  • একটি একক জন্য if() ... else, শর্তাধীন দ্রুত কারণ আপনি ফাংশন কল ওভারহেড সংরক্ষণ করুন।

  • সুতরাং, যখন আপনার ক্ষেত্রে ফ্ল্যাট বিতরণ থাকে তখন অবশ্যই একটি বিরতি-পয়েন্ট থাকা উচিত। একমাত্র প্রশ্ন এটি কোথায় অবস্থিত।

  • আপনি যদি মই বা ভার্চুয়াল ফাংশন কলের switch()পরিবর্তে ব্যবহার করেন তবে else if()আপনার সংকলকটি আরও ভাল কোড তৈরি করতে পারে: এটি কোনও অবস্থানের জন্য একটি শাখা করতে পারে যা একটি টেবিল থেকে দেখা যায়, তবে যা কোনও ফাংশন কল নয়। এটি হ'ল সমস্ত ফাংশন কল ওভারহেড ছাড়াই ভার্চুয়াল ফাংশন কলের সমস্ত বৈশিষ্ট্য আপনার রয়েছে।

  • যদি বাকীগুলির চেয়ে কেউ ঘন ঘন ঘন হয় তবে if() ... elseসেই ক্ষেত্রে একটি শুরু করা আপনাকে সেরা পারফরম্যান্স দেবে: আপনি একটি একক শর্তাধীন শাখা কার্যকর করবেন যা বেশিরভাগ ক্ষেত্রে সঠিকভাবে পূর্বাভাস দেওয়া হয়।

  • আপনার সংকলক কেসগুলির প্রত্যাশিত বিতরণের কোনও জ্ঞান নেই এবং এটি একটি সমতল বিতরণ অনুমান করবে।

যেহেতু আপনার সংকলকটি সম্ভবত switch()কোনও else if()মই হিসাবে বা টেবিলে অনুসন্ধান হিসাবে কোড করবেন সে বিষয়ে কিছু ভাল হুরিস্টিকস রয়েছে । মামলার বিতরণ পক্ষপাতদুষ্ট না হলে আপনি তার রায়কে বিশ্বাস করতে চাই trust

সুতরাং, আমার পরামর্শটি হ'ল:

  • যদি কোনও ক্ষেত্রে ফ্রিকোয়েন্সি বিবেচনা করে বাকী অংশকে বামন করে তোলে, তবে বাছাই করা else if()মই ব্যবহার করুন ।

  • অন্যথায় একটি switch()বিবৃতি ব্যবহার করুন , যদি না অন্য যে কোনও একটি পদ্ধতি আপনার কোডকে আরও বেশি পঠনযোগ্য করে তোলে। নিশ্চিত হয়ে নিন যে আপনি উল্লেখযোগ্যভাবে হ্রাসযোগ্য পাঠযোগ্যতার সাথে অবহেলাযোগ্য পারফরম্যান্স লাভটি কিনছেন না।

  • যদি আপনি একটি ব্যবহার করেন switch()এবং এখনও পারফরম্যান্সে সন্তুষ্ট না হন তবে তুলনা করুন, তবে এটি অনুসন্ধানের জন্য প্রস্তুত থাকুন যে এটি switch()ইতিমধ্যে দ্রুততম সম্ভাবনা ছিল।


2
কিছু সংকলক সংক্ষিপ্ত বিবরণ সংকলককে বলতে দেয় যে কোন ক্ষেত্রেটি সত্য হওয়ার সম্ভাবনা বেশি, এবং comp সংকলকগণ এ্যানোটেশনটি সঠিক হিসাবে ততক্ষণ দ্রুত কোড তৈরি করতে পারে।
gnasher729

5
একটি ও (1) অপারেশন রিয়েল-ওয়ার্ল্ড এক্সিকিউশন সময় কোনও ও (এন) বা এমনকি ও (এন ^ 20) এর চেয়ে বেশি দ্রুত হয় না।
কিসনেম

2
@ ওয়াটসিসনাম এ কারণেই আমি "অনেক ক্ষেত্রে" বলেছি। সংজ্ঞা অনুসারে O(1)এবং O(n)সেখানে একটি উপস্থিত রয়েছে kযাতে ফাংশনটি সবার জন্য ফাংশনের O(n)চেয়ে বেশি । একমাত্র প্রশ্ন হ'ল আপনার অনেক ক্ষেত্রে এমন সম্ভাবনা রয়েছে। এবং, হ্যাঁ, আমি এতগুলি ক্ষেত্রে স্টেটমেন্ট দেখেছি যে কোনও মই অবশ্যই ভার্চুয়াল ফাংশন কল বা বোঝা প্রেরণের চেয়ে ধীর। O(1)n >= kswitch()else if()
মাস্টার -

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

7

সাধারণভাবে, ব্রাঞ্চিং এড়াতে ভার্চুয়াল ফাংশনগুলি ব্যবহার করা কি মূল্য?

সাধারণভাবে, হ্যাঁ রক্ষণাবেক্ষণের সুবিধাগুলি উল্লেখযোগ্য (পৃথকীকরণ, উদ্বেগের পৃথকীকরণ, উন্নত মডুলারালিটি এবং এক্সটেনসিবিলিটি পরীক্ষা করা)।

তবে, সাধারণভাবে, ভার্চুয়াল ফাংশন বনাম শাখাগুলি কত ব্যয়বহুল তা সাধারণ করার জন্য পর্যাপ্ত প্ল্যাটফর্মে পরীক্ষা করা শক্ত, তাই আমি ভাবছিলাম যে কারও কাছে কোনও থাম্বের মোটামুটি নিয়ম রয়েছে কিনা (সুন্দর যদি এটি 4 ifs এর মতো সাধারণ হয় তবে ব্রেকআপপয়েন্ট)

আপনি যদি নিজের কোডটি প্রোফাইল না করে থাকেন এবং শাখাগুলির মধ্যে প্রেরণের ( শর্তাদি মূল্যায়ন ) না জেনে থাকেন তবে সম্পাদিত গণনাগুলি ( শাখাগুলিতে কোড ) তুলনায় বেশি সময় নেয় না , সঞ্চালিত গুণাগুণকে অনুকূলিত করে তোলে।

এটি হ'ল "ভার্চুয়াল ফাংশন বনাম ব্রাঞ্চিং কত দামি" এর সঠিক উত্তর পরিমাপ করে খুঁজে বার করুন।

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

আপনি বলছেন যে আপনি এই বিভাগটি যত দ্রুত সম্ভব চালিত করতে চান; তা কত দ্রুত? আপনার কংক্রিট প্রয়োজনীয়তা কি?

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

ভার্চুয়াল ফাংশন ব্যবহার করুন। এটি এমনকি যদি প্রয়োজন হয় তবে প্রতি প্ল্যাটফর্ম প্রতি অনুকূলিতকরণ এবং ক্লায়েন্ট কোড পরিষ্কার রাখতে দেয়।


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

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

5

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

আমি একটি পাইথন স্ক্রিপ্ট লিখেছি যাতে কোনও ভিএমের জন্য সি ++ ১৪ কোড এলোমেলোভাবে একটি নির্দেশ সেট আকারের সাথে এলোমেলোভাবে বেছে নেওয়া হয় (যদিও অভিন্ন নয়, কম পরিসরে আরও ঘন করে নমুনা দেওয়া হয়) 1 এবং 10000 এর মধ্যে। উত্পাদিত ভিএম এর সর্বদা 128 টি রেজিস্টার ছিল এবং না র্যাম. নির্দেশাবলী অর্থবহ নয় এবং সকলের নিম্নলিখিত ফর্ম রয়েছে।

inline void
op0004(machine_state& state) noexcept
{
  const auto c = word_t {0xcf2802e8d0baca1dUL};
  const auto r1 = state.registers[58];
  const auto r2 = state.registers[69];
  const auto r3 = ((r1 + c) | r2);
  state.registers[6] = r3;
}

স্ক্রিপ্টটি switchবিবৃতি ব্যবহার করে প্রেরণের রুটিনগুলিও তৈরি করে ...

inline int
dispatch(machine_state& state, const opcode_t opcode) noexcept
{
  switch (opcode)
  {
  case 0x0000: op0000(state); return 0;
  case 0x0001: op0001(state); return 0;
  // ...
  case 0x247a: op247a(state); return 0;
  case 0x247b: op247b(state); return 0;
  default:
    return -1;  // invalid opcode
  }
}

… এবং ফাংশন পয়েন্টারগুলির একটি অ্যারে।

inline int
dispatch(machine_state& state, const opcode_t opcode) noexcept
{
  typedef void (* func_type)(machine_state&);
  static const func_type table[VM_NUM_INSTRUCTIONS] = {
    op0000,
    op0001,
    // ...
    op247a,
    op247b,
  };
  if (opcode >= VM_NUM_INSTRUCTIONS)
    return -1;  // invalid opcode
  table[opcode](state);
  return 0;
}

কোন প্রেরণের রুটিন উত্পন্ন হয়েছিল তা প্রতিটি উত্পন্ন ভিএম-এর জন্য এলোমেলোভাবে বেছে নেওয়া হয়েছিল।

বেঞ্চমার্কিংয়ের জন্য, অপ-কোডগুলির স্ট্রিমটি এলোমেলোভাবে বীজযুক্ত ( std::random_device) মের্সেন টুইস্টার র্যান্ডম ইঞ্জিন ( std::mt19937_64) দ্বারা উত্পাদিত হয়েছিল ।

প্রতিটি ভিএম এর কোডটি জি সি সি 5.2.0 সহ -DNDEBUG, -O3এবং -std=c++14স্যুইচ ব্যবহার করে সংকলিত হয়েছিল । প্রথমত, এটি -fprofile-generate1000 এলোমেলো নির্দেশাবলীর অনুকরণের জন্য সংগৃহীত বিকল্প এবং প্রোফাইল ডেটা ব্যবহার করে সংকলিত হয়েছিল । এরপরে -fprofile-useসংগৃহীত প্রোফাইল ডেটার উপর ভিত্তি করে অপ্টিমাইজেশনকে অনুমতি দিয়ে বিকল্পটি দিয়ে কোডটি পুনরায় সংকলন করা হয়েছিল ।

এরপরে ভিএমটি 50,000 000 চক্রের জন্য চারবার এবং একই রানের জন্য পরিমাপ করা হয়েছিল (একই প্রক্রিয়াতে) times কোল্ড-ক্যাশে প্রভাবগুলি দূর করতে প্রথম রানটি বাতিল করা হয়েছিল। আরআরএনজিকে রানগুলির মধ্যে পুনরায় বদ্ধ করা হয়নি যাতে তারা নির্দেশের একই ক্রমটি সম্পাদন না করে।

এই সেটআপটি ব্যবহার করে প্রতিটি প্রেরণের রুটিনের জন্য 1000 ডাটা পয়েন্ট সংগ্রহ করা হয়েছিল। কোয়াড কোর এএমডি এ 8-6600 কে এপিইউতে তথ্য সংগ্রহ করা হয়েছিল 2048 কিবি ক্যাশে একটি গ্রাফিকাল ডেস্কটপ বা অন্যান্য প্রোগ্রাম চলমান ছাড়া 64 বিট জিএনইউ / লিনাক্স চালিত। নীচে দেখানো হয়েছে প্রতিটি ভিএমের প্রতি নির্দেশ অনুসারে গড় সিপিইউ সময়ের (স্ট্যান্ডার্ড বিচ্যুতি সহ) একটি প্লট।

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

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

বেঞ্চমার্কের জন্য সমস্ত উত্স কোডের পাশাপাশি পুরো পরীক্ষামূলক ডেটা এবং একটি উচ্চ রেজোলিউশন প্লট আমার ওয়েবসাইটে পাওয়া যাবে ।


3

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

আরেকটি বিষয় মনে রাখবেন: যদি আপনার সমালোচনামূলক পথে কোনও লুপ জড়িত থাকে তবে প্রেরণের গন্তব্য দ্বারা লুপটি বাছাই করা সহায়ক হতে পারে। স্পষ্টতই এটি নিষ্ক্রিয়, লুপটি অতিক্রম করার সময় কেবল এন, তবে আপনি যদি বহুবার ট্র্যাভার করতে যাচ্ছেন তবে এটি মূল্যবান হতে পারে। প্রেরণের গন্তব্য অনুসারে বাছাই করে, আপনি নিশ্চিত হন যে একই কোডটি বারবার কার্যকর করা হবে, এটি আইচাকে গরম রেখে, ক্যাশে মিস মিস না করে repeatedly

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

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


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

1
এটি অতিরিক্ত পদক্ষেপ জড়িত না। ভেটেবল নিজেই ফাংশন পয়েন্টার ধারণ করে, সুতরাং আপনি যখন এটিটি ভিটিবেলে পরিণত করেন, আপনি একই ফাংশন পয়েন্টার দিয়ে শুরু করে একই অবস্থানে পৌঁছেছেন। ভিটিবেলে যাওয়ার আগে সমস্ত কিছু অতিরিক্ত কাজ। ক্লাসগুলিতে তাদের ভেটেবল থাকে না, তাদের ভিটিবেলে পয়েন্টার থাকে এবং সেই পয়েন্টারটি অনুসরণ করা একটি অতিরিক্ত ডিসেরেন্স। বাস্তবে, পলিমারফিক ক্লাসগুলি সাধারণত বেস ক্লাস পয়েন্টার দ্বারা অনুষ্ঠিত হয় বলে অনেক সময় তৃতীয় ডিরিফারেন্স হয়, সুতরাং আপনাকে ভিটিবেল ঠিকানা (এটির অবলম্বন করতে ;-)) পেতে একটি পয়েন্টারকে ডিফারেন্স করতে হবে।
নীড় ফ্রেডম্যান

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

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

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

2

আমি কেবল এটি ব্যাখ্যা করতে পারি যে আমি কেন এটি একটি এক্সওয়াই সমস্যা ? (আপনি তাদের জিজ্ঞাসা একা নন।)

আমি ধরে নিলাম আপনার আসল লক্ষ্যটি কেবল ক্যাশে-মিস এবং ভার্চুয়াল ফাংশন সম্পর্কে একটি বিন্দু বোঝার জন্য নয়, সামগ্রিকভাবে সময় সাশ্রয় করা।

বাস্তব সফ্টওয়্যারটিতে বাস্তব পারফরম্যান্সের টিউন করার উদাহরণ এখানে's

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

আমি যে উদাহরণটির সাথে লিঙ্ক করেছি, এটি মূলত "কাজ" প্রতি 2700 মাইক্রোসেকেন্ড নিয়েছিল। পিজ্জার চারদিকে ঘড়ির কাঁটার দিকে চলে ছয়টি সমস্যার একটি সিরিজ ঠিক করা হয়েছিল। প্রথম স্পিডআপটি 33% সময় সরিয়ে ফেলে। দ্বিতীয়টি 11% অপসারণ করেছে। তবে লক্ষ্য করুন, দ্বিতীয়টি খুঁজে পাওয়া সময়ে ১১% ছিল না, এটি ছিল ১%%, কারণ প্রথম সমস্যাটি শেষ হয়ে গিয়েছিল । একইভাবে, তৃতীয় সমস্যাটি two.৪% থেকে ১৩% (প্রায় দ্বিগুণ) পরিণত হয়েছিল কারণ প্রথম দুটি সমস্যা চলে গেছে।

শেষ পর্যন্ত, এই পরিবর্ধন প্রক্রিয়াটি 3.7 মাইক্রোসেকেন্ডগুলি ব্যতীত সমস্ত অপসারণের অনুমতি দেয়। এটি আসল সময়ের 0.14% বা 730x গতিবেগ।

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

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

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

চূড়ান্ত প্রোগ্রামটি কি সর্বোত্তম ছিল? সম্ভবত না. স্পিডআপগুলির কোনওটিরই ক্যাশে মিস করার কোনও সম্পর্ক ছিল না। এখন কি ক্যাশে মিস করবে? হতে পারে.

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


তিনি ইতিমধ্যে বলেছেন যে তাঁর বেশ কয়েকটি "অত্যন্ত সমালোচনামূলক বিভাগ" রয়েছে যার জন্য প্রতিটি শেষ ন্যানোসেকেন্ডের অভিনয় প্রয়োজন। সুতরাং এটি তাঁর জিজ্ঞাসা করা প্রশ্নের উত্তর নয় (এমনকি এটি অন্য কারও প্রশ্নের দুর্দান্ত উত্তর
হলেও

2
@gbjbaanb: যদি প্রতি শেষ ন্যানোসেকেন্ড গণনা করা হয়, তবে প্রশ্নটি "সাধারণভাবে" কেন শুরু হয়? ওটা ফালতু কথা. যখন ন্যানোসেকেন্ডগুলি গণনা করা হয়, আপনি সাধারণ উত্তরগুলির সন্ধান করতে পারবেন না, আপনি সংকলকটি কী করে তা দেখুন, হার্ডওয়্যার কী করে তা দেখুন, আপনি বিভিন্নতা চেষ্টা করেন এবং প্রতিটি প্রকারের পরিমাপ করেন।
gnasher729

@ gnasher729 আমি জানি না, তবে কেন এটি "অত্যন্ত সমালোচনা বিভাগ" দিয়ে শেষ হয়? আমার ধারণা, স্ল্যাশডোটের মতো, সর্বদা সামগ্রীটি পড়া উচিত, এবং কেবল শিরোনাম নয়!
gbjbaanb

2
@gbjbaanb: প্রত্যেকেই বলেছে যে তারা "অত্যন্ত সমালোচনামূলক বিভাগ" পেয়েছে। তারা কীভাবে জানবে? আমি জানি না যে আমি 10 টি নমুনা না নিয়ে, না বলা এবং এগুলির 2 বা ততোধিক কিছু দেখার জন্য কিছু সমালোচনামূলক। এর মতো ক্ষেত্রে, যদি পদ্ধতিগুলি বলা হচ্ছে 10 টিরও বেশি নির্দেশনা গ্রহণ করে তবে ভার্চুয়াল ফাংশন ওভারহেড সম্ভবত তুচ্ছ।
মাইক ডুনলাভে 12'15

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