Vengineerの戯言

人生は短いけど、長いです。人生を楽しみましょう!

Intel nGraph で Halide はどのように使われているか?(その3)



cpu::op::HalideOpは、src/ngraph/runtime/cpu/op/halide_op.hpp にて宣言されています。
                class HalideOp : public ngraph::op::Op
                {
                public:
                    HalideOp(const NodeVector& args,
                             const std::list<std::shared_ptr<Node>>& ops,
                             const element::Type& out_type,
                             const Shape& out_shape);

                    virtual void validate_and_infer_types() override;

                    virtual std::shared_ptr<Node>
                        copy_with_new_args(const NodeVector& new_args) const override;

                    const std::list<std::shared_ptr<Node>>& get_ops() const { return m_ops; }
                private:
                    std::list<std::shared_ptr<Node>> m_ops;
                    element::Type m_output_type;
                    Shape m_output_shape;
                };

実装は、src/ngraph/runtime/cpu/op/halide_op.cpp
shared_ptr<Node> runtime::cpu::op::HalideOp::copy_with_new_args(const NodeVector& new_args) const
{
    return make_shared<HalideOp>(new_args, m_ops, m_output_type, m_output_shape);
}

runtime::cpu::op::HalideOp::HalideOp(const NodeVector& args,
                                     const std::list<std::shared_ptr<Node>>& ops,
                                     const element::Type& out_type,
                                     const Shape& out_shape)
    : Op("HalideOp", check_single_output_args(args))
    , m_ops(ops)
    , m_output_type(out_type)
    , m_output_shape(out_shape)
{
    constructor_validate_and_infer_types();
}

void runtime::cpu::op::HalideOp::validate_and_infer_types()
{
    set_output_type(0, m_output_type, m_output_shape);
}

HalideOpは、src/ngraph/runtime/cpu/builder/halide_op.cppのBuilder::BUILDER_DECL(ngraph::runtime::cpu::op::HalideOp)にて、Halideを使ってコード生成しています。
            void Builder::BUILDER_DECL(ngraph::runtime::cpu::op::HalideOp)
            {
                const ngraph::runtime::cpu::op::HalideOp* hs =
                    static_cast<const ngraph::runtime::cpu::op::HalideOp*>(node);

                const auto& generators = ngraph::runtime::cpu::halide::get_halide_generators();

get_halide_generators が 各Op を Halide に変換していきます。詳細は、明日のブログで解析していきます。

                auto& halide_functions = external_function->get_halide_functions();
                auto& subgraph_params = external_function->get_subgraph_params();
                auto& subgraph_param_sizes = external_function->get_subgraph_param_sizes();
                auto& subgraph_param_ptrs = external_function->get_subgraph_param_ptrs();

external_functionの各メソッドを呼んでいます。この部分については、明日のブログで解析していきます。

hs->get_ops() にて、各Op1 を Halide に変換していきます。

                for (const auto& op : hs->get_ops())
                {
                    if (!generators.count(TI(*op)))
                    {
                        throw ngraph_error("Invalid op in halide subgraph");
                    }
                    vector<Halide::Func> inputs;
                    for (const auto& input : op->get_inputs())
                    {
                        auto tensor_name = input.get_output().get_tensor_ptr()->get_name();
                        if (halide_functions.count(tensor_name))
                        {
                            inputs.emplace_back(halide_functions[tensor_name]);
                        }
                        else
                        {
                            subgraph_params[tensor_name] = Halide::ImageParam(Halide::Float(32), 1);
                            subgraph_param_sizes[tensor_name] =
                                shape_size(input.get_output().get_tensor_ptr()->get_shape());
                            subgraph_param_ptrs.emplace(
                                tensor_name, external_function->get_tensor_data(tensor_name));
                            inputs.emplace_back(subgraph_params[tensor_name]);
                        }
                    }
                    halide_functions[op->get_output_tensor_ptr()->get_name()] =
                        generators.at(TI(*op))(inputs);
                }

次に、Halide部分 を Functor にマッピングします。

                auto out_tensor_name = hs->get_ops().back()->get_output_tensor_ptr()->get_name();
                auto& functors = external_function->get_functors();
                auto& out_tensor = external_function->get_tensor_data(out[0].get_name());
                auto& terminal_func = halide_functions[out_tensor_name];
                auto out_size = out[0].get_size();

                auto functor = [&, out_size](CPURuntimeContext* ctx, CPUExecutionContext* ectx) {
                    for (auto& param : subgraph_params)
                    {
                        Halide::Buffer<float> param_buffer(
                            static_cast<float*>(subgraph_param_ptrs.at(param.first).get()),
                            subgraph_param_sizes.at(param.first));
                        param.second.set(param_buffer);
                    }
                    Halide::Buffer<float> out_buffer(static_cast<float*>(out_tensor), out_size);
                    terminal_func.realize(out_buffer);
                };
                functors.emplace_back(functor);
            }