Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New features]Add function node in phi_kernel for MKLDNN #51073

Merged
merged 15 commits into from
Mar 10, 2023

Conversation

heavyrain-lzy
Copy link
Contributor

@heavyrain-lzy heavyrain-lzy commented Mar 1, 2023

PR types

New features

PR changes

OPs

Describe

card-67001
执行静态图时,为inputs准备数据阶段会根据expected_kernel和每个tensor的定义进行backend、layout、dtype转换。在phi下的kernel借助了GetKernelTypeForVar和kernel内注册的argsdef进行转换,大部分的GetKernelTypeForVar函数与动态图统一,但由于MKLDNN的引入,存在部分op的GetKernelTypeForVar多了一些专门处理MKLDNN kernel的特殊代码,如interpolate_op.cc中:

  phi::KernelKey GetKernelTypeForVar(
      const std::string& var_name,
      const phi::DenseTensor& tensor,
      const phi::KernelKey& expected_kernel_type) const override {
#ifdef PADDLE_WITH_MKLDNN
    if ((expected_kernel_type.layout() == phi::DataLayout::ONEDNN) &&
        (tensor.layout() != phi::DataLayout::ONEDNN)) {
      auto attrs = Attrs();
      auto ar = paddle::framework::AttrReader(attrs);
      const std::string data_format = ar.Get<std::string>("data_layout");
      auto dl = phi::StringToDataLayout(data_format);
      // Some models may have intentionally set "AnyLayout" for pool
      // op. Treat this as NCHW (default data_format value)
      if (dl != phi::DataLayout::kAnyLayout) {
        return phi::KernelKey(tensor.place(), dl, expected_kernel_type.dtype());
      }
    }
#endif

    if (var_name == "OutSize" || var_name == "SizeTensor" ||
        var_name == "Scale") {
      return phi::KernelKey(phi::Backend::ALL_BACKEND,
                            expected_kernel_type.layout(),
                            expected_kernel_type.dtype());
    }
    return phi::KernelKey(
        tensor.place(), tensor.layout(), expected_kernel_type.dtype());
  }

这段逻辑较为特殊并且不符合规范,如果需要自动生成,会导致自动生成脚本变得臃肿。综合考虑后,决定在kernel中增加一个功能和GetKernelTypeForVar类似的函数指针,默认缺省,只在GetKernelTypeForVar有特殊逻辑的MKLDNN kernel中注册,如在OneDnn kernel文件interploate_kernel.cc中增加对应的函数和注册:

phi::KernelKey InterpolateGetKernelTypeForVar(
    const InferVarKernelContext* ctx) {
  const std::string& var_name = ctx->GetVarName();
  const DenseTensor& tensor = ctx->GetTensor();
  const KernelKey& expected_kernel_type = ctx->GetKernelKey();
  const AttributeMap& attrs = ctx->GetAttrs();
  // Only input require reshaping, weights and
  // bias are having shape in NCHW order
  if ((expected_kernel_type.layout() == phi::DataLayout::ONEDNN) &&
      (tensor.layout() != phi::DataLayout::ONEDNN)) {
    auto it = attrs.find("data_layout");
    PADDLE_ENFORCE_NE(it,
                      attrs.end(),
                      paddle::platform::errors::NotFound(
                          "Cannot find attribute %s.", "data_layout"));
    const std::string data_layout = PADDLE_GET_CONST(std::string, it->second);
    auto dl = phi::StringToDataLayout(data_layout);
    // Some models may have intentionally set "AnyLayout" for pool
    // op. Treat this as NCHW (default data_format value)
    if (dl != phi::DataLayout::kAnyLayout) {
      return phi::KernelKey(tensor.place(), dl, expected_kernel_type.dtype());
    }
  }
  if (var_name == "OutSize" || var_name == "SizeTensor" ||
      var_name == "Scale") {
    return phi::KernelKey(phi::Backend::ALL_BACKEND,
                          expected_kernel_type.layout(),
                          expected_kernel_type.dtype());
  }
  return phi::KernelKey(
      tensor.place(), tensor.layout(), expected_kernel_type.dtype());
}
...
...
...
PD_REGISTER_KERNEL(bilinear_interp,
                   OneDNN,
                   ONEDNN,
                   phi::BilinearInterpKernel,
                   float,
                   phi::dtype::bfloat16) {}
                   phi::dtype::bfloat16) {
  kernel->get_kerneltype_forvar_fn_ = phi::InterpolateGetKernelTypeForVar;
}

PD_REGISTER_KERNEL(nearest_interp,
                   OneDNN,
                   ONEDNN,
                   phi::NearestInterpKernel,
                   float,
                   phi::dtype::bfloat16,
                   int8_t,
                   uint8_t) {}
                   uint8_t) {
  kernel->get_kerneltype_forvar_fn_ = phi::InterpolateGetKernelTypeForVar;
}

此接口也能用于未来其他硬件的接入。

When executing a static graph, the stage of preparing data for inputs will perform backend, layout, and dtype conversions according to the definition of expected_kernel and each tensor. The kernel under phi is converted with the help of GetKernelTypeForVar and the argsdef registered in the kernel. Most of the GetKernelTypeForVar functions are unified with the dynamic graph. However, due to the introduction of MKLDNN, there are some operators' GetKernelTypeForVar with special codes, such as in interpolate_op.cc:

  phi::KernelKey GetKernelTypeForVar(
      const std::string& var_name,
      const phi::DenseTensor& tensor,
      const phi::KernelKey& expected_kernel_type) const override {
#ifdef PADDLE_WITH_MKLDNN
    if ((expected_kernel_type.layout() == phi::DataLayout::ONEDNN) &&
        (tensor.layout() != phi::DataLayout::ONEDNN)) {
      auto attrs = Attrs();
      auto ar = paddle::framework::AttrReader(attrs);
      const std::string data_format = ar.Get<std::string>("data_layout");
      auto dl = phi::StringToDataLayout(data_format);
      // Some models may have intentionally set "AnyLayout" for pool
      // op. Treat this as NCHW (default data_format value)
      if (dl != phi::DataLayout::kAnyLayout) {
        return phi::KernelKey(tensor.place(), dl, expected_kernel_type.dtype());
      }
    }
#endif

    if (var_name == "OutSize" || var_name == "SizeTensor" ||
        var_name == "Scale") {
      return phi::KernelKey(phi::Backend::ALL_BACKEND,
                            expected_kernel_type.layout(),
                            expected_kernel_type.dtype());
    }
    return phi::KernelKey(
        tensor.place(), tensor.layout(), expected_kernel_type.dtype());
  }

This piece of logic is special and does not conform to the specification. If it needs to be automatically generated, it will cause the automatic generation script to become bloated. After comprehensive consideration, it is decided to add a function pointer similar to GetKernelTypeForVar in the kernel. By default, it is only registered in the MKLDNN kernel with special logic for GetKernelTypeForVar. For an example, add the function and register the function node in the OneDnn kernel file interploate_kernel.cc:

phi::KernelKey InterpolateGetKernelTypeForVar(
    const InferVarKernelContext* ctx) {
  const std::string& var_name = ctx->GetVarName();
  const DenseTensor& tensor = ctx->GetTensor();
  const KernelKey& expected_kernel_type = ctx->GetKernelKey();
  const AttributeMap& attrs = ctx->GetAttrs();
  // Only input require reshaping, weights and
  // bias are having shape in NCHW order
  if ((expected_kernel_type.layout() == phi::DataLayout::ONEDNN) &&
      (tensor.layout() != phi::DataLayout::ONEDNN)) {
    auto it = attrs.find("data_layout");
    PADDLE_ENFORCE_NE(it,
                      attrs.end(),
                      paddle::platform::errors::NotFound(
                          "Cannot find attribute %s.", "data_layout"));
    const std::string data_layout = PADDLE_GET_CONST(std::string, it->second);
    auto dl = phi::StringToDataLayout(data_layout);
    // Some models may have intentionally set "AnyLayout" for pool
    // op. Treat this as NCHW (default data_format value)
    if (dl != phi::DataLayout::kAnyLayout) {
      return phi::KernelKey(tensor.place(), dl, expected_kernel_type.dtype());
    }
  }
  if (var_name == "OutSize" || var_name == "SizeTensor" ||
      var_name == "Scale") {
    return phi::KernelKey(phi::Backend::ALL_BACKEND,
                          expected_kernel_type.layout(),
                          expected_kernel_type.dtype());
  }
  return phi::KernelKey(
      tensor.place(), tensor.layout(), expected_kernel_type.dtype());
}
...
...
...
PD_REGISTER_KERNEL(bilinear_interp,
                   OneDNN,
                   ONEDNN,
                   phi::BilinearInterpKernel,
                   float,
                   phi::dtype::bfloat16) {}
                   phi::dtype::bfloat16) {
  kernel->get_kerneltype_forvar_fn_ = phi::InterpolateGetKernelTypeForVar;
}

PD_REGISTER_KERNEL(nearest_interp,
                   OneDNN,
                   ONEDNN,
                   phi::NearestInterpKernel,
                   float,
                   phi::dtype::bfloat16,
                   int8_t,
                   uint8_t) {}
                   uint8_t) {
  kernel->get_kerneltype_forvar_fn_ = phi::InterpolateGetKernelTypeForVar;
}

@paddle-bot
Copy link

paddle-bot bot commented Mar 1, 2023

你的PR提交成功,感谢你对开源项目的贡献!
请关注后续CI自动化测试结果,详情请参考Paddle-CI手册
Your PR has been submitted. Thanks for your contribution!
Please wait for the result of CI firstly. See Paddle CI Manual for details.

paddle/fluid/framework/data_transform.cc Show resolved Hide resolved
paddle/fluid/framework/data_transform.cc Show resolved Hide resolved
paddle/phi/core/infer_varkernel_utils.h Outdated Show resolved Hide resolved
paddle/phi/kernels/onednn/interpolate_kernel.cc Outdated Show resolved Hide resolved
paddle/phi/core/infer_varkernel_utils.h Outdated Show resolved Hide resolved
paddle/phi/api/yaml/op_compat.yaml Outdated Show resolved Hide resolved
paddle/phi/api/yaml/op_compat.yaml Outdated Show resolved Hide resolved
paddle/fluid/framework/data_transform.cc Show resolved Hide resolved
paddle/phi/core/infer_varkernel_utils.cc Outdated Show resolved Hide resolved
paddle/phi/core/infer_varkernel_utils.h Outdated Show resolved Hide resolved
paddle/phi/core/infer_varkernel_utils.h Outdated Show resolved Hide resolved
@heavyrain-lzy
Copy link
Contributor Author

heavyrain-lzy commented Mar 7, 2023

@xinyu-intel, @YangQun1,Please help review the code.

@yaomichael yaomichael requested a review from Silv3S March 9, 2023 07:30
const AttributeMap &fluid_attrs,
phi::AttributeMap *phi_attrs,
bool has_infer_varkernel_fn) {
// According to "GetKernelTypeForVar" in some ops those have MKLDNN codes,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// According to "GetKernelTypeForVar" in some ops those have MKLDNN codes,
// According to "GetKernelTypeForVar" in some ops executed with oneDNN,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I will change the comment according to your suggestion.

class KernelKey;
class DenseTensor;
/**
* Note: GetKernelTypeForVarContext is currently designed to MKLDNN kernel when
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Note: GetKernelTypeForVarContext is currently designed to MKLDNN kernel when
* Note: GetKernelTypeForVarContext is currently designed for oneDNN kernel when

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I will change the comment according to your suggestion.

/**
* Note: GetKernelTypeForVarContext is currently designed to MKLDNN kernel when
* the related memeber function 'GetKernelTypeForVar' is special. It is
* possiable to uesed for other custom hardwares in the future.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* possiable to uesed for other custom hardwares in the future.
* possible to leverage to other vendor libraries in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I will change the comment according to your suggestion.

private:
const KernelKey* kernel_key_; // not owned
// Use AttributeMap in namespace 'phi' to avoid depending 'fuild'
const AttributeMap* attrs_; // not owned
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems that oneDNN specific stuffs are hided inside the AttributeMap. Do you mind making these as members as this is only used for oneDNN kernel.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the attribute name that will be used can be "data_layout" or "data_format" or other for the future vendors, it is brief to using AttributeMap.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, as data_layout/data_format has been defined through the phi API, it is natural to stay in the structure directly. I think you're right that AttributeMap will make it easier for the future vendors to store more specific stuffs, but at the same time, the content inside the map can be out of controlled by the framework. Both are okay to me.

Comment on lines +30 to +31
// Only input require reshaping, weights and
// bias are having shape in NCHW order
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I understand these correctly. Do you mean "Only input requires changing data_layout"?
Usually, we distinguish shape and layout. Take Tensor{shape={1,3,8,8}, data_format={"NCHW"},stride={192,64,8,1}} as an example. data_format means the shape is ordered by NCHW(a.k.a channel_size is 3). stride means it has contiguous data_layout(DataLayout::kNCHW). We can reorder the tensor to kNHWC/KONEDNN but keep the shape unchanged. We can reshape the tensor to {3,8,8}/{24,8}/{1,192} with the layout unchanged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that shape data_formatandstride is a whole, together to represent a tensor。Take Tensor{shape={1,3,8,8}, data_format={"NCHW"},stride={192,64,8,1}} as an example. If you change the data_format from NCHW to NHWC, the shape must change from {1,3,8,8} to {1, 8, 8, 3} together. Otherwise, the data is wrong when you use.

// bias are having shape in NCHW order
if ((expected_kernel_type.layout() == DataLayout::ONEDNN) &&
(tensor.layout() != DataLayout::ONEDNN)) {
auto it = attrs.find("data_layout");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I'm a little confusing about the definition of data_layout and data_format in Paddle. In LRNOp::GetKernelTypeForVar, we get the data_format from Attrs, but here we get the data_layout instead. Do they have any difference?

BTW, do we also plan to move the GetKernelTypeForVar of LRNOp/Pad2dOp/Pad3dOp to phi? since there are also some MKLDNN specific code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Attrs name data_formatand data_layout have the same meaning. However we can't change them for compatibility.
We also plan to move the GetKernelTypeForVar of LRNOp/Pad2dOp/Pad3dOp to phi soon.

@YangQun1
Copy link
Contributor

If it needs to be automatically generated, it will cause the automatic generation script to become bloated.

May I ask how the MKLDNN specific code impact the auto generation? Not sure if I understand correctly, currently I didn't find GetKernelTypeForVar related code in the generated api.cc and backward_api.cc files.

@heavyrain-lzy
Copy link
Contributor Author

You can find the code automatically generated from generated_op1.cc~generated_op4.cc after executing cmake command.


const std::string& GetVarName(void) const;

const DenseTensor& GetTensor(void) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里会不会出现tensor为null的情况

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果没有给tensor成员赋值,会出现null情况,为确保安全后续加上安全性检查。

Copy link
Contributor

@jiahy0825 jiahy0825 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zyfncg zyfncg merged commit a0a6dc6 into PaddlePaddle:develop Mar 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants