কেন ল্যাম্বডাস সরল ফাংশনগুলির তুলনায় সংকলক দ্বারা আরও ভাল করতে পারেন?


171

The C++ Standard Library (Second Edition)নিকোলাই জোসুটিস তাঁর বইয়ে লিখেছেন যে ল্যাম্বডাস সংকলক দ্বারা সরল ফাংশনগুলির চেয়ে আরও ভালভাবে অনুকূল করা যেতে পারে।

তদতিরিক্ত, সি ++ সংকলকগুলি ল্যাম্বডাসকে সাধারণ ক্রিয়াকলাপের চেয়ে আরও ভাল করে। (পৃষ্ঠা 213)

কেন এমন?

আমি ভেবেছিলাম যখন এটি ইনলাইনিংয়ের কথা আসে তখন আর কোনও পার্থক্য থাকা উচিত নয়। আমি একমাত্র কারণটি ভাবতে পারি যে কম্পাইলারগুলিতে ল্যাম্বডাসের সাথে আরও ভাল স্থানীয় প্রসঙ্গ থাকতে পারে এবং এগুলি আরও অনুমান করা যায় এবং আরও অনুকূলিতকরণ করতে পারে।



মূলত, বিবৃতিটি কেবল ল্যাম্বডাস নয়, সমস্ত ফাংশন অবজেক্টের ক্ষেত্রে প্রযোজ্য ।
newacct

4
এটি ভুল হবে কারণ ফাংশন পয়েন্টারগুলিও ফাংশন অবজেক্ট।
জোহানেস স্কাউব -

2
@ লিটব: আমার মনে হয় আমি এর সাথে একমত নই ^ উইকিপিডিয়া), লোকেরা ফাংশন অবজেক্ট বলে যখন উদাহরণস্বরূপ কিছু কলযোগ্য-বর্গ বোঝায়।
সেবাস্তিয়ান মাচ

1
কিছু সংকলক সরল ফাংশনগুলির চেয়ে ল্যাম্বডাসকে আরও অনুকূল করতে পারে তবে সমস্তটি নয় :-(
কোডি গ্রে

উত্তর:


175

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

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

উদাহরণ হিসাবে, নিম্নলিখিত ফাংশন টেম্পলেট বিবেচনা করুন:

template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

এটিকে এভাবে ল্যাম্বডা দিয়ে কল করা:

int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });

এই ইনস্ট্যান্টেশন ফলাফল (সংকলক দ্বারা নির্মিত):

template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
    for (; begin != end; ++begin)
        *begin = f.operator()(*begin);
}

… সংকলক জানে _some_lambda_type::operator ()এবং এগুলিকে তুচ্ছভাবে কল করতে পারে line (এবং অন্য কোনও ল্যাম্বডারের mapসাথে ফাংশনটি আহ্বান করা একটি নতুন ইনস্ট্যান্টেশন তৈরি করবে যেহেতু প্রতিটি ল্যাম্বডায় একটি আলাদা ধরণের রয়েছে))map

তবে যখন ফাংশন পয়েন্টার দিয়ে ডাকা হয় তখন ইনস্ট্যান্টেশনটি নিম্নরূপ দেখায়:

template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

… এবং এখানে fপ্রতিটি কলের জন্য পৃথক ঠিকানার দিকে ইঙ্গিত করে mapএবং এভাবে সংকলক fআশেপাশের mapকলটিও ইনলাইন না করা পর্যন্ত কলগুলিতে ইনলাইন করতে পারে না যাতে সংকলকটি fএকটি নির্দিষ্ট ফাংশনে সমাধান করতে পারে।


4
সম্ভবত এটি উল্লেখ করার মতো যে একই ল্যাম্বদা এক্সপ্রেশন সহ একই ফাংশন টেম্পলেটটি ইনস্ট্যান্ট করা অনন্য ধরণের একটি সম্পূর্ণ নতুন ফাংশন তৈরি করবে, যা সম্ভবত একটি অসুবিধাও হতে পারে।
ঠাণ্ডা

2
নিখুঁতভাবে সমস্যাটি হ'ল ফাংশনগুলি হ্যান্ডল করার সময় যা ইনলাইন করা যায় না (কারণ সেগুলি খুব বড়)। এখানে কলব্যাকের কলটি ল্যাম্বদার ক্ষেত্রে এখনও ইনলাইন করা যেতে পারে তবে কোনও ফাংশন পয়েন্টারের ক্ষেত্রে নয়। std::sortএখানে কোনও ফাংশন পয়েন্টারের পরিবর্তে ল্যাম্বডাস ব্যবহারের শাস্ত্রীয় উদাহরণটি সাত গুণ পর্যন্ত (সম্ভবত আরও বেশি, তবে আমার তাতে কোনও ডেটা নেই!) কর্মক্ষমতা বৃদ্ধি পায়।
কনরাড রুডল্ফ

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

2
@ গ্রেগগো আমি জানি যদিও আমার উত্তরের শেষ বাক্যটি এটি আক্ষরিক বলে।
কনরাড রুডল্ফ

1
যা আমি কৌতূহলী মনে করি (এটি কেবলমাত্র এতে হোঁচট খেয়েছে) তা হল একটি সাধারণ বুলিয়ান ফাংশন দেওয়া predযার সংজ্ঞা দৃশ্যমান, এবং জিসিসি ভি 5.3 ব্যবহার করে, std::find_if(b, e, pred)ইনলাইন করে না pred, তবে std::find_if(b, e, [](int x){return pred(x);})তা করে। বিড়ম্বনা উভয়কেই ইনলাইন করতে পরিচালিত করে তবে ল্যাম্বদার সাথে জি ++ এর মতো দ্রুততর কোড তৈরি করে না।
ধনী

26

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


5
"ল্যাম্বডা ফাংশনে কলটি একটি সরাসরি কল" - সত্যই। এবং একই জিনিসটি কেবল ল্যাম্বডাস নয়, সমস্ত ফাংশন অবজেক্টের ক্ষেত্রে সত্য । এটি কেবল ফাংশন পয়েন্টার যা কোনওভাবেই যদি সহজে .োকানো যায় না।
পিট বেকার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.