[转载]tensorflow二次开发
作者:互联网
本文转载自
https://leslie-fang.github.io/2019/02/27/tensorflow%E4%BA%8C%E6%AC%A1%E5%BC%80%E5%8F%91/
1. 编译
- 方法1
./configure
bazel build --config=opt //tensorflow/tools/pip_package:build_pip_package
build出错清理:
/root/.cache/bazel
把下面的之前出错的缓存文件给删除掉
生成whell包
bazel-bin/tensorflow/tools/pip_package/build_pip_package /root/tensorflow/wheel_pkg/build_withSource
- 方法2
yes "" | python configure.py
bazel build --config=mkl --copt=-mavx2 --copt=-O3 --copt=-DINTEL_MKL_QUANTIZED -s //tensorflow/tools/pip_package:build_pip_package
生成whell包
bazel-bin/tensorflow/tools/pip_package/build_pip_package /root/tensorflow/wheel_pkg/build_withSource
1.1 编译命令和过程分析
视频:https://www.youtube.com/watch?v=Rw-KrbfyABQ
https://www.cnblogs.com/shouhuxianjian/p/9416934.html
运行configure.py会把一些编译参数放入.bazelrc和.tf_configure.bazelrc文件里面(https://www.jianshu.com/p/5cd111ebb8bb)
bazelrc文件的解释
https://docs.bazel.build/versions/master/guide.html
build 后面接的都是默认的编译参数
build:mkl 后面接的编译参数只有当bazel build –config=mkl的时候mkl后面的编译参数才会起作用
bazel build的其他编译选项:
https://docs.bazel.build/versions/master/user-manual.html
–copt: This option takes an argument which is to be passed to the compiler. 所以–copt后面传进来的都是gcc或者是icc的编译参数
–strip是否删除debug信息,never表示不删除debug信息
1.2 增量编译
直接bazel build
然后重新生成wheel包
pip unistall tensorflow
一定先卸载然后重新安装
否则还是原来的包
2. 编译之后
生成pywrap_tensorflow_internal.py 以及 pywrap_tensorflow_internal.cc在~/.cache/bazel目录下面,所有代码都在_pywrap_tensorflow_internal.so 的动态链接库里面
pywrap_tensorflow_internal.py: 负责对接上层 Python 调用
pywrap_tensorflow_internal.cc: 负责对接下层 C API 调用
- pywrap_tensorflow_internal.py 模块首次被导入时,自动地加
载 _pywrap_tensorflow_internal.so 的动态链接库;其中, _pywrap_tensorflow_internal.so
包含了整个 TensorFlow 运行时的所有符号。 - 在 pywrap_tensorflow_internal.cc 的实现中,静态注册了一个函数符号表,实现了 Python 函数名到 C 函数名的二元关系。在运行时,按照 Python 的函数名称,匹找到对应的 C 函数实现,最终实现 Python 到 c_api.c 具体实现的调用关系。
3. 调整tensorflow运行的日志等级
TF代码又两个函数打印日志,LOG以及VLOG
LOG是正常的打印日志,通过TF_CPP_MIN_LOG_LEVEL
export TF_CPP_MIN_LOG_LEVEL=level
去设置,值越小,打印日志越多
VLOG通过
export TF_CPP_MIN_VLOG_LEVEL=level
去设置,但是VLOG只有在LOG等级为0的时候设置才有用
比如要打印mkl_layout_pass.cc初始化rewirte op时的信息
export TF_CPP_MIN_LOG_LEVEL=0
export TF_CPP_MIN_VLOG_LEVEL=1
4. 编译debug版本的tensorflow
添加 -c dbg选项
移除优化选项 –copt=-O3 以及 -c opt
bazel build --config=mkl --copt=-mavx2 --copt=-O3 --copt=-DINTEL_MKL_QUANTIZED -s -c dbg //tensorflow/tools/pip_package:build_pip_package
debug版本编译完大概有20G左右
export OMP_NUM_THREADS=1
设置intra和inter值为1
4.1 指定编译目录
默认编译在/root/.cache/bazel目录下面,有时候root目录空间不够
build_dir=/home/lesliefang/bazel_build
bazel --output_user_root=$build_dir clean
bazel --output_user_root=$build_dir build --config=mkl --copt=-mavx2 --copt=-O3 --copt=-DINTEL_MKL_QUANTIZED -s -c dbg //tensorflow/tools/pip_package:build_pip_package
4.2 编译报错找不到–march=broadwell
使用gcc6.3以及以上版本,低版本的编译器不认识broadwell的选项
4.3 whell太大无法打包
https://github.com/tensorflow/tensorflow/issues/5538
5. 替换mkldnn版本
以TF从0.18升级到0.19为例
5.1 下载mkldnn0.19计算sha256sum
wget https://github.com/intel/mkl-dnn/archive/v0.19.tar.gz
sha256sum v0.19.tar.gz
记录这个结果
ba39da6adb263df05c4ca2a120295641fc97be75b588922e4274cb628dbe1dcd
后面会用到
5.2 修改$tensorflow_root/tensorflow/workspace.bzl
搜索mkl_dnn
121 # Important: If you are upgrading MKL-DNN, then update the version numbers
122 # in third_party/mkl_dnn/mkldnn.BUILD. In addition, the new version of
123 # MKL-DNN might require upgrading MKL ML libraries also. If they need to be
124 # upgraded then update the version numbers on all three versions above
125 # (Linux, Mac, Windows).
126 tf_http_archive(
127 name = "mkl_dnn",
128 build_file = clean_dep("//third_party/mkl_dnn:mkldnn.BUILD"),
129 sha256 = "38a1c02104ee9f630c1ad68164119cd58ad0aaf59e04ccbe7bd5781add7bfbea",
130 strip_prefix = "mkl-dnn-0.18",
131 urls = [
132 "http://mirror.tensorflow.org/github.com/intel/mkl-dnn/archive/v0.18.tar.gz",
133 "https://github.com/intel/mkl-dnn/archive/v0.18.tar.gz",
134 ],
135 )
- 把里面所有0.18替换成0.19
- 替换上面得到的sha256sum
5.3 看第二步的注释和代码
需要修改”//third_party/mkl_dnn:mkldnn.BUILD”
$tensorflow_root/tensorflow/workspace.bzl
vim $tensorflow_root/third_party/mkl_dnn/mkldnn.BUILD
把里面的版本号从0.18改到0.19
注意:
tensorflow里面,mkldnn是被当做source code编译进去的,
所以不存在动态链接库
check:
build_dir/b3a4cb07d89ceca0353d37b5d32ffadc/external/mkl_dnn
里面是mkldnn下载下来的代码
里面有个readme文件在开头的地方可以check版本是0.18还是0.19
6. gdb 调试
二种方法方法去debug TF:
method1:
1. gdb python
2. run file.py
3. bt
method2:
1. 跑测试
2. top 看到python进程的pid
3. gdb -p pid
挂上之后,原来测试会挂住
break 函数名或者其它打上断点,tensorflow找不到符号的情况下可以 文件名:line的方式去打断点
continue 继续测试直到core-dump
如何添加python的信息 参考这个blog
http://jcf94.com/2018/01/13/2018-01-13-tfunpacking/
6.1 warning找不到文件
dir 目录
去指定文件的搜索根目录
使用gdbgui去调试的时候,也需要指定了目录之后才可以显示文件
6.2 调试前的参数设置以及技巧
所有并行计算线程设置为1,避免多线程导致断点带来的麻烦
命令后加&echo $!
输出PID,进行gdb -p的调试
7. mkldnn调试
export MKLDNN_VERBOSE=1
python ***
在运行测试之前,添加环境变量
可以打出mkldnn的信息
每一行的信息Each line with verbose information is formatted as a comma-separated list containing:
- mkldnn_verbose
- stage, e.g. create or exec
- primitive-kind, e.g. convolution, reorder, sum, …
- primitive implementation name
- propagation-kind, e.g. forward_training
- input/output data info, e.g. data type and data format
- auxiliary information, e.g. algorithm or number of input
- problem description
- for convolution the problem description is dumped in benchdnn friendly format
- for reorder, sum, and concat problem description is simply logical dims
- for other primitives the problem description is similar to convolution one
- execution time in milliseconds
8. 看python到C++调用关系
8.1 以Session 为例子:tf.Session时候的调用关系
- python api
/root/tensorflow_src/test_code/private-tensorflow/tensorflow/python
目录下面:
- grep -rni “class Session”
client/session.py:1475:class Session(BaseSession):
里面调用了baseSession的构造函数 - 看baseSession
里面调用了tf_session
self._session = tf_session.TF_NewSessionRef(self._graph._c_graph, opts)
from tensorflow.python import pywrap_tensorflow as tf_session
-
看pywrap_tensorflow.py
这个就是对应了编译出来的so文件 -
在source insight里面搜索TF_NewSessionRef
看到定义在tf_session_help.cc里面
里面调用了TF_NewSession -
source insight里面搜索TF_NewSession
已经进入到C++ 代码内部
8.2 以matmul为列
https://ggaaooppeenngg.github.io/zh-CN/2018/05/29/Tensorflow-%E7%9A%84-Tensor-%E5%92%8C-OpKernel-%E5%88%86%E6%9E%90/
调用 tf.matmul(a,b)
- 查看
grep -rni "tf_export.*matmul" #这个函数需要用tf_export导出
ops/math_ops.py:2277:@tf_export(“linalg.matmul”, “matmul”)
-
看math_ops.py:2277
api的使用有详细的解释
调用了gen_math_ops.batch_mat_mul 或者 gen_math_ops.mat_mul -
看gen_math_ops.py
find / -name "gen_math_ops.py"
这个文件看文件名字,应该是在编译的时候生成的
这个文件里面搜:batch_mat_mul
- batch_mat_mul函数
这个函数里面调用了
_result = _pywrap_tensorflow.TFE_Py_FastPathExecute(
_ctx._context_handle, _ctx._eager_context.device_name, "BatchMatMul",
name, _ctx._post_execution_callbacks, x, y, "adj_x", adj_x, "adj_y",
adj_y)
所以C++里面的op函数应该是BatchMatMul
- 搜索所有注册这个op的地方
搜索op定义
[root@localhost private-tensorflow]# grep -rni "REGISTER_OP(\"MatMul\")"
tensorflow/core/ops/math_ops.cc:763:REGISTER_OP("MatMul")
搜索op的kernel实现
grep -rni "Name(\"MatMul\")"
找到所有定义operation
break 文件名:行
在每个computer的d地方打断点
看看调用到了哪个kernel
看class MatMulOp 的Compute方法里面最后调用了LaunchMatMul方法
LaunchMatMul 继承自LaunchMatMulBase,在 LaunchMatMulBase 当中调用了 functor::MatMulFunctor,这个 functor 主要就会执行乘法操作添加链接描述
MatMulFunctor里面调用了MatMul方法
MatMul方法里面进一步调用了out.device(d) = in0.contract(in1, dim_pair);
contract是Eigen的一个方法,表示矩阵相乘,Eigen是一套高效的C++中调用的数学平台,里面实现了很多通用的数学运算。
8.3 以conv2d为例
这个人博客很多好文章:http://lanhin.xyz/
http://lanhin.xyz/2018/10/29/tensorflow%E4%B8%AD2d%E5%8D%B7%E7%A7%AF%E4%BB%A3%E7%A0%81%E7%AE%80%E6%9E%90/
- python 接口 tf.nn.conv2d
grep -rni "tf_export.*conv2d"
tensorflow_src/test_code/private-tensorflow/tensorflow/python/ops/nn_ops.py:1376:@tf_export(“nn.conv2d”, v1=[])
- 查找输出的地方
find / -name "gen_math_ops.py"
- 查看op注册和实现的地方
grep -rni "REGISTER_OP(\"Conv2D\")"
grep -rni "Name(\"Conv2D\")"
- 进入conv_ops.cc文件
看Compute方法
输入为浮点数float调用LaunchDeepConvOp::Run
其它输入类型调用launcher_
进一步看调用到了
LaunchConv2DOp::operator()
再往下
tensorflow::LaunchGeneric::operator
这个函数里面通过不同的条件判断调用两个不同的计算kernel:functor::MatMulConvFunctor()
和functor::SpatialConvolution()
MatMulConvFunctor定义在conv_2d.h文件里面
out.device(d) = in0.contract(in1, dim_pair, output_kernel);
到最后还是调用了矩阵乘法的函数
这个contract应该是eigen库提供的接口
8.4 INT8 operation
- 读取RN50 int8的pb
用tensorboard查看
看到用到了op:QuantizedConv2DWithBiasAndReluAndRequantize
搜索不到对应op的时候
tensorflow做了op的转换
private-tensorflow\tensorflow\core\graph\mkl_layout_pass.cc
参考这个文件
果然再这个文件里面可以搜索到
QuantizedConv2DWithBiasAndReluAndRequantize
mkl_layout_pass.cc 根据PPT里面的解释,会把标准的输入的TF的graph转换成mkl优化的图,里面有个run函数应该是转换的入口
也有可能定义tensorflow/core/api_def/base_api/api_def_QuantizedMatMulWithBias.pbtxt
这个目录下面也可能定义了pb文件
python api有两种定义方法(https://groups.google.com/a/tensorflow.org/forum/#!topic/developers/LmKn-y7LZ_E):
Python API endpoints are currently added using 2 ways:
- apidef.pbtxt files (python_op_gen_internal.cc would actually add tf_export decorator for each visible endpoint specified in apidef.pbtxt files)
- tf_export decorators
- 搜索这个op
[root@localhost ~]# grep -rni "name(\"QuantizedConv2DWithBiasAndReluAndRequantize\")"
tensorflow_src/test_code/private-tensorflow/tensorflow/core/kernels/mkl_conv_ops.cc:1997:REGISTER_KERNEL_BUILDER(Name("QuantizedConv2DWithBiasAndReluAndRequantize")
这个op对应的kernel实现就是QuantizedConv2DWithBiasAndReluAndRequantize
对应的kernel叫做NoOp
看到注释:
// Register NoOp kernel for QuantizedConv2DWithBiasAndRelu to get a python
// interface.
// This kernel will be replaced by an MKL kernel during graph-optimization pass.
NoOp是因为这个op在图优化阶段被rewrite了(mkl_layout_pass.cc的RunPass函数)
同一个文件里面看另外一个op
_MklQuantizedConv2DWithBiasSumAndRelu
对应的kernel是MklQuantizedConv2DSumReluOp
继承了MklQuantizedConv2DOp这个kernel
MklQuantizedConv2DOp这个kernel继承了MklConvOp
MklQuantizedConv2DOp的compute方法首先调用了
// Compute int32 output tensor
MklConvOp<Device, quint8, qint8, Tbias, Toutput, Ttemp_output, int32,
biasEnabled, false>::Compute(context);
MklConvOp里面的compute方法调用了mkldnn
conv_fwd->Execute执行mkldnn的计算
注意
class MklConvOp在这个文件里面有两个类的定义
通过template Execute
根据文件里面的宏的定义,应该只有一个函数会被编译出来
看这个mkldnn的类的实现代码,可以先看看MKLDNN的教程和实例代码mkldnn代码库的simple_net.cpp以及解释
基本概念比较清晰,先创建memory/operator descriptor,再创建对应的Primitive descriptor ,最后创建primitive,然后把primitive放到stream里面去执行
tensorflow的这个类的实现follow这个逻辑只是加了一些封装
至于mkldnn里面进一步的实现(如何多线程等)就是mkldnn的事情了
可以看我的mkldnn的文章
9. 自己定义个operation
9.1 定义operation
#include "tensorflow/core/framework/op.h"
REGISTER_OP("ZeroOut")
.Input("to_zero: int32")
.Output("zeroed: int32");`
9.2 定义kernel
#include "tensorflow/core/framework/op_kernel.h"
using namespace tensorflow;
class ZeroOutOp : public OpKernel {
public:
explicit ZeroOutOp(OpKernelConstruction* context) : OpKernel(context) {}
void Compute(OpKernelContext* context) override {
// 获取输入 tensor.
const Tensor& input_tensor = context->input(0);
auto input = input_tensor.flat<int32>();
// 创建一个输出 tensor.
Tensor* output_tensor = NULL;
OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),
&output_tensor));
auto output = output_tensor->template flat<int32>();
// 设置 tensor 除第一个之外的元素均设为 0.
const int N = input.size();
for (int i = 1; i < N; i++) {
output(i) = 0;
}
// 尽可能地保留第一个元素的值.
if (N > 0) output(0) = input(0);
}
};
REGISTER_KERNEL_BUILDER(Name("ZeroOut").Device(DEVICE_CPU), ZeroOutOp);
9.3 添加python wrap
经过前面两步在编译之后,可以在bazel-genfiles/tensorflow/python/ops/gen_user_ops.py文件,比如我的一个例子
vim /home/lesliefang/bazel_build/615e7e34d0a05b2b7ebac45eda8ba3c5/execroot/org_tensorflow/bazel-out/k8-opt/bin/tensorflow/tools/pip_package/build_pip_package.runfiles/org_tensorflow/tensorflow/python/ops/gen_user_ops.py
里面找到对应的operation的函数
为了使得python可以调用到,在tensorflow/python/user_ops/user_ops.py 文件中添加接口
@tf_export(v1=['user_ops.leslie_zero_out'])
def leslie_zero_out(input):
"""Example of overriding the generated code for an Op."""
return _gen_user_ops.zero_out(input)
9.4 测试
重新编译之后安装之后
测试代码
import tensorflow as tf
import numpy as np
import datetime
import os
import time
if __name__ == "__main__":
#time.sleep(30)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
result = tf.user_ops.leslie_zero_out([5, 4, 3, 2, 1])
print("result is {}".format(result))
print("result is {}".format(sess.run(result)))
10. 多线程
To write a multi-threaded CPU kernel, the Shard function in work_sharder.h can be used. This function shards a computation function across the threads configured to be used for intra-op threading (see intra_op_parallelism_threads in config.proto).
11. 核心运行机制
推荐一个很好的Blog:http://jcf94.com/2018/01/13/2018-01-13-tfunpacking/
这个blog对C++部分session的机制分析的很清楚
这边从python调用session.run开始分析
11.1 在python里面
- session.run
result = self._run(None, fetches, feed_dict, options_ptr,
run_metadata_ptr)
- 在_run里面
results = self._do_run(handle, final_targets, final_fetches,
feed_dict_tensor, options, run_metadata)
- do_run里面
return self._call_tf_sessionrun(
options, feed_dict, fetch_list, target_list, run_metadata)
- call_tf_sessionrun里面
return tf_session.TF_SessionRun_wrapper(
self._session, options, feed_dict, fetch_list, target_list,
run_metadata)
TF_SessionRun_wrapper 定义在pywrap_tensorflow_internal.py里面
就是python和C++的桥梁
11.2 下面进入C++的部分
-
_SessionRun_wrapper_helper函数
里面调用了TF_SessionRun -
TF_SessionRun 函数
调用了TF_Run_Helper函数 -
TF_Run_Helper函数
调用了session->Run函数 -
这是个虚函数
用gdb跟进去看
参考这篇文章:https://zhuanlan.zhihu.com/p/26031658
local用direction_session
分布式用grpc_session
所以我们这边调用到了DirectSession::Run -
看DirectSession::Run函数
这个函数的分析:http://jcf94.com/2018/01/13/2018-01-13-tfunpacking/ -
GetOrCreateExecutors函数里面会去寻找有没有符合条件的exectuor,不存在的话则调用CreateExecutors函数去创建executors
同时CreateExecutors里面调用到了CreateGraphs
在CreateExecutors调用了CreateGraphs之后看到:
params.create_kernel = [this, lib, opseg](const NodeDef& ndef,
OpKernel** kernel)
我理解就是在这里实现了param里面的创建kernel的函数指针
在CreateExecutors的最后调用了NewExecutor函数,会传入param变量(里面带上了create_kernel方法)
NewExecutor函数里面通过工厂模式来生成Executor
是个虚函数,通过gdb看到里面调用了
tensorflow::(anonymous namespace)::DefaultExecutorRegistrar::Factory::NewExecutor (this=0x1fffd10, params=…, graph=…,
out_executor=0x72fdee8) at tensorflow/core/common_runtime/executor.cc:2857
class Factory : public ExecutorFactory {
Status NewExecutor(const LocalExecutorParams& params,
std::unique_ptr<const Graph> graph,
std::unique_ptr<Executor>* out_executor) override {
Executor* ret = nullptr;
TF_RETURN_IF_ERROR(NewLocalExecutor(params, std::move(graph), &ret));
out_executor->reset(ret);
return Status::OK();
}
};
里面调用了NewLocalExecutor
进一步调用ExecutorImpl->Initialize函数
这个函数里面调用了params_.create_kernel函数去创建kernel
(这个create_kernel函数就是之前在CreateExecutors函数里面定义的)
同时在这个函数里面看到了一行注释
// Preprocess every node in the graph to create an instance of op
// kernel for each node.
11.3 调试CreateExecutors的create_kernel函数
gdb断点进去CreateKernel函数
tensorflow/core/common_runtime/function.cc:521
调用到526行的CreateKernel函数
tensorflow/core/common_runtime/function.cc:526
executor.cc的CreateNonCachedKernel函数
op_kernel.cc的CreateOpKernel函数(*kernel = registration->factory->Create(&context);)
mkl_conv_ops.cc的TF_CALL_float(REGISTER_MKL_CPU_2D_FUSED);函数
mkl_conv_ops.cc的MklFusedConvOp的构造函数
所以调用session.run多次,因为已经存在符合条件的exectuors,并不会多次创建图
(别人的评论:第一次执行 sess.run(….) 的时候会根据 python 层的图构造出 C++ 层的图然后保存下来,之后如果下次 sess.run() 的目标节点是相同的,就不需要重新构造一遍了。详细可以去分析 sess.run() 的执行流程)
- 调用到了RunInternal函数
-
RunInternal函数
里面调用了item.executor->RunAsync(args, barrier->Get());
去执行异步计算 -
通过日志知道RunAsync会调用到executor的Process()函数
process函数做了什么:
http://jcf94.com/2018/01/13/2018-01-13-tfunpacking/
遍历每个节点,针对每个节点的kernel进行计算(调用device->Compute
,里面调用op_kernel->Compute(context);
)
在每个kernel里面都可以搜索到对应的Compute函数
12. 看一个inner product的kernel是怎么生成的
断点打在
b mkl_qmatmul_op.cc:183(一个setup函数里面)
分析代码知道这个setup函数是设置上下文变量的
查看调用栈
#0 tensorflow::MklIPFwdPrimitive<float, Eigen::QUInt8, Eigen::QInt8, Eigen::QInt32, Eigen::QUInt8>::Setup (this=0x3d1a300, IPFwdDims=...)
at tensorflow/core/kernels/mkl_qmatmul_op.cc:183
#1 0x00007f6a77ee938c in tensorflow::MklIPFwdPrimitive<float, Eigen::QUInt8, Eigen::QInt8, Eigen::QInt32, Eigen::QUInt8>::MklIPFwdPrimitive (this=0x3d1a300, IPFwdDims=...)
at tensorflow/core/kernels/mkl_qmatmul_op.cc:77
#2 0x00007f6a77ee81c3 in tensorflow::MklIPFwdPrimitiveFactory<float, Eigen::QUInt8, Eigen::QInt8, Eigen::QInt32, Eigen::QUInt8>::Get (IPFwdDims=..., do_not_cache=false)
at tensorflow/core/kernels/mkl_qmatmul_op.cc:298
#3 0x00007f6a77ee0515 in tensorflow::MklIPOp<Eigen::ThreadPoolDevice, Eigen::QUInt8, Eigen::QInt8, Eigen::QInt32, Eigen::QUInt8, Eigen::QUInt8, true>::Compute (
this=0x1ea0f20, context=0x7f6a53f1d5f0) at tensorflow/core/kernels/mkl_qmatmul_op.cc:499
#4 0x00007f6a77edee0e in tensorflow::MklQuantizedIPOp<Eigen::ThreadPoolDevice, Eigen::QInt32, Eigen::QUInt8, Eigen::QUInt8, true>::Compute (this=0x1ea0f20,
context=0x7f6a53f1d5f0) at tensorflow/core/kernels/mkl_qmatmul_op.cc:752
#5 0x00007f6a78410eae in tensorflow::Device::Compute (this=0x40a6780, op_kernel=0x1ea0f20, context=0x7f6a53f1d5f0) at ./tensorflow/core/common_runtime/device.h:89
#6 0x00007f6a6c90f868 in tensorflow::(anonymous namespace)::ExecutorState::Process (this=0x54f6480, tagged_node=..., scheduled_nsec=0)
at tensorflow/core/common_runtime/executor.cc:1817
- #0 mkl_qmatmul_op.cc:183 在tensorflow里面这个primitive的setup函数
看这个setup里面,看到先创建mkldnn的primitive的desc
// create a inner product
context_.fwd_desc.reset(new inner_product_forward::desc(
prop_kind::forward_inference, *context_.src_md, *context_.weight_md,
*context_.bias_md,
*context_.dst_md));
然后通过这个desc去创建primitive_desc(pd),跟进到mkldnn里面看,就是在创建pd的时候回去遍历mkldnn里面所有pd找到对应的满足条件的pd
- #1 mkl_qmatmul_op.cc:77 MklIPFwdPrimitive的构造函数
- #2 mkl_qmatmul_op.cc:298 MklIPFwdPrimitiveFactory的Get函数,Get函数根据输入的MklIPFwdParams去try to find a suitable one in pool
没有找到的话(if (IP_fwd == nullptr))会去创建 - #3 mkl_qmatmul_op.cc:499 MklIPOp的compute方法,里面调用了MklIPFwdPrimitiveFactory的Get方法去拿到对应的IP_fwd(Primitive)
MklIPOp的compute方法 应该是tensorflow在运行图的节点的时候会被调用到的方法
继续看这个MklIPOp的compute方法
后面会调用IP_fwd->Execute(src_data, weight_data, bias_data, dstdata);
去做计算
这个根据前几步选中的mkldnn的pd,会调用到mkldnn的submit函数(context.fwdstream->submit(context.fwd_primitives)标签:kernel,调用,cc,mkl,二次开发,tensorflow,转载,op 来源: https://blog.csdn.net/s_sunnyy/article/details/100729842