আমার কখন একটি কম্পাইলার প্লাগইন তৈরি করা উচিত?
LiteRT ফ্রেমওয়ার্কের সাথে কম্পাইলার নির্ভরতার সাথে একটি নির্দিষ্ট হার্ডওয়্যার অ্যাক্সিলারেটর সংহত করার প্রয়োজন হলে একটি LiteRT কম্পাইলার প্লাগইন প্রয়োজন।
আপনার একটি কম্পাইলার প্লাগইন তৈরি করা উচিত যদি:
- আপনি একটি নতুন হার্ডওয়্যার ব্যাকএন্ডকে টার্গেট করছেন যা সমর্থিত নয়।
- কর্মক্ষমতা বা শক্তি দক্ষতার জন্য আপনি নির্দিষ্ট মডেলের ক্রিয়াকলাপগুলিকে সেই হার্ডওয়্যার অ্যাক্সিলারেটরে অফলোড করতে চান।
- আপনার AOT কম্পাইলেশন (ওয়ার্কস্টেশনে) অথবা অন-ডিভাইস কম্পাইলেশনের জন্য সমর্থন প্রয়োজন।
প্লাগইনটি একটি সেতু হিসেবে কাজ করে, মেশিন লার্নিং মডেলের কিছু অংশ গ্রহণ করে এবং ব্যাকএন্ডের কম্পাইলারে কল করে আপনার টার্গেট হার্ডওয়্যার কার্যকর করতে পারে এমন একটি ফর্ম্যাটে রূপান্তর করে। LiteRT প্লাগইন দ্বারা তৈরি কাস্টম বাইটকোডকে .tflite মডেলের সাথে একত্রিত করে, যা LiteRT রানটাইম ব্যবহার করে এটিকে এক্সিকিউটেবল করে তোলে।
কম্পাইলার প্লাগইন কিভাবে কাজ করে?
LiterRT ফ্রেমওয়ার্ক মডেল লোডিং বা অফলাইন প্রি-প্রসেসিং পর্যায়ে কম্পাইলার প্লাগইন ব্যবহার করে টার্গেট হার্ডওয়্যারে কার্যকর করার জন্য মডেল সাবগ্রাফ সনাক্ত এবং প্রস্তুত করে।
এই প্রক্রিয়াটিতে প্লাগইনের এক্সপোর্ট করা ফাংশন ব্যবহার করে ফ্রেমওয়ার্ক দ্বারা পরিচালিত দুটি প্রধান পর্যায় জড়িত:
- পার্টিশনিং: প্লাগইনটি সম্পূর্ণ মডেল গ্রাফটি পরিদর্শন করে এবং এমন ক্রিয়াকলাপের উপসেটগুলি সনাক্ত করে যা এটি সমর্থন করে এবং লক্ষ্য হার্ডওয়্যারে দক্ষতার সাথে ত্বরান্বিত করতে পারে। এই সমর্থিত সাবগ্রাফগুলি সংকলনের জন্য "পার্টিশন" (চিহ্নিত) এবং রূপরেখাযুক্ত।
- সংকলন: LiterRT ফ্রেমওয়ার্ক পার্টিশন করা সাবগ্রাফগুলিকে প্লাগইনে ফেরত পাঠায়। প্লাগইনটি তখন তার অভ্যন্তরীণ লজিক এবং সম্ভবত বহিরাগত টুলচেইন (কম্পাইলার) ব্যবহার করে পার্টিশন বাস্তবায়নের জন্য এক বা একাধিক হার্ডওয়্যার-নির্দিষ্ট বাইটকোড মডিউল তৈরি করে। এই বাইটকোডটিই লক্ষ্য হার্ডওয়্যারের রানটাইম (HAL/ড্রাইভার) অবশেষে লোড এবং এক্সিকিউট করবে।
ফ্রেমওয়ার্কটি মূল সাবগ্রাফগুলিকে কাস্টম অপারেশন দিয়ে প্রতিস্থাপন করে যা হার্ডওয়্যার ড্রাইভারকে আহ্বান করে, প্লাগইন দ্বারা তৈরি কম্পাইল করা বাইটকোড বরাবর।
LiterRT Dispatch হল কম্পাইলার প্লাগইনের রানটাইম অ্যানালগ। তারা HAL প্রদত্ত কম্পাইলার আউটপুটে কল করার মাধ্যম প্রদান করে। আরও বিস্তারিত জানার জন্য, ডিসপ্যাচ ডকুমেন্টেশন দেখুন।
AOT বনাম অন-ডিভাইস
LiterRT আমাদের টুলিং এর মাধ্যমে AOT কম্পাইলেশন সমর্থন করার জন্য কম্পাইলার প্লাগইন ব্যবহার করতে পারে, সেইসাথে অন-ডিভাইস কম্পাইলেশনও ব্যবহার করতে পারে। অন-ডিভাইস কম্পাইলেশন আরও নমনীয়, LiterT রানটাইম API এর মধ্যে সম্পূর্ণরূপে অভ্যন্তরীণ এবং শুধুমাত্র একটি মডেলের ব্যবস্থাপনার প্রয়োজন হয়। AOT ফ্লো যখন কম্পাইলেশনটি ডিভাইসে চালানোর জন্য খুব বেশি রিসোর্স-ইনটেনসিভ হয় তখন তা আনব্লক করতে পারে যা অনেক সমসাময়িক বৃহৎ মডেলের ক্ষেত্রে হতে পারে।
ফলব্যাক
LiterRT ভিন্ন ভিন্ন গ্রাফের জন্য সমর্থন সহ তৈরি। প্লাগইন দ্বারা নির্বাচিত না হওয়া যেকোনো অপারেশন CPU-তে ছেড়ে দেওয়া হবে অথবা অন্য ব্যাকএন্ডে ত্বরণের জন্য উপলব্ধ করা হবে।
একটি কম্পাইলার প্লাগইন বাস্তবায়ন
একটি LiterT কম্পাইলার প্লাগইন একটি শেয়ার্ড লাইব্রেরি হিসেবে বাস্তবায়িত হয় যা LiterT C API-তে সংজ্ঞায়িত C ফাংশনের একটি নির্দিষ্ট সেট রপ্তানি করে।
প্রয়োজনীয় ইন্টারফেস ফাংশন
মূল কার্যকারিতা দুটি মূল সংকলন ধাপের চারপাশে আবর্তিত হয়: LiteRtCompilerPluginPartition এবং LiteRtCompilerPluginCompile ।
| ফাংশন | উদ্দেশ্য |
|---|---|
| LiteRtCompilerPluginPartition সম্পর্কে | একটি প্রদত্ত মডেল সাবগ্রাফের ( পার্টিশন ধাপ) মধ্যে সমস্ত সমর্থিত ক্রিয়াকলাপ নির্বাচন করে এবং চিহ্নিত করে। |
| LiterRtCompilerPluginCompile$ সম্পর্কে | পূর্বে নির্বাচিত পার্টিশনের জন্য হার্ডওয়্যার-নির্দিষ্ট বাইটকোড তৈরি করে ( কম্পাইল ধাপ)। |
সি এপিআই স্নিপেটস
// Name associated with the manufacturer this plugin relates to.
LITERT_CAPI_EXPORT const char* LiteRtGetCompilerPluginSocManufacturer();
// Create and initialize the plugin instance.
LITERT_CAPI_EXPORT LiteRtStatus
LiteRtCreateCompilerPlugin(LiteRtCompilerPlugin* compiler_plugin,
LiteRtEnvironmentOptions env, LiteRtOptions options);
// Choose ops for compilation.
// This is the PARTITION step.
LITERT_CAPI_EXPORT LiteRtStatus LiteRtCompilerPluginPartition(
LiteRtCompilerPlugin compiler_plugin, const char* soc_model,
LiteRtSubgraph subgraph, LiteRtOpList selected_ops);
// Prepare result to pass to the runtime for given model containing partitioned
// subgraphs. This is the COMPILE step.
LITERT_CAPI_EXPORT LiteRtStatus LiteRtCompilerPluginCompile(
LiteRtCompilerPlugin compiler_plugin, const char* soc_model,
LiteRtModel partitions, LiteRtCompiledResult* compiled_result);
১. পার্টিশন ফাংশন
ফাংশন স্বাক্ষরটি হল:
LITERT_CAPI_EXPORT LiteRtStatus LiteRtCompilerPluginPartition(
LiteRtCompilerPlugin compiler_plugin, const char* soc_model,
LiteRtSubgraph subgraph, LiteRtOpList selected_ops);
partition ফাংশনটি কী করে: এটি নির্বাচনের পর্যায়। প্লাগইনটি ইনপুট LiteRtSubgraph এ ক্রিয়াকলাপগুলির উপর পুনরাবৃত্তি করে। টার্গেট হার্ডওয়্যার সমর্থন করে এবং ত্বরান্বিত করতে পারে এমন প্রতিটি ক্রিয়াকলাপের জন্য, প্লাগইনটি selected_ops প্যারামিটারে প্রদত্ত LiteRtOpList$-এ সেই ক্রিয়াকলাপটি যুক্ত করে । LiteRt ফ্রেমওয়ার্কটি চূড়ান্ত সংকলন ধাপের জন্য পাঠানো পার্টিশনের সীমানা নির্ধারণ করতে এই তালিকাটি ব্যবহার করে।
ডিফল্টরূপে, LiterRT সমস্ত নির্বাচিত অপশনগুলিকে সম্ভাব্য বৃহত্তম সাব-DAG-তে গোষ্ঠীভুক্ত করবে। আরও সূক্ষ্মভাবে বিভক্ত করার জন্য, অপশন নির্বাচন করার সময় একটি সূচক যুক্ত করা যেতে পারে যা এই উপগ্রাফগুলিকে আরও বিভক্ত করতে কাজ করে।
2. কম্পাইল ফাংশন
ফাংশন স্বাক্ষরটি হল:
LITERT_CAPI_EXPORT LiteRtStatus LiteRtCompilerPluginCompile(
LiteRtCompilerPlugin compiler_plugin, const char* soc_model,
LiteRtModel partitions, LiteRtCompiledResult* compiled_result);
compile ফাংশন কী করে: এটি জেনারেশন ফেজ। ইনপুট partitions এমন একটি মডেল উপস্থাপন করে যেখানে সমস্ত নির্বাচিত সাবগ্রাফ বিচ্ছিন্ন করা হয়েছে। প্লাগইন এই পার্টিশনগুলি প্রক্রিয়া করে, লক্ষ্য হার্ডওয়্যারের জন্য বাইটকোড তৈরি করার জন্য তার নির্দিষ্ট টুলচেইন ব্যবহার করে। আশা করা যায় যে প্লাগইনের আউটপুট সংকলনের জন্য পাস করা প্রতিটি সাবগ্রাফের জন্য একটি এন্ট্রি পয়েন্ট প্রদান করবে। বেশিরভাগ ক্ষেত্রে এটি হয় প্রতিটি ইনপুট সাবগ্রাফের জন্য পৃথক বাইট কোড মডিউল, অথবা একাধিক এন্ট্রি পয়েন্ট সহ একটি একক বাইট কোড মডিউল।
compile দ্বারা ফেরত দেওয়া ডেটার ধরণ: LiteRtCompilerPluginCompile ফাংশনটি আউট-প্যারামিটার LiteRtCompiledResult ব্যবহার করে তার আউটপুট ফেরত দেয়।
LiteRtCompiledResult হল প্লাগইন দ্বারা পরিচালিত একটি কাঠামোর একটি অস্বচ্ছ (LiterRT এর সাপেক্ষে) হ্যান্ডেল। এটি সংকলনের আউটপুট উপস্থাপন করে এবং দুটি প্রধান তথ্য ধারণ করে:
- বাইট কোড মডিউল: হার্ডওয়্যার-নির্দিষ্ট এক্সিকিউটেবল বাইটকোড (অর্থাৎ, সংকলিত নির্দেশাবলী) ধারণকারী এক বা একাধিক কাঁচা মেমরি বাফার।
- কল তথ্য: প্রতিটি পার্টিশনের জন্য মেটাডেটা। এটি ইনপুট সাবগ্রাফ থেকে
iফলাফল বাইট কোড মডিউল এবং সেই মডিউলে প্রবেশ বিন্দু শনাক্তকারীর ম্যাপিং প্রদান করে।
উদাহরণ বাস্তবায়ন
নিম্নলিখিত স্নিপেটগুলি ব্যাখ্যা করে যে কীভাবে একটি মৌলিক প্লাগইন মূল ফাংশনগুলি বাস্তবায়ন করতে পারে। এই উদাহরণটি litert/vendors/examples/ এর একটি সম্পূর্ণ কার্যকরী উদাহরণ থেকে নেওয়া হয়েছে।
প্লাগইন সনাক্তকরণ এবং সেটআপ
এই ফাংশনগুলি প্লাগইন এবং হার্ডওয়্যার সম্পর্কে মৌলিক তথ্য সহ ফ্রেমওয়ার্ক সরবরাহ করে।
// Define the plugin's internal state structure
struct LiteRtCompilerPluginT {};
// Identify the manufacturer
const char* LiteRtGetCompilerPluginSocManufacturer() {
return "AcmeCorp"; // Example manufacturer name
}
// Specify the supported hardware (in this example, it supports kLiteRtHwAcceleratorNpu)
LiteRtStatus LiteRtGetCompilerPluginSupportedHardware(
LiteRtCompilerPlugin compiler_plugin,
LiteRtHwAccelerators* supported_hardware) {
// ... argument checking ...
*supported_hardware = kLiteRtHwAcceleratorNpu;
return kLiteRtStatusOk;
}
পার্টিশন লজিক ( LiteRtCompilerPluginPartition )
এই উদাহরণে দেখানো হয়েছে যে প্লাগইনটি সীমিত সংখ্যক অপারেশন ( mul , sub , এবং একটি নির্দিষ্ট কম্পোজিট op) নির্বাচন করে শুধুমাত্র যদি সমস্ত ইনপুট এবং আউটপুট 32 বিট ফ্লোট হয়। সাধারণত একটি অপারেশন নির্বাচন করা উচিত কিনা তা নির্ধারণের ক্ষেত্রে ব্যাকএন্ডের কম্পাইলার টুলচেইনে একটি বৈধতা হুকে কল করা অন্তর্ভুক্ত থাকে।
LiteRtStatus LiteRtCompilerPluginPartition(LiteRtCompilerPlugin compiler_plugin,
const char* soc_model,
LiteRtSubgraph subgraph,
LiteRtOpList selected_ops) {
// Iterate over ops and check criteria for selection
// (using a C++ wrapper namespace '::litert' for convenience).
// `subgraph` is a single subgraph from the original model, as such
// this function will be called for each subgraph in the original model.
::litert::Subgraph main_subgraph(subgraph);
for (const auto& op : main_subgraph.Ops()) {
// 1. Check a constraint: require all tensors to be Float32
bool only_f32 = true;
// ... logic to check input/output types ...
if (!only_f32) {
continue;
}
// 2. Check op codes and push to selected_ops list
if (op.Code() == kLiteRtOpCodeTflMul) {
LITERT_RETURN_IF_ERROR(LiteRtPushOp(selected_ops, op.Get(), 0));
} else if (op.Code() == kLiteRtOpCodeTflSub) {
LITERT_RETURN_IF_ERROR(LiteRtPushOp(selected_ops, op.Get(), 0));
} else if (op.Code() == kLiteRtOpCodeShloComposite) {
// Example of checking composite op options
// ... logic to check for "odml.rms_norm" name ...
LITERT_RETURN_IF_ERROR(LiteRtPushOp(selected_ops, op.Get(), 0));
}
}
return kLiteRtStatusOk;
}
সংকলন কল করার আগে, LiterRT একটি নতুন মধ্যবর্তী মডেলে নির্বাচিত সমস্ত অপগুলিকে যাচাই করবে এবং নতুন সাবগ্রাফে "রূপরেখা" তৈরি করবে। এই ইন্টারমিডাইট মডেলটিই সংকলনে স্থানান্তরিত হয়।
সংকলন যুক্তি ( LiteRtCompilerPluginCompile )
এই ফাংশনটি পার্টিশন করা সাবগ্রাফগুলি নেয় এবং একটি কাস্টম LiteRtCompiledResult তৈরি করে। এই উদাহরণটি প্রতিটি পার্টিশন কম্পাইল করার জন্য একটি স্বতন্ত্র বাইটকোড মডিউল তৈরি করে। বাস্তব ক্ষেত্রে, এর জন্য সাধারণত LiteRT অপসকে ব্যাকএন্ড কম্পাইলার লাইব্রেরিতে টাইপে রূপান্তর করা হয়। কার্যকরী উদাহরণ প্লাগইনের "সংকলন" একটি মানব পাঠযোগ্য স্ট্রিং তৈরি করে যা গ্রাফটিকে এনকোড করে।
// Internal structure defining the compiled output
struct LiteRtCompiledResultT {
std::vector<std::string> byte_code; // The hardware bytecode buffers
std::vector<std::string> per_op_data; // Per-call metadata (CallInfo)
};
LiteRtStatus LiteRtCompilerPluginCompile(
LiteRtCompilerPlugin compiler_plugin, const char* soc_model,
LiteRtModel partitions, LiteRtCompiledResult* compiled_result) {
// 1. Create the internal result structure
auto model = litert::Model::CreateFromNonOwnedHandle(partitions);
const auto num_partitions = model.NumSubgraphs();
auto result = std::make_unique<LiteRtCompiledResultT>();
result->byte_code.resize(num_partitions);
result->per_op_data.resize(num_partitions);
// 2. Iterate and compile each partition
for (auto i = 0; i < num_partitions; ++i) {
// CompileSinglePartition is an internal helper that converts the subgraph
// into the target hardware's format and stores it in result->byte_code.
// In the case of the example this is just a stringification of the graph.
// ... internal call to CompileSinglePartition ...
// Example: result.byte_code[i] = generated_hw_code;
// Example: result.per_op_data[i] = absl::StrFormat("Partition_%d", i);
// The "per_op_data" is a unique identifier associated to the `ith` partition.
// This is analogous to the name of a function in a library.
// This is only meaningful when the plugin is preparing single modules with multiple entry points.
}
// 3. Pass ownership of the result back to the framework
*compiled_result = result.release();
return kLiteRtStatusOk;
}
// Functions to expose the compiled result data to the framework
LiteRtStatus LiteRtGetCompiledResultByteCode(
LiteRtCompiledResult compiled_result, LiteRtParamIndex byte_code_idx,
const void** byte_code, size_t* byte_code_size) {
// ... implementation reads from compiled_result->byte_code ...
}
// ... other LiteRtGetCompiledResult* functions ...
ব্যবহার এবং বৈধতা
LiterRT মডেল ফাইলগুলিতে কম্পাইলার প্লাগইন প্রয়োগ, ফলাফল কার্যকর করা এবং যাচাই/বেঞ্চমার্কিং করার জন্য বিভিন্ন সরঞ্জাম সরবরাহ করে। অ্যাক্সিলারেটর টেস্ট স্যুট ডকুমেন্টেশন এবং বেঞ্চমার্কিং এবং প্রোফাইলিং ডকুমেন্টেশন দেখুন।