সুস্পষ্ট তাত্ক্ষণিকতা সংকলনের সময় এবং অবজেক্টের আকার হ্রাস করতে দেয়
এগুলি এটি সরবরাহ করতে পারে এমন বড় লাভ। তারা নীচের বিভাগগুলিতে বিস্তারিতভাবে বর্ণিত নিম্নলিখিত দুটি প্রভাব থেকে এসেছে:
- বিল্ড সরঞ্জামগুলিকে পুনর্নির্মাণকারী অন্তর্ভুক্তকরণ থেকে রোধ করতে শিরোনামগুলি থেকে সংজ্ঞাগুলি সরান
- অবজেক্টের নতুন সংজ্ঞা
শিরোনামগুলি থেকে সংজ্ঞাগুলি সরান
সুস্পষ্ট ইনস্ট্যান্টেশন আপনাকে .cpp ফাইলে সংজ্ঞা রাখতে দেয়।
সংজ্ঞাটি যখন শিরোনামে থাকে এবং আপনি এটিকে সংশোধন করেন, একটি বুদ্ধিমান বিল্ড সিস্টেম সমস্ত অন্তর্ভুক্তকারীকে পুনরায় সংকলন করবে, যা কয়েক ডজন ফাইল হতে পারে, সংকলনটি অসহনীয়ভাবে ধীর করে দেয়।
.Cpp ফাইলগুলিতে সংজ্ঞা স্থাপনের ফলে এমন ক্ষতি হয় যে বাহ্যিক গ্রন্থাগারগুলি তাদের নিজস্ব নতুন ক্লাসগুলির সাহায্যে টেমপ্লেটটি পুনরায় ব্যবহার করতে পারে না, তবে "অন্তর্ভুক্ত শিরোনামগুলি থেকে সংজ্ঞাগুলি সরিয়ে ফেলতে পারে তবে টেমপ্লেটগুলি বহিরাগত এপিআইও উন্মুক্ত করে" নীচে একটি কার্যপ্রণালী দেখায়।
নীচে কংক্রিট উদাহরণ দেখুন।
অবজেক্টের পুনঃনির্ধারণ লাভ: সমস্যাটি বোঝা
আপনি যদি একটি শিরোনাম ফাইলটিতে কেবলমাত্র একটি টেম্পলেটকে সম্পূর্ণরূপে সংজ্ঞায়িত করেন তবে প্রতিটি একক সংকলন ইউনিট যার মধ্যে শিরোনামটি অন্তর্ভুক্ত থাকে প্রতিটি পৃথক টেম্পলেট আর্গুমেন্ট ব্যবহারের জন্য টেমপ্লেটের নিজস্ব অন্তর্নিহিত অনুলিপি সংকলন করে।
এর অর্থ প্রচুর অকেজো ডিস্ক ব্যবহার এবং সংকলনের সময়।
এখানে একটি কংক্রিটের উদাহরণ দেওয়া আছে, যেখানে এই ফাইলগুলিতে এর ব্যবহারের কারণে উভয়ই main.cpp
এবং notmain.cpp
স্পষ্টভাবে সংজ্ঞায়িত MyTemplate<int>
করে।
main.cpp
#include <iostream>
#include "mytemplate.hpp"
#include "notmain.hpp"
int main() {
std::cout << notmain() + MyTemplate<int>().f(1) << std::endl;
}
notmain.cpp
#include "mytemplate.hpp"
#include "notmain.hpp"
int notmain() { return MyTemplate<int>().f(1); }
mytemplate.hpp
#ifndef MYTEMPLATE_HPP
#define MYTEMPLATE_HPP
template<class T>
struct MyTemplate {
T f(T t) { return t + 1; }
};
#endif
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
int notmain();
#endif
গিটহাব উজানের দিকে ।
প্রতীকগুলি সংকলন এবং দেখুন nm
:
g++ -c -Wall -Wextra -std=c++11 -pedantic-errors -o notmain.o notmain.cpp
g++ -c -Wall -Wextra -std=c++11 -pedantic-errors -o main.o main.cpp
g++ -Wall -Wextra -std=c++11 -pedantic-errors -o main.out notmain.o main.o
echo notmain.o
nm -C -S notmain.o | grep MyTemplate
echo main.o
nm -C -S main.o | grep MyTemplate
আউটপুট:
notmain.o
0000000000000000 0000000000000017 W MyTemplate<int>::f(int)
main.o
0000000000000000 0000000000000017 W MyTemplate<int>::f(int)
থেকে man nm
, আমরা এর W
অর্থ দুর্বল প্রতীক, যা জিসিসি বেছে নিয়েছে কারণ এটি একটি টেম্পলেট ফাংশন। দুর্বল প্রতীকটির অর্থ হ'ল সংকলিত নিখুঁতভাবে উত্পন্ন কোড MyTemplate<int>
দুটি ফাইলেই সংকলিত হয়েছিল।
একাধিক সংজ্ঞা সহ লিংক সময়ে এটি উড়ে না যাওয়ার কারণটি হ'ল লিঙ্কার একাধিক দুর্বল সংজ্ঞা গ্রহণ করে, এবং কেবলমাত্র চূড়ান্ত সম্পাদনযোগ্যতে রাখার জন্য তাদের মধ্যে একটি বেছে নেয়।
আউটপুট সংখ্যার অর্থ:
0000000000000000
: বিভাগের মধ্যে ঠিকানা। এই শূন্যটি কারণ টেমপ্লেটগুলি স্বয়ংক্রিয়ভাবে তাদের নিজস্ব বিভাগে রাখা হয়
0000000000000017
: তাদের জন্য উত্পন্ন কোডের আকার
আমরা এর সাথে আরও স্পষ্ট দেখতে পাচ্ছি:
objdump -S main.o | c++filt
যা শেষ হয়:
Disassembly of section .text._ZN10MyTemplateIiE1fEi:
0000000000000000 <MyTemplate<int>::f(int)>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 89 7d f8 mov %rdi,-0x8(%rbp)
c: 89 75 f4 mov %esi,-0xc(%rbp)
f: 8b 45 f4 mov -0xc(%rbp),%eax
12: 83 c0 01 add $0x1,%eax
15: 5d pop %rbp
16: c3 retq
এবং _ZN10MyTemplateIiE1fEi
সেই মঙ্গলের নাম MyTemplate<int>::f(int)>
যা c++filt
স্থির করার সিদ্ধান্ত নিয়েছে।
সুতরাং আমরা দেখতে পাচ্ছি যে প্রতিটি একক পদ্ধতি ইনস্ট্যান্টেশনের জন্য একটি পৃথক বিভাগ তৈরি করা হয়েছে এবং তাদের প্রত্যেকে অবজেক্ট ফাইলগুলিতে অবশ্যই স্থান গ্রহণ করবে।
অবজেক্টের পুনরায় সংজ্ঞা সমস্যার সমাধান
এই সমস্যাটি সুস্পষ্ট তাত্ক্ষণিক ব্যবহারের মাধ্যমে বা এড়ানো যায়:
এইচপিপিতে সংজ্ঞা রাখুন এবং extern template
স্পষ্টভাবে তাত্ক্ষণিকভাবে চলতে চলেছে এমন প্রকারের জন্য এইচপিকে যুক্ত করুন।
যেমন ব্যাখ্যা করা হয়েছে: বহির্মুখী টেম্পলেট (সি ++ 11) extern template
ব্যবহার করে আমাদের স্পষ্ট তাত্ক্ষণিকতা ব্যতীত সম্পূর্ণরূপে সংজ্ঞায়িত টেমপ্লেট সংকলন ইউনিট দ্বারা ইনস্ট্যান্ট হওয়া থেকে বাধা দেয়। এইভাবে, কেবলমাত্র আমাদের স্পষ্ট ইনস্ট্যান্টেশন চূড়ান্ত বস্তুগুলিতে সংজ্ঞায়িত করা হবে:
mytemplate.hpp
#ifndef MYTEMPLATE_HPP
#define MYTEMPLATE_HPP
template<class T>
struct MyTemplate {
T f(T t) { return t + 1; }
};
extern template class MyTemplate<int>;
#endif
mytemplate.cpp
#include "mytemplate.hpp"
template class MyTemplate<int>;
main.cpp
#include <iostream>
#include "mytemplate.hpp"
#include "notmain.hpp"
int main() {
std::cout << notmain() + MyTemplate<int>().f(1) << std::endl;
}
notmain.cpp
#include "mytemplate.hpp"
#include "notmain.hpp"
int notmain() { return MyTemplate<int>().f(1); }
নেতিবাচক:
- আপনি যদি কেবল শিরোনামের গ্রন্থাগার হন তবে আপনি বাহ্যিক প্রকল্পগুলিকে তাদের নিজস্ব সুস্পষ্ট ইনস্ট্যান্টেশন করতে বাধ্য করেন। আপনি যদি কেবল শিরোনামের গ্রন্থাগার না হন তবে এই সমাধানটি সম্ভবত সেরা।
- যদি টেমপ্লেট প্রকারটি আপনার নিজস্ব প্রকল্পে সংজ্ঞায়িত করা হয় এবং কোনও বিল্ট-ইন লাইক নয়
int
, তবে মনে হয় যে আপনি এটির জন্য অন্তর্ভুক্তটি শিরোনামে যুক্ত করতে বাধ্য হয়েছেন, একটি অগ্রণী ঘোষণা যথেষ্ট নয়: বাহ্যিক টেম্পলেট এবং অসম্পূর্ণ প্রকারের ফলে শিরোনাম নির্ভরতা বাড়ে একটু.
সিপিপি ফাইলে সংজ্ঞাটি সরিয়ে, এইচপিপিতে কেবল ঘোষণা রেখে যান, অর্থাত্ আসল উদাহরণটি পরিবর্তন করুন:
mytemplate.hpp
#ifndef MYTEMPLATE_HPP
#define MYTEMPLATE_HPP
template<class T>
struct MyTemplate {
T f(T t);
};
#endif
mytemplate.cpp
#include "mytemplate.hpp"
template<class T>
T MyTemplate<T>::f(T t) { return t + 1; }
template class MyTemplate<int>;
ডাউনসাইড: বাহ্যিক প্রকল্পগুলি তাদের টেম্পলেটটি তাদের নিজস্ব ধরণের সাথে ব্যবহার করতে পারে না। এছাড়াও আপনি সমস্ত ধরণের স্পষ্টভাবে তাত্ক্ষণিকভাবে বাধ্য হতে বাধ্য হন। তবে সম্ভবত এটি একটি উল্টোপাল্ট থেকে প্রোগ্রামাররা ভুলে যাবেন না।
এইচপিপিতে সংজ্ঞা রাখুন এবং extern template
প্রতিটি অন্তর্ভুক্তকারীকে যুক্ত করুন:
mytemplate.cpp
#include "mytemplate.hpp"
template class MyTemplate<int>;
main.cpp
#include <iostream>
#include "mytemplate.hpp"
#include "notmain.hpp"
extern template class MyTemplate<int>;
int main() {
std::cout << notmain() + MyTemplate<int>().f(1) << std::endl;
}
notmain.cpp
#include "mytemplate.hpp"
#include "notmain.hpp"
extern template class MyTemplate<int>;
int notmain() { return MyTemplate<int>().f(1); }
ডাউনসাইড: সমস্ত অন্তর্ভুক্তকারীকে extern
তাদের সিপিপি ফাইলগুলিতে যুক্ত করতে হবে, যা প্রোগ্রামাররা সম্ভবত এটি করতে ভুলে যাবে।
এই সমাধানগুলির যে কোনও একটি সহ, nm
এখন রয়েছে:
notmain.o
U MyTemplate<int>::f(int)
main.o
U MyTemplate<int>::f(int)
mytemplate.o
0000000000000000 W MyTemplate<int>::f(int)
তাই আমরা দেখতে আছে শুধুমাত্র mytemplate.o
একটি সঙ্কলন হয়েছে MyTemplate<int>
সময়, পছন্দসই হিসাবে notmain.o
এবং main.o
না, কারণ U
উপায়ে undefined।
অন্তর্ভুক্ত শিরোলেখগুলি থেকে সংজ্ঞাগুলি সরান তবে টেমপ্লেটগুলি কেবলমাত্র শিরোনামের লাইব্রেরিতে একটি বাহ্যিক এপিআই প্রকাশ করে
আপনার লাইব্রেরি যদি কেবল শিরোনাম না হয় তবে extern template
পদ্ধতিটি কাজ করবে, যেহেতু প্রকল্পগুলি ব্যবহার করা কেবলমাত্র আপনার অবজেক্ট ফাইলে লিঙ্ক করবে, এতে সুস্পষ্ট টেম্পলেট ইনস্ট্যান্টেশনের অবজেক্ট থাকবে।
তবে, কেবল শিরোনামের গ্রন্থাগারগুলির জন্য, যদি আপনি উভয়ই চান:
- আপনার প্রকল্পের সংকলন গতি
- অন্যদের এটির ব্যবহারের জন্য একটি বাহ্যিক গ্রন্থাগার API হিসাবে শিরোনামগুলি প্রকাশ করুন
তাহলে আপনি নিম্নলিখিতগুলির মধ্যে একটি ব্যবহার করতে পারেন:
-
mytemplate.hpp
: টেমপ্লেট সংজ্ঞা
mytemplate_interface.hpp
: mytemplate_interface.hpp
কোনও সংজ্ঞা নয়, কেবল সংজ্ঞাগুলির সাথে মিলে টেমপ্লেট ঘোষণা
mytemplate.cpp
: অন্তর্ভুক্ত mytemplate.hpp
এবং স্পষ্ট তাত্ক্ষণিক করা
main.cpp
এবং কোড বেসের অন্য যে কোনও জায়গায়: অন্তর্ভুক্ত করুন mytemplate_interface.hpp
নাmytemplate.hpp
-
mytemplate.hpp
: টেমপ্লেট সংজ্ঞা
mytemplate_implementation.hpp
: অন্তর্ভুক্ত করা হবে mytemplate.hpp
এবং extern
প্রতি ক্লাসে যুক্ত হবে যা তাত্ক্ষণিক হবে
mytemplate.cpp
: অন্তর্ভুক্ত mytemplate.hpp
এবং স্পষ্ট তাত্ক্ষণিক করা
main.cpp
এবং কোড বেসের অন্য যে কোনও জায়গায়: অন্তর্ভুক্ত করুন mytemplate_implementation.hpp
নাmytemplate.hpp
বা আরও ভাল সম্ভবত একাধিক শিরোনাম: আপনার ফোল্ডারের ভিতরে একটি intf
/ impl
ফোল্ডার তৈরি করুন includes/
এবং mytemplate.hpp
সর্বদা নাম হিসাবে ব্যবহার করুন ।
mytemplate_interface.hpp
পদ্ধতির ভালো দেখায়:
mytemplate.hpp
#ifndef MYTEMPLATE_HPP
#define MYTEMPLATE_HPP
#include "mytemplate_interface.hpp"
template<class T>
T MyTemplate<T>::f(T t) { return t + 1; }
#endif
mytemplate_interface.hpp
#ifndef MYTEMPLATE_INTERFACE_HPP
#define MYTEMPLATE_INTERFACE_HPP
template<class T>
struct MyTemplate {
T f(T t);
};
#endif
mytemplate.cpp
#include "mytemplate.hpp"
template class MyTemplate<int>;
main.cpp
#include <iostream>
#include "mytemplate_interface.hpp"
int main() {
std::cout << MyTemplate<int>().f(1) << std::endl;
}
সংকলন এবং চালান:
g++ -c -Wall -Wextra -std=c++11 -pedantic-errors -o mytemplate.o mytemplate.cpp
g++ -c -Wall -Wextra -std=c++11 -pedantic-errors -o main.o main.cpp
g++ -Wall -Wextra -std=c++11 -pedantic-errors -o main.out main.o mytemplate.o
আউটপুট:
2
উবুন্টুতে পরীক্ষিত 18.04।
সি ++ 20 মডিউল
https://en.cppreferences.com/w/cpp/language/modules
আমি মনে করি এই বৈশিষ্ট্যটি উপলব্ধ হয়ে ওঠার সাথে সাথে এগিয়ে যাওয়ার সর্বোত্তম সেটআপ সরবরাহ করবে তবে আমি এখনও এটি পরীক্ষা করে দেখিনি কারণ এটি এখনও আমার জিসিসি 9.2.1 এ উপলব্ধ নেই।
স্পিডআপ / ডিস্ক সাশ্রয় পেতে আপনাকে এখনও স্পষ্ট তাত্ক্ষণিক কাজ করতে হবে তবে কমপক্ষে আমাদের "অন্তর্ভুক্ত শিরোনামগুলি থেকে সংজ্ঞাগুলি সরিয়ে ফেলুন তবে টেমপ্লেটগুলি একটি বাহ্যিক এআইপিও উন্মুক্ত করুন" এর জন্য একটি সমঝোতা সমাধান হবে যার জন্য প্রায় 100 বার জিনিস অনুলিপি করার প্রয়োজন হয় না।
প্রত্যাশিত ব্যবহার (সুস্পষ্ট উন্মাদনা ছাড়াই, সঠিক বাক্য গঠন কেমন হবে তা নিশ্চিত নয়, দেখুন: সি ++ 20 মডিউলগুলির সাহায্যে টেম্পলেট কীভাবে স্পষ্টভাবে ইনস্ট্যান্টেশন ব্যবহার করবেন? ) পাশাপাশি কিছু থাকুন:
helloworld.cpp
export module helloworld;
import <iostream>;
template<class T>
export void hello(T t) {
std::cout << t << std::end;
}
main.cpp
import helloworld;
int main() {
hello(1);
hello("world");
}
এবং তারপরে https://quuxplusone.github.io/blog/2019/11/07/modular-hello-world/ এ উল্লিখিত সংকলন
clang++ -std=c++2a -c helloworld.cpp -Xclang -emit-module-interface -o helloworld.pcm
clang++ -std=c++2a -c -o helloworld.o helloworld.cpp
clang++ -std=c++2a -fprebuilt-module-path=. -o main.out main.cpp helloworld.o
সুতরাং এ থেকে আমরা দেখতে পাচ্ছি যে ঝনঝন টেম্পলেট ইন্টারফেস + প্রয়োগকে যাদুতে উত্তোলন করতে পারে helloworld.pcm
, যার মধ্যে উত্সটির কিছু এলএলভিএম অন্তর্বর্তী প্রতিনিধিত্ব থাকতে হবে: সি ++ মডিউল সিস্টেমে টেমপ্লেটগুলি কীভাবে পরিচালিত হয়? যা এখনও টেমপ্লেট স্পেসিফিকেশন ঘটতে দেয়।
আপনার বিল্ডটিকে কীভাবে দ্রুত বিশ্লেষণ করবেন তা দেখার জন্য এটি টেমপ্লেট ইনস্ট্যান্টেশন থেকে অনেক উপার্জন পাবে কিনা
সুতরাং, আপনি একটি জটিল প্রকল্প পেয়েছেন এবং আপনি সিদ্ধান্ত নিতে চান যে টেম্পলেট ইনস্ট্যান্টেশনটি আসলে সম্পূর্ণ রিফেক্টরটি না করেই উল্লেখযোগ্য লাভ অর্জন করবে কিনা?
নীচের বিশ্লেষণগুলি আপনাকে সিদ্ধান্ত নিতে সহায়তা করতে পারে বা কমপক্ষে কমপক্ষে কিছু ধারণা ধার করে আপনি পরীক্ষার সময় প্রথমে রিফ্যাক্টর করার জন্য সবচেয়ে প্রতিশ্রুতিবদ্ধ বিষয়গুলি বেছে নিতে পারেন: আমার সি ++ অবজেক্ট ফাইলটি খুব বড়
# List all weak symbols with size only, no address.
find . -name '*.o' | xargs -I{} nm -C --size-sort --radix d '{}' |
grep ' W ' > nm.log
# Sort by symbol size.
sort -k1 -n nm.log -o nm.sort.log
# Get a repetition count.
uniq -c nm.sort.log > nm.uniq.log
# Find the most repeated/largest objects.
sort -k1,2 -n nm.uniq.log -o nm.uniq.sort.log
# Find the objects that would give you the most gain after refactor.
# This gain is calculated as "(n_occurences - 1) * size" which is
# the size you would gain for keeping just a single instance.
# If you are going to refactor anything, you should start with the ones
# at the bottom of this list.
awk '{gain = ($1 - 1) * $2; print gain, $0}' nm.uniq.sort.log |
sort -k1 -n > nm.gains.log
# Total gain if you refactored everything.
awk 'START{sum=0}{sum += $1}END{print sum}' nm.gains.log
# Total size. The closer total gain above is to total size, the more
# you would gain from the refactor.
awk 'START{sum=0}{sum += $1}END{print sum}' nm.log
স্বপ্ন: একটি টেম্পলেট সংকলক ক্যাশে
আমি মনে করি চূড়ান্ত সমাধানটি যদি আমরা এটি দিয়ে তৈরি করতে পারি:
g++ --template-cache myfile.o file1.cpp
g++ --template-cache myfile.o file2.cpp
এবং তারপরে myfile.o
ফাইলগুলির মধ্যে পূর্ববর্তী সংকলিত টেম্পলেটগুলি স্বয়ংক্রিয়ভাবে পুনরায় ব্যবহার করা হবে।
এর অর্থ হল আপনার বিল্ড সিস্টেমে অতিরিক্ত সিএলআই বিকল্পটি পাস করার পাশাপাশি প্রোগ্রামারগুলিতে 0 অতিরিক্ত প্রচেষ্টা করা।
সুস্পষ্ট টেম্পলেট ইনস্ট্যান্টিয়েশনের একটি গৌণ বোনাস: সহায়তা IDE গুলি তালিকা টেম্পলেট ইনস্ট্যান্টেশন
আমি খুঁজে পেয়েছি যে কিছু আইডিই যেমন একটিগ্রহের সমাধান করতে পারে না "ব্যবহৃত সমস্ত টেম্পলেট ইনস্ট্যান্টের একটি তালিকা"।
সুতরাং উদাহরণস্বরূপ, আপনি যদি একটি টেম্পলেট কোডের অভ্যন্তরে থাকেন এবং আপনি এই টেমপ্লেটের সম্ভাব্য মানগুলি খুঁজে পেতে চান তবে আপনাকে কনস্ট্রাক্টরের একের পর এক ব্যবহার ব্যবহার করতে হবে এবং সম্ভাব্য প্রকারগুলি একের পর এক করে করতে হবে।
তবে এক্সিলিপ ২০২০-০৩-তে আমি ক্লাসের নাম অনুসন্ধান করে সমস্ত ব্যবহার (সিটিআরএল + আল্ট + জি) অনুসন্ধান করে খুব সহজেই তাত্ক্ষণিক টেম্পলেটগুলি তালিকাভুক্ত করতে পারি, যা আমাকে উদাহরণস্বরূপ নির্দেশ করে:
template <class T>
struct AnimalTemplate {
T animal;
AnimalTemplate(T animal) : animal(animal) {}
std::string noise() {
return animal.noise();
}
};
প্রতি:
template class AnimalTemplate<Dog>;
এখানে একটি ডেমো রয়েছে: https://github.com/cirosantilli/ide-test-projects/blob/e1c7c6634f2d5cdeafd2bdc79bcfbb2057cb04c4/cpp/animal_template.hpp#L15
আপনি IDE এর বাইরে অন্য গেরিলা কৌশল ব্যবহার করতে পারেন তবে তা nm -C
চূড়ান্ত সম্পাদনযোগ্য এবং চালানো টেমপ্লেটের নামটি গ্রেপ করতে হবে :
nm -C main.out | grep AnimalTemplate
যা প্রত্যক্ষভাবে নির্দেশ করে যে Dog
এটি অন্যতম একটি ইনস্ট্যান্টেশন ছিল:
0000000000004dac W AnimalTemplate<Dog>::noise[abi:cxx11]()
0000000000004d82 W AnimalTemplate<Dog>::AnimalTemplate(Dog)
0000000000004d82 W AnimalTemplate<Dog>::AnimalTemplate(Dog)