หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

ใช้การรวบรวม AOT

tfcompile คืออะไร?

tfcompile เป็นเครื่องมือแบบสแตนด์อโลนที่ล่วงหน้า (AOT) รวบรวมกราฟ TensorFlow เป็นรหัสปฏิบัติการ สามารถลดขนาดไบนารีทั้งหมดและหลีกเลี่ยงค่าใช้จ่ายรันไทม์บางอย่าง กรณีการใช้งานทั่วไปของ tfcompile คือการรวบรวมกราฟการอนุมานลงในโค้ดปฏิบัติการสำหรับอุปกรณ์มือถือ

โดยปกติแล้วกราฟ TensorFlow จะดำเนินการโดยรันไทม์ TensorFlow สิ่งนี้ทำให้เกิดค่าใช้จ่ายรันไทม์บางส่วนสำหรับการดำเนินการของแต่ละโหนดในกราฟ สิ่งนี้ยังนำไปสู่ขนาดไบนารีรวมที่ใหญ่ขึ้นเนื่องจากต้องมีโค้ดสำหรับรันไทม์ TensorFlow นอกเหนือจากกราฟ โค้ดปฏิบัติการที่สร้างโดย tfcompile ไม่ได้ใช้รันไทม์ TensorFlow และมีการอ้างอิงเฉพาะกับเคอร์เนลที่ใช้จริงในการคำนวณ

คอมไพเลอร์ถูกสร้างขึ้นบนเฟรมเวิร์ก XLA รหัสเชื่อมโยง TensorFlow กับเฟรมเวิร์ก XLA อยู่ภายใต้ เทนเซอร์โฟลว์ / คอมไพเลอร์

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 เพื่อคอมไพล์กราฟย่อย

ขั้นตอนนี้จะแปลงกราฟเป็น 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 / compiler / aot / testing / BUILD

ค่าคงที่ที่แสดงในกราฟย่อยที่คอมไพล์แล้วจะถูกคอมไพล์โดยตรงในโค้ดที่สร้างขึ้น ในการส่งผ่านค่าคงที่ไปยังฟังก์ชันที่สร้างขึ้นแทนที่จะรวบรวมไว้ในนั้นให้ส่งผ่านค่าเหล่านั้นเป็นฟีด

สำหรับรายละเอียดเกี่ยวกับ tf_library สร้างแมโครดู tfcompile.bzl

สำหรับรายละเอียดเกี่ยวกับเครื่องมือ tfcompile อ้างอิงโปรดดูที่ tfcompile_main.cc

ขั้นตอนที่ 3: เขียนโค้ดเพื่อเรียกใช้กราฟย่อย

ขั้นตอนนี้ใช้ไฟล์ส่วนหัว ( test_graph_tfmatmul.h ) ที่สร้างโดยแมโครสร้าง tf_library ในขั้นตอนก่อนหน้าเพื่อเรียกใช้โค้ดที่สร้างขึ้น ไฟล์ส่วนหัวจะอยู่ใน bazel-bin ตรงกับแพ็กเกจ build และตั้งชื่อตามแอ็ตทริบิวต์ name ที่ตั้งค่าบนแมโคร 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 คลาสที่สร้างขึ้นทั้งหมดมี API ที่คล้ายกันโดยมีข้อแตกต่างเพียงอย่างเดียวคือวิธีจัดการบัฟเฟอร์อาร์กิวเมนต์และผลลัพธ์ วิธีการเหล่านี้แตกต่างกันไปตามจำนวนและประเภทของบัฟเฟอร์ซึ่งระบุโดย feed และ fetch อาร์กิวเมนต์ไปยังมาโคร tf_library

มีบัฟเฟอร์สามประเภทที่จัดการภายในคลาสที่สร้างขึ้น: args แสดงถึงอินพุต results แสดงถึงเอาต์พุตและ temps แสดงถึงบัฟเฟอร์ชั่วคราวที่ใช้ภายในเพื่อทำการคำนวณ โดยค่าเริ่มต้นแต่ละอินสแตนซ์ของคลาสที่สร้างขึ้นจะจัดสรรและจัดการบัฟเฟอร์เหล่านี้ทั้งหมดให้คุณ อาร์กิวเมนต์ตัวสร้าง AllocMode อาจถูกใช้เพื่อเปลี่ยนลักษณะการทำงานนี้ บัฟเฟอร์ทั้งหมดอยู่ในแนวเดียวกันกับขอบเขต 64 ไบต์

คลาส C ++ ที่สร้างขึ้นเป็นเพียง Wrapper รอบ ๆ โค้ดระดับต่ำที่สร้างโดย 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",
    ]
)