OP 実装を C++ で作成する - 2.5 日本語

Vitis AI ライブラリ ユーザー ガイド (UG1354)

Document ID
UG1354
Release Date
2022-06-15
Version
2.5 日本語
  1. my_add_op.cpp に C++ クラスを作成します。ソース ファイルまたはクラスには命名規則の要件はありません。
    // in my_add_op.cpp
    class MyAddOp {
    };
  2. 次のコード スニペットに示すようにコンストラクター関数を作成します。
    #include <vart/op_imp.h>
    
    class MyAddOp {
        MyAddOp(const xir::Op* op1, xir::Attrs* attrs) : op{op1} {
          // op and attrs is not in use.
    }
    public:
        const xir::Op * const op;
    };
    注記: MyAddOp には、op という名前の public メンバー変数が必要です。op は、コンストラクター関数の最初の入力引数 (例: op1) で初期化されます。これは、DEF_XIR_OP_IMP の要件です。
  3. 次のコード スニペットに示すようにメンバー関数 calculate を作成します。
    class MyAddOp {
      ...
      int calculate(vart::simple_tensor_buffer_t output,
                    std::vector<vart::simple_tensor_buffer_t<float>> inputs) {
        for (auto i = 0u; i < output.mem_size / sizeof(float); ++i) {
          output.data[i] = 0.0f;
          for (auto input : inputs) {
            output.data[i] = output.data[i] + input.data[i];
          }
        }
        return 0;
      }
    ...
    }
  4. ソース ファイルをコンパイルします。
    % g++ -fPIC -std=c++17 -c -o  /tmp/my_add_op.o -Wall -Werror -I ~/.local/Ubuntu.18.04.x86_64.Debug/include/ my_add_op.cpp
    注記: C++ 17 以降を使用してください。共有ライブラリをビルドするには、-fPIC を有効にします。Vitis AI ライブラリは ~/.local/Ubuntu.18.04.x86_64.Debug にインストールされているものとします。
  5. 共有ライブラリにリンクするには、次のコードを使用します。
    % mkdir -p /tmp/lib;
    % g++ -Wl,--no-undefined -shared -o /tmp/lib/libvart_op_imp_add.so /tmp/my_add_op.o -L ~/.local/Ubuntu.18.04.x86_64.Debug/lib -lglog -lvitis_ai_library-runner_helper -lvart-runner -lxir

    Makefile を使用してライブラリのコンパイルとリンクを実行することもできます。次のコード スニペットに、makefile の例を示します。

    OUTPUT_DIR = $(HOME)/build/customer_op
    
    all: $(OUTPUT_DIR) $(OUTPUT_DIR)/libvart_op_imp_add.so
    
    $(OUTPUT_DIR):
        mkdir -p $@
    
    $(OUTPUT_DIR)/my_add_op.o: my_add_op.cpp
        $(CXX) -std=c++17 -fPIC -c -o $@ -I. -I=/install/Debug/include -Wall  -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 $<
    
    $(OUTPUT_DIR)/libvart_op_imp_add.so:  $(OUTPUT_DIR)/my_add_op.o
        $(CXX) -Wl,--no-undefined -shared -o $@ $+ -L=/install/Debug/lib  -lglog -lvitis_ai_library-runner_helper -lvart-runner -lxir
    
  6. OP 実装をテストするため、次に示すサンプル XIR グラフを作成します。
    % ipython;
    import xir
    g = xir.Graph("simple_graph")
    a = g.create_op("a", "data", {"shape": [1,2,2,4], "data_type": "FLOAT32"});
    b = g.create_op("b", "data", {"shape": [1,2,2,4], "data_type": "FLOAT32"});
    add = g.create_op("add_op", "add",  {"shape": [1,2,2,4], "data_type": "FLOAT32"}, {"input": [a,b]})
    root = g.get_root_subgraph()
    root.create_child_subgraph()
    user_subgraph = root.merge_children(set([g.get_leaf_subgraph(a), g.get_leaf_subgraph(b)]))
    cpu_subgraph = root.merge_children(set([g.get_leaf_subgraph(add)]))
    user_subgraph.set_attr("device", "USER")
    cpu_subgraph.set_attr("device", "CPU")
    g.serialize("/tmp/add.xmodel")
  7. サンプル入力ファイルを作成します。
    % cd /tmp
    % mkdir -p ref
    % ipython
    import numpy as np
    a = np.arange(1, 17, dtype=np.float32)
    b = np.arange(1, 17, dtype=np.float32)
    a.tofile("ref/a.bin")
    b.tofile("ref/b.bin")
    c = a + b
    c.tofile("ref/c.bin")
    % cd /tmp
    % mkdir -p /tmp/dump
    % env LD_LIBRARY_PATH=$HOME/.local/Ubuntu.18.04.x86_64.Debug/lib:/tmp/lib $HOME/.local/Ubuntu.18.04.x86_64.Debug/share/vitis_ai_library/test/cpu_task/test_op_imp --graph /tmp/add.xmodel --op "add_op"
    注記: 作成した共有ライブラリを CPU ランナーが検出できるように、検索パス LD_LIBRARY_PATH/tmp/lib を追加します。
    重要: 共有ライブラリの名前は libvart_op_imp_<YOUR_OP_TYPE>.so とする必要があります。CPU ランナーは、この命名規則を使用してカスタム xir::Op 実装を検出します。

    xdputil run_op を使用して op を検証することもできます。

    root@xilinx-zcu102-2021_2:~/add_op# xdputil run_op add.xmodel add_op -r ref -d dump
    WARNING: Logging before InitGoogleLogging() is written to STDERR
    I1202 09:32:41.497661  1208 test_op_run.cpp:79] try to test op: add_op
    I1202 09:32:41.497745  1208 test_op_run.cpp:97]  input op: a tensor: a
    I1202 09:32:41.497768  1208 test_op_run.cpp:97]  input op: b tensor: b
    I1202 09:32:41.497865  1208 test_op_run.cpp:55] read ref/a.bin to 0xaaab17d605d0 size=64
    I1202 09:32:41.497917  1208 test_op_run.cpp:55] read ref/b.bin to 0xaaab17c549b0 size=64
    I1202 09:32:41.498561  1208 test_op_run.cpp:114] graph name:simple_graphtesting op: {
        {args: input= TensorBuffer{@0xaaab17ba9b90,tensor=xir::Tensor{name = a, type = FLOAT32, shape = {1, 2, 2, 4}},location=HOST_VIRT,data=[(Virt=0xaaab17d605d0, 64)]} TensorBuffer{@0xaaab17e2a860,tensor=xir::Tensor{name = b, type = FLOAT32, shape = {1, 2, 2, 4}},location=HOST_VIRT,data=[(Virt=0xaaab17c549b0, 64)]}}
    {
    I1202 09:32:41.499586  1208 test_op_run.cpp:68] write output to dump/add_op.bin from 0xaaab17de7090 size=64
    test pass
  8. これをリファレンス結果と比較し、op が正しく実装されたかを検証します。
    % diff -u <(xxd ref/c.bin) <(xxd dump/add_op.bin)
    % xxd ref/c.bin
    % xxd dump/add_op.bin