ما هو tfcompile؟
tfcompile
هو أداة مستقل أن (عثمان) بتجميع TensorFlow الرسوم البيانية في قانون قابل للتنفيذ قبل من الوقت. يمكنه تقليل الحجم الإجمالي الثنائي ، وكذلك تجنب بعض النفقات العامة لوقت التشغيل. A استخدام حالة نموذجية من tfcompile
هو تجميع الرسم البياني الاستدلال إلى رمز قابل للتنفيذ للهواتف النقالة.
عادةً ما يتم تنفيذ الرسم البياني TensorFlow بواسطة وقت تشغيل TensorFlow. هذا يتطلب بعض الحمل الزائد لوقت التشغيل لتنفيذ كل عقدة في الرسم البياني. يؤدي هذا أيضًا إلى حجم ثنائي إجمالي أكبر ، نظرًا لأن رمز وقت تشغيل TensorFlow يجب أن يكون متاحًا ، بالإضافة إلى الرسم البياني نفسه. رمز قابل للتنفيذ التي تنتجها tfcompile
لا يستخدم وقت TensorFlow، ولها تبعيات على حبات التي تستخدم فعلا في حساب فقط.
تم بناء المترجم فوق إطار عمل XLA. رمز سد TensorFlow ليتواجد إطار XLA تحت tensorflow / مترجم .
ماذا يفعل tfcompile؟
tfcompile
يأخذ رسم بياني ثانوي، التي حددها المفاهيم TensorFlow الأعلاف والجلب، ويولد وظيفة التي تنفذ هذا رسم بياني ثانوي. و feeds
هي الحجج إدخال الدالة، و fetches
هي الحجج الناتج عن وظيفة. يجب أن يتم تحديد جميع المدخلات بالكامل من قبل الأعلاف ؛ لا يمكن أن يحتوي الرسم البياني الفرعي الناتج على عنصر نائب أو عقد متغيرة. من الشائع تحديد جميع العناصر النائبة والمتغيرات على هيئة موجزات ، مما يضمن عدم احتواء الرسم البياني الفرعي الناتج على هذه العقد. يتم حزم وظيفة ولدت كما cc_library
، مع ملف رأس تصدير التوقيع وظيفة، وملف الكائن الذي يحتوي على التنفيذ. يكتب المستخدم رمزًا لاستدعاء الوظيفة المولدة بالشكل المناسب.
باستخدام tfcompile
تفاصيل هذا القسم الخطوات رفيعة المستوى لتوليد ثنائي قابل للتنفيذ مع tfcompile
من رسم بياني ثانوي TensorFlow. الخطوات هي:
- الخطوة 1: تكوين الرسم البياني الفرعي للترجمة
- الخطوة 2: استخدام
tf_library
بناء ماكرو لتجميع رسم بياني ثانوي - الخطوة 3: اكتب رمزًا لاستدعاء الرسم البياني الفرعي
- الخطوة 4: قم بإنشاء الملف الثنائي النهائي
الخطوة 1: تكوين الرسم البياني الفرعي للترجمة
حدد الخلاصات وعمليات الجلب التي تتوافق مع وسيطات الإدخال والإخراج للوظيفة المُنشأة. ثم تكوين feeds
و fetches
في tensorflow.tf2xla.Config
بروتو.
# Each feed is a positional input argument for the generated function. The order
# of each entry matches the order of each input argument. Here “x_hold” and “y_hold”
# refer to the names of placeholder nodes defined in the graph.
feed {
id { node_name: "x_hold" }
shape {
dim { size: 2 }
dim { size: 3 }
}
}
feed {
id { node_name: "y_hold" }
shape {
dim { size: 3 }
dim { size: 2 }
}
}
# Each fetch is a positional output argument for the generated function. The order
# of each entry matches the order of each output argument. Here “x_y_prod”
# refers to the name of a matmul node defined in the graph.
fetch {
id { node_name: "x_y_prod" }
}
الخطوة 2: استخدم tf_library build macro لتجميع الرسم البياني الفرعي
هذه الخطوة تحويل الرسم البياني إلى cc_library
باستخدام tf_library
بناء الماكرو. و cc_library
يتكون من ملف الكائن الذي يحتوي على الشفرة التي تم إنشاؤها من الرسم البياني، جنبا إلى جنب مع ملف الرأس الذي يتيح الوصول إلى الشفرة التي تم إنشاؤها. tf_library
متنفع tfcompile
لتجميع الرسم البياني TensorFlow إلى رمز قابل للتنفيذ.
load("//tensorflow/compiler/aot:tfcompile.bzl", "tf_library")
# Use the tf_library macro to compile your graph into executable code.
tf_library(
# name is used to generate the following underlying build rules:
# <name> : cc_library packaging the generated header and object files
# <name>_test : cc_test containing a simple test and benchmark
# <name>_benchmark : cc_binary containing a stand-alone benchmark with minimal deps;
# can be run on a mobile device
name = "test_graph_tfmatmul",
# cpp_class specifies the name of the generated C++ class, with namespaces allowed.
# The class will be generated in the given namespace(s), or if no namespaces are
# given, within the global namespace.
cpp_class = "foo::bar::MatMulComp",
# graph is the input GraphDef proto, by default expected in binary format. To
# use the text format instead, just use the ‘.pbtxt’ suffix. A subgraph will be
# created from this input graph, with feeds as inputs and fetches as outputs.
# No Placeholder or Variable ops may exist in this subgraph.
graph = "test_graph_tfmatmul.pb",
# config is the input Config proto, by default expected in binary format. To
# use the text format instead, use the ‘.pbtxt’ suffix. This is where the
# feeds and fetches were specified above, in the previous step.
config = "test_graph_tfmatmul.config.pbtxt",
)
لإنشاء بروتو GraphDef (test_graph_tfmatmul.pb) في هذا المثال، تشغيل make_test_graphs.py وتحديد موقع الإخراج مع العلم --out_dir.
الرسوم البيانية نموذجية تحتوي على Variables
التي تمثل الأوزان التي يتم تعلمها من خلال التدريب، ولكن tfcompile
لا يمكن تجميع رسم بياني ثانوي التي تحتوي على Variables
. و freeze_graph.py أداة تحويل المتغيرات إلى الثوابت، وذلك باستخدام القيم المخزنة في ملف نقطة تفتيش. للسهولة، و tf_library
ماكرو يدعم freeze_checkpoint
الحجة، التي تدير الأداة. لمزيد من الأمثلة ترى tensorflow / مترجم / عثمان / الاختبارات / BUILD .
الثوابت التي تظهر في الرسم البياني الفرعي المترجمة يتم تجميعها مباشرة في الكود المُنشأ. لتمرير الثوابت إلى الدالة المُولَّدة ، بدلاً من تجميعها ، ما عليك سوى تمريرها كموجز.
للاطلاع على تفاصيل tf_library
بناء ماكرو، انظر tfcompile.bzl .
للاطلاع على تفاصيل الكامنة tfcompile
الأداة، راجع tfcompile_main.cc .
الخطوة 3: اكتب رمزًا لاستدعاء الرسم البياني الفرعي
تستخدم هذه الخطوة ملف الرأس ( test_graph_tfmatmul.h
) التي تم إنشاؤها من قبل tf_library
بناء الماكرو في الخطوة السابقة لاستدعاء الشفرة التي تم إنشاؤها. يقع ملف الرأس في bazel-bin
دليل المقابلة لحزمة البناء، ويدعى استنادا إلى مجموعة سمة اسم على tf_library
بناء الماكرو. على سبيل المثال، رأس إنشاؤها من أجل test_graph_tfmatmul
سيكون test_graph_tfmatmul.h
. يوجد أدناه نسخة مختصرة لما تم إنشاؤه. الملف الذي تم إنشاؤه في bazel-bin
، يحتوي على تعليقات إضافية مفيدة.
namespace foo {
namespace bar {
// MatMulComp represents a computation previously specified in a
// TensorFlow graph, now compiled into executable code.
class MatMulComp {
public:
// AllocMode controls the buffer allocation mode.
enum class AllocMode {
ARGS_RESULTS_AND_TEMPS, // Allocate arg, result and temp buffers
RESULTS_AND_TEMPS_ONLY, // Only allocate result and temp buffers
};
MatMulComp(AllocMode mode = AllocMode::ARGS_RESULTS_AND_TEMPS);
~MatMulComp();
// Runs the computation, with inputs read from arg buffers, and outputs
// written to result buffers. Returns true on success and false on failure.
bool Run();
// Arg methods for managing input buffers. Buffers are in row-major order.
// There is a set of methods for each positional argument.
void** args();
void set_arg0_data(float* data);
float* arg0_data();
float& arg0(size_t dim0, size_t dim1);
void set_arg1_data(float* data);
float* arg1_data();
float& arg1(size_t dim0, size_t dim1);
// Result methods for managing output buffers. Buffers are in row-major order.
// Must only be called after a successful Run call. There is a set of methods
// for each positional result.
void** results();
float* result0_data();
float& result0(size_t dim0, size_t dim1);
};
} // end namespace bar
} // end namespace foo
ويطلق على الفئة التي تم إنشاؤها C ++ MatMulComp
في foo::bar
مساحة الاسم، لأن ذلك كان cpp_class
المحددة في tf_library
الماكرو. جميع الفئات التي تم إنشاؤها لها واجهة برمجة تطبيقات مماثلة ، مع الاختلاف الوحيد هو طرق التعامل مع الوسيطات الوسيطة والنتيجة. هذه الأساليب تختلف بناء على عدد وأنواع المخازن المؤقتة، التي تم تحديدها من قبل feed
و fetch
الحجج ل tf_library
الماكرو.
هناك ثلاثة أنواع من مخازن تدار ضمن الفئة التي تم إنشاؤها: args
تمثل المدخلات و results
تمثل المخرجات، و temps
يمثل المخازن المؤقتة المستخدمة داخليا لأداء الحساب. بشكل افتراضي ، يخصص كل مثيل من الفئة التي تم إنشاؤها ويدير كل هذه المخازن المؤقتة نيابة عنك. و AllocMode
يمكن أن تستخدم حجة منشئ لتغيير هذا السلوك. يتم محاذاة كافة المخازن المؤقتة إلى حدود 64 بايت.
فئة C ++ التي تم إنشاؤها هي مجرد غلاف حول رمز المستوى المنخفض الذي تم إنشاؤه بواسطة XLA.
مثال على استدعاء وظيفة ولدت على أساس tfcompile_test.cc
:
#define EIGEN_USE_THREADS
#define EIGEN_USE_CUSTOM_THREAD_POOL
#include <iostream>
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/compiler/aot/tests/test_graph_tfmatmul.h" // generated
int main(int argc, char** argv) {
Eigen::ThreadPool tp(2); // Size the thread pool as appropriate.
Eigen::ThreadPoolDevice device(&tp, tp.NumThreads());
foo::bar::MatMulComp matmul;
matmul.set_thread_pool(&device);
// Set up args and run the computation.
const float args[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
std::copy(args + 0, args + 6, matmul.arg0_data());
std::copy(args + 6, args + 12, matmul.arg1_data());
matmul.Run();
// Check result
if (matmul.result0(0, 0) == 58) {
std::cout << "Success" << std::endl;
} else {
std::cout << "Failed. Expected value 58 at 0,0. Got:"
<< matmul.result0(0, 0) << std::endl;
}
return 0;
}
الخطوة 4: قم بإنشاء الملف الثنائي النهائي
هذه الخطوة يجمع المكتبة التي تم إنشاؤها بواسطة tf_library
في الخطوة 2 وقانون مكتوب في الخطوة 3 لإنشاء ثنائي النهائي. فيما يلي مثال bazel
ملف BUILD.
# Example of linking your binary
# Also see //tensorflow/compiler/aot/tests/BUILD
load("//tensorflow/compiler/aot:tfcompile.bzl", "tf_library")
# The same tf_library call from step 2 above.
tf_library(
name = "test_graph_tfmatmul",
...
)
# The executable code generated by tf_library can then be linked into your code.
cc_binary(
name = "my_binary",
srcs = [
"my_code.cc", # include test_graph_tfmatmul.h to access the generated header
],
deps = [
":test_graph_tfmatmul", # link in the generated object file
"//third_party/eigen3",
],
linkopts = [
"-lpthread",
]
)