zoukankan      html  css  js  c++  java
  • Tensorflow[源码安装时bazel行为解析]


    0. 引言

    通过源码方式安装,并进行一定程度的解读,有助于理解tensorflow源码,本文主要基于tensorflow v1.8源码,并借鉴于如何阅读TensorFlow源码.

    首先,自然是需要去bazel官网了解下必备知识,如(1)什么是bazel; (2)bazel如何对cpp项目进行构建的; (3)bazel构建时候的函数大全。然后就是bazel官网的一些其他更细节部分了。下文中会给出超链接。

    ps: 找了很久,基本可以确定bazel除了官网是没有如书籍等资料出现的,所以只有官网和别人博客这2个途径进行学习了解
    因为bazel官网很多链接不在左边的导航中,所以推荐直接将整个网站镜像下来

    wget -m -c -x -np -k -E -p https://docs.bazel.build/versions/master/bazel-overview.html
    

    1. 从源码编译tensorflow

    如下图所示:

    图1.1 github上tensorflow v1.8源码目录

    1.1 先配置

    源代码树的根目录中包含了一个名为 configure 的 bash 脚本。此脚本会要求您确定所有相关 TensorFlow 依赖项的路径名,并指定其他构建配置选项,例如编译器标记。您必须先运行此脚本,然后才能创建 pip 软件包并安装 TensorFlow
    然后是运行该configure

    ./configure
    
    $ cd tensorflow  # cd to the top-level directory created
    $ ./configure
    Please specify the location of python. [Default is /usr/bin/python]: /usr/bin/python2.7    # python解释器路径
    Found possible Python library paths:
     /usr/local/lib/python2.7/dist-packages
     /usr/lib/python2.7/dist-packages
    Please input the desired Python library path to use.  Default is [/usr/lib/python2.7/dist-packages]       # python 库路径
    
    Using python library path: /usr/local/lib/python2.7/dist-packages
    Please specify optimization flags to use during compilation when bazel option "--config=opt" is specified [Default is -march=native]:    # 是否在编译期间启用优化
    Do you wish to use jemalloc as the malloc implementation? [Y/n]        # 是否将 jemalloc 作为malloc的实现
    jemalloc enabled
    Do you wish to build TensorFlow with Google Cloud Platform support? [y/N]    # 是否开启google云平台支持
    No Google Cloud Platform support will be enabled for TensorFlow
    Do you wish to build TensorFlow with Hadoop File System support? [y/N]    # 是否开启hdfs的支持
    No Hadoop File System support will be enabled for TensorFlow
    Do you wish to build TensorFlow with the XLA just-in-time compiler (experimental)? [y/N]    # 是否启用尚在实验性质的XLA jit编译
    No XLA support will be enabled for TensorFlow
    Do you wish to build TensorFlow with VERBS support? [y/N]    # 是否开启VERBS支持
    No VERBS support will be enabled for TensorFlow
    Do you wish to build TensorFlow with OpenCL support? [y/N]    # 是否开启OpenCL支持
    No OpenCL support will be enabled for TensorFlow
    Do you wish to build TensorFlow with CUDA support? [y/N] Y    # 是否开启CUDA支持
    CUDA support will be enabled for TensorFlow
    Do you want to use clang as CUDA compiler? [y/N]    # 是否将clang作为CUDA的编译器
    nvcc will be used as CUDA compiler
    Please specify the CUDA SDK version you want to use, e.g. 7.0. [Leave empty to default to CUDA 9.0]: 9.0    # 选择cuda版本
    Please specify the location where CUDA 9.0 toolkit is installed. Refer to README.md for more details. [Default is /usr/local/cuda]:    # 告知cuda的安装路径
    Please specify which gcc should be used by nvcc as the host compiler. [Default is /usr/bin/gcc]:    # 指定host侧的 编译器
    Please specify the cuDNN version you want to use. [Leave empty to default to cuDNN 7.0]: 7    # cuDNN版本
    Please specify the location where cuDNN 7 library is installed. Refer to README.md for more details. [Default is /usr/local/cuda]:    # 告知cuDNN 的安装路径
    Please specify a list of comma-separated CUDA compute capabilities you want to build with.     # 告知当前机器上GPU的计算力
    You can find the compute capability of your device at: https://developer.nvidia.com/cuda-gpus.    
    Please note that each additional compute capability significantly increases your build time and binary size.
    
    Do you wish to build TensorFlow with MPI support? [y/N]    # 是否开启MPI支持
    MPI support will not be enabled for TensorFlow
    Configuration finished
    

    我们先来看看configure到底做了什么事情,

    #!/usr/bin/env bash
    
    set -e
    set -o pipefail
    
    if [ -z "$PYTHON_BIN_PATH" ]; then
      PYTHON_BIN_PATH=$(which python || which python3 || true)
    fi
    
    # Set all env variables
    CONFIGURE_DIR=$(dirname "$0")
    "$PYTHON_BIN_PATH" "${CONFIGURE_DIR}/configure.py" "$@"    #  这行表明该configure文件是通过调用 对应的configure.py来完成配置过程的
    
    echo "Configuration finished"
    

    从configure.py的第1491行开始,发现如上述运行代码中展示的配置过程

      set_build_var(environ_cp, 'TF_NEED_JEMALLOC', 'jemalloc as malloc',
                    'with_jemalloc', True)
      set_build_var(environ_cp, 'TF_NEED_GCP', 'Google Cloud Platform',
                    'with_gcp_support', True, 'gcp')
      set_build_var(environ_cp, 'TF_NEED_HDFS', 'Hadoop File System',
                    'with_hdfs_support', True, 'hdfs')
      set_build_var(environ_cp, 'TF_NEED_AWS', 'Amazon AWS Platform',
                    'with_aws_support', True, 'aws')
      set_build_var(environ_cp, 'TF_NEED_KAFKA', 'Apache Kafka Platform',
                    'with_kafka_support', True, 'kafka')
      set_build_var(environ_cp, 'TF_ENABLE_XLA', 'XLA JIT', 'with_xla_support',
                    False, 'xla')
      set_build_var(environ_cp, 'TF_NEED_GDR', 'GDR', 'with_gdr_support',
                    False, 'gdr')
      set_build_var(environ_cp, 'TF_NEED_VERBS', 'VERBS', 'with_verbs_support',
                    False, 'verbs')
    

    所以配置过程可以简单的理解,就是各种参数的收集,最后会有3个文件的时间信息更新(即生成或者修改的):

    其中.bazelrc内容如下:

    import /mnt/d/tensorflow/tensorflow-master/.tf_configure.bazelrc
    

    即导入的是在当前文件夹下新生成的文件.tf_configure.bazelrc,而该文件就纪录了配置

    build --action_env PYTHON_BIN_PATH="/home/shouhuxianjian/anaconda3/bin/python"
    build --action_env PYTHON_LIB_PATH="/home/shouhuxianjian/anaconda3/lib/python3.6/site-packages"
    build --python_path="/home/shouhuxianjian/anaconda3/bin/python"
    build --define with_jemalloc=true
    build:gcp --define with_gcp_support=true
    build:hdfs --define with_hdfs_support=true
    build:aws --define with_aws_support=true
    build:kafka --define with_kafka_support=true
    build:xla --define with_xla_support=true
    build:gdr --define with_gdr_support=true
    build:verbs --define with_verbs_support=true
    build --action_env TF_NEED_OPENCL_SYCL="0"
    build --action_env TF_NEED_CUDA="0"
    build --action_env TF_DOWNLOAD_CLANG="0"
    build --define grpc_no_ares=true
    build:opt --copt=-march=native
    build:opt --host_copt=-march=native
    build:opt --define with_default_optimizations=true
    build --strip=always
    

    其中的build:hdfs等形式等效于build --config=hdfs ,见这里的--config
    上述在hdfs,gcp,aws,kafka选择时点击了N,如果点击Y则会变换成如下形式:

    build --define with_gcp_support=true
    build --define with_hdfs_support=true
    build --define with_aws_support=true
    build --define with_kafka_support=true
    

    可以发现和

    build --define with_jemalloc=true
    

    一样了。而对于bazel而言,如果build:package形式,则编译时候会忽略该包(hdfs包中BUILD内容为:

    # 文档在 tensorflow-master/third_party/hadoop/BUILD
    package(default_visibility = ["//visibility:public"])
    
    licenses(["notice"])  # Apache 2.0
    
    exports_files(["LICENSE.txt"])
    
    cc_library(
        name = "hdfs",
        hdrs = ["hdfs.h"],
    )
    

    所以下面真的调用bazel进行编译的时候,需要显示采用--config=opt来告知bazel,不要忽略opt这个package(这里是为了使用command:name中group这个特性)。

    1.2 再bazel编译

    如果只编译支持cpu的,敲如下代码

    $ bazel build --config=opt //tensorflow/tools/pip_package:build_pip_package
    

    如果需要gpu支持的,敲如下代码:

    $ bazel build --config=opt --config=cuda //tensorflow/tools/pip_package:build_pip_package 
    

    1.2.1 BUILD文件结构格式推荐

    在解读tensorflow-master/tensorflow/tools/pip_package/BUILD的时候,需要温习bazel构建时候的函数大全,还有官方推荐的BUILD文件结构格式File structure
    . 如下形式:

    Package description (a comment)
    
    All load() statements
    
    The package() function.
    
    Calls to rules and macros
    

    1.2.2 tensorflow/tools/pip_package/BUILD文件解读

    在下面的tensorflow/tools/pip_package/BUILD文件中,你可以看到package描述,load函数,transitive_hdrs,生成python的pb_binary,内部变量COMMON_PIP_DEPS,filegroup,生成shell的sh_binary和genrule等等。

    # Description:
    #  Tools for building the TensorFlow pip package.
    #  原型:package(default_deprecation, default_testonly, default_visibility, features)
    #  此函数声明适用于包中每个后续规则的元数据。 它最多只能在一个包(BUILD文件)中使用一次。
    #  此函数应该出现文件顶部,在所有load()语句之后,任何规则之前的范围内,调用package()函数。
    #  [package](https://docs.bazel.build/versions/master/be/functions.html#package)
    # private表示后续的规则默认情况下只能在当前包内可见 https://docs.bazel.build/versions/master/be/common-definitions.html#common-attributes
    package(default_visibility = ["//visibility:private"])    
    
    # Bazel的扩展是以.bzl结尾的文件。 通过使用load语句从可以从bazel的扩展文件中导入对应符号到当前BUILD中使用。
    # [load](https://docs.bazel.build/versions/master/skylark/concepts.html)
    load(
        "//tensorflow:tensorflow.bzl",
        "if_not_windows",
        "if_windows",
        "transitive_hdrs",
    )
    load("//third_party/mkl:build_defs.bzl", "if_mkl")
    load("//tensorflow:tensorflow.bzl", "if_cuda")
    load("@local_config_tensorrt//:build_defs.bzl", "if_tensorrt")
    load("//tensorflow/core:platform/default/build_config_root.bzl", "tf_additional_license_deps")
    
    # This returns a list of headers of all public header libraries (e.g.,
    # framework, lib), and all of the transitive dependencies of those
    # public headers.  Not all of the headers returned by the filegroup
    # are public (e.g., internal headers that are included by public
    # headers), but the internal headers need to be packaged in the
    # pip_package for the public headers to be properly included.
    #
    # Public headers are therefore defined by those that are both:
    #
    # 1) "publicly visible" as defined by bazel
    # 2) Have documentation.
    #
    # This matches the policy of "public" for our python API.
    transitive_hdrs(
        name = "included_headers",
        deps = [
            "//tensorflow/core:core_cpu",
            "//tensorflow/core:framework",
            "//tensorflow/core:lib",
            "//tensorflow/core:protos_all_cc",
            "//tensorflow/core:stream_executor",
            "//third_party/eigen3",
        ] + if_cuda([
            "@local_config_cuda//cuda:cuda_headers",
        ]),
    )
    
    py_binary(
        name = "simple_console",
        srcs = ["simple_console.py"],
        srcs_version = "PY2AND3",
        deps = ["//tensorflow:tensorflow_py"],
    )
    
    COMMON_PIP_DEPS = [
        ":licenses",
        "MANIFEST.in",
        "README",
        "setup.py",
        ":included_headers",
        "//tensorflow:tensorflow_py",
        "//tensorflow/contrib/autograph:autograph",
        "//tensorflow/contrib/autograph/converters:converters",
        "//tensorflow/contrib/autograph/converters:test_lib",
        "//tensorflow/contrib/autograph/impl:impl",
        "//tensorflow/contrib/autograph/pyct:pyct",
        "//tensorflow/contrib/autograph/pyct/static_analysis:static_analysis",
        "//tensorflow/contrib/boosted_trees:boosted_trees_pip",
        "//tensorflow/contrib/cluster_resolver:cluster_resolver_pip",
        "//tensorflow/contrib/data/python/kernel_tests:dataset_serialization_test",
        "//tensorflow/contrib/data/python/ops:contrib_op_loader",
        "//tensorflow/contrib/eager/python/examples:examples_pip",
        "//tensorflow/contrib/eager/python:checkpointable_utils",
        "//tensorflow/contrib/eager/python:evaluator",
        "//tensorflow/contrib/gan:gan",
        "//tensorflow/contrib/graph_editor:graph_editor_pip",
        "//tensorflow/contrib/keras:keras",
        "//tensorflow/contrib/labeled_tensor:labeled_tensor_pip",
        "//tensorflow/contrib/nn:nn_py",
        "//tensorflow/contrib/predictor:predictor_pip",
        "//tensorflow/contrib/proto:proto_pip",
        "//tensorflow/contrib/receptive_field:receptive_field_pip",
        "//tensorflow/contrib/rpc:rpc_pip",
        "//tensorflow/contrib/session_bundle:session_bundle_pip",
        "//tensorflow/contrib/signal:signal_py",
        "//tensorflow/contrib/signal:test_util",
        "//tensorflow/contrib/slim:slim",
        "//tensorflow/contrib/slim/python/slim/data:data_pip",
        "//tensorflow/contrib/slim/python/slim/nets:nets_pip",
        "//tensorflow/contrib/specs:specs",
        "//tensorflow/contrib/summary:summary_test_util",
        "//tensorflow/contrib/tensor_forest:init_py",
        "//tensorflow/contrib/tensor_forest/hybrid:hybrid_pip",
        "//tensorflow/contrib/timeseries:timeseries_pip",
        "//tensorflow/contrib/tpu",
        "//tensorflow/examples/tutorials/mnist:package",
        "//tensorflow/python:distributed_framework_test_lib",
        "//tensorflow/python:meta_graph_testdata",
        "//tensorflow/python:spectral_ops_test_util",
        "//tensorflow/python:util_example_parser_configuration",
        "//tensorflow/python/debug:debug_pip",
        "//tensorflow/python/eager:eager_pip",
        "//tensorflow/python/kernel_tests/testdata:self_adjoint_eig_op_test_files",
        "//tensorflow/python/saved_model:saved_model",
        "//tensorflow/python/tools:tools_pip",
        "//tensorflow/python:test_ops",
        "//tensorflow/tools/dist_test/server:grpc_tensorflow_server",
    ]
    
    # On Windows, python binary is a zip file of runfiles tree.
    # Add everything to its data dependency for generating a runfiles tree
    # for building the pip package on Windows.
    py_binary(
        name = "simple_console_for_windows",
        srcs = ["simple_console_for_windows.py"],
        data = COMMON_PIP_DEPS,
        srcs_version = "PY2AND3",
        deps = ["//tensorflow:tensorflow_py"],
    )
    
    filegroup(
        name = "licenses",
        data = [
            "//third_party/eigen3:LICENSE",
            "//third_party/fft2d:LICENSE",
            "//third_party/hadoop:LICENSE.txt",
            "@absl_py//absl/flags:LICENSE",
            "@arm_neon_2_x86_sse//:LICENSE",
            "@astor_archive//:LICENSE",
            "@aws//:LICENSE",
            "@boringssl//:LICENSE",
            "@com_google_absl//:LICENSE",
            "@com_googlesource_code_re2//:LICENSE",
            "@cub_archive//:LICENSE.TXT",
            "@curl//:COPYING",
            "@eigen_archive//:COPYING.MPL2",
            "@farmhash_archive//:COPYING",
            "@fft2d//:fft/readme.txt",
            "@flatbuffers//:LICENSE.txt",
            "@gast_archive//:PKG-INFO",
            "@gemmlowp//:LICENSE",
            "@gif_archive//:COPYING",
            "@grpc//:LICENSE",
            "@highwayhash//:LICENSE",
            "@jemalloc//:COPYING",
            "@jpeg//:LICENSE.md",
            "@kafka//:LICENSE",
            "@libxsmm_archive//:LICENSE",
            "@lmdb//:LICENSE",
            "@local_config_nccl//:LICENSE",
            "@local_config_sycl//sycl:LICENSE.text",
            "@grpc//third_party/nanopb:LICENSE.txt",
            "@grpc//third_party/address_sorting:LICENSE",
            "@nasm//:LICENSE",
            "@nsync//:LICENSE",
            "@pcre//:LICENCE",
            "@png_archive//:LICENSE",
            "@protobuf_archive//:LICENSE",
            "@six_archive//:LICENSE",
            "@snappy//:COPYING",
            "@swig//:LICENSE",
            "@termcolor_archive//:COPYING.txt",
            "@zlib_archive//:zlib.h",
            "@org_python_pypi_backports_weakref//:LICENSE",
        ] + if_mkl([
            "//third_party/mkl:LICENSE",
        ]) + tf_additional_license_deps(),
    )
    
    # 对应的shell二进制规则,其中涉及到了select(主要用来做平台依赖选择),在bazel的编译命令中,并未显式的指定build_pip_package的属性,所以这里采用了默认的条件
    # [select](https://docs.bazel.build/versions/master/skylark/lib/globals.html#select)
    # [select](https://docs.bazel.build/versions/master/be/functions.html#select)
    sh_binary(
        name = "build_pip_package",
        srcs = ["build_pip_package.sh"],
        data = select({
            "//tensorflow:windows": [":simple_console_for_windows"],
            "//tensorflow:windows_msvc": [":simple_console_for_windows"],
            "//conditions:default": COMMON_PIP_DEPS + [
                ":simple_console",
                "//tensorflow/contrib/lite/python:interpreter_test_data",
                "//tensorflow/contrib/lite/python:tf_lite_py_pip",
                "//tensorflow/contrib/lite/toco:toco",
                "//tensorflow/contrib/lite/toco/python:toco_wrapper",
                "//tensorflow/contrib/lite/toco/python:toco_from_protos",
            ],
        }) + if_mkl(["//third_party/mkl:intel_binary_blob"]) + if_tensorrt([
            "//tensorflow/contrib/tensorrt:init_py",
        ]),
    )
    
    # A genrule for generating a marker file for the pip package on Windows
    #
    # This only works on Windows, because :simple_console_for_windows is a
    # python zip file containing everything we need for building the pip package.
    # However, on other platforms, due to https://github.com/bazelbuild/bazel/issues/4223,
    # when C++ extensions change, this generule doesn't rebuild.
    genrule(
        name = "win_pip_package_marker",
        srcs = if_windows([
            ":build_pip_package",
            ":simple_console_for_windows",
        ]),
        outs = ["win_pip_package_marker_file"],
        cmd = select({
            "//conditions:default": "touch $@",
            "//tensorflow:windows": "md5sum $(locations :build_pip_package) $(locations :simple_console_for_windows) > $@",
        }),
        visibility = ["//visibility:public"],
    )
    

    1.2.3 编译build_pip_package的过程

    因编译命令显式的编译build_pip_package,对应上述文件中的sh_binary。sh_binary中主要负责依赖的data的生成,其中基于平台依赖选用了select函数,且bazel命令行中并未对当前build_pip_package做显式的选择,所以读取默认配置,

     COMMON_PIP_DEPS + [
                ":simple_console",
                "//tensorflow/contrib/lite/python:interpreter_test_data",
                "//tensorflow/contrib/lite/python:tf_lite_py_pip",
                "//tensorflow/contrib/lite/toco:toco",
                "//tensorflow/contrib/lite/toco/python:toco_wrapper",
                "//tensorflow/contrib/lite/toco/python:toco_from_protos",
            ]
    

    那么现在焦点就转移到COMMON_PIP_DEPS 部分了。该变量中,一开始的三个文件MANIFEST.in、README、setup.py是直接存在的,因此不会有什么操作。然后我们看下一行的

    :included_headers
    

    这里表示当前范围内的target,所以是对应的

    # This matches the policy of "public" for our python API.
    transitive_hdrs(
        name = "included_headers",
        deps = [
            "//tensorflow/core:core_cpu",
            "//tensorflow/core:framework",
            "//tensorflow/core:lib",
            "//tensorflow/core:protos_all_cc",
            "//tensorflow/core:stream_executor",
            "//third_party/eigen3",
        ] + if_cuda([
            "@local_config_cuda//cuda:cuda_headers",
        ]),
    )
    

    而transitive_hdrs 并不是关键字类型的函数,是由上面的load导入的

    load(
        "//tensorflow:tensorflow.bzl",
        "if_not_windows",
        "if_windows",
        "transitive_hdrs",
    )
    

    transitive_hdrs在tensorflow:tensorflow.bzl中的实现为

    # Bazel rule for collecting the header files that a target depends on.
    def _transitive_hdrs_impl(ctx):
      outputs = depset()
      for dep in ctx.attr.deps:
        outputs += dep.cc.transitive_headers
      return struct(files=outputs)
    
    # 这里调用了对应的rule函数
    # [rule](https://docs.bazel.build/versions/master/skylark/lib/globals.html#rule)
    _transitive_hdrs = rule(
        attrs = {
            "deps": attr.label_list(
                allow_files = True,
                providers = ["cc"],
            ),
        },
        implementation = _transitive_hdrs_impl,
    )
    # transitive_hdrs所在的位置,其通过内部的_transitive_hdrs规则,而该规则是通过_transitive_hdrs_impl 实现的
    def transitive_hdrs(name, deps=[], **kwargs):
      _transitive_hdrs(name=name + "_gather", deps=deps)
      native.filegroup(name=name, srcs=[":" + name + "_gather"])
    
    

    这部分先放下,我们接着找和cpp交互的部分。我们先关注下,接下来的

    "//tensorflow:tensorflow_py",
    

    当前WORKSPACE所在的位置为根位置//,后面的tensorflow表示对应的tensorflow文件夹,后面的tensorflow_py可以定位到文件tensorflow/BUILD中

    # 当前文件为tensorflow/BUILD的539-548行
    py_library(
        name = "tensorflow_py",
        srcs = ["__init__.py"],
        srcs_version = "PY2AND3",
        visibility = ["//visibility:public"],
        deps = [
            "//tensorflow/python",
            "//tensorflow/tools/api/generator:python_api",
        ],
    )
    

    这里依赖于//tensorflow/python这个包,这个包依赖于tensorflow/python/BUILD进行生成,其内部

    # 当前文件为tensorflow/python/BUILD
    py_library(
        name = "python",
        srcs = ["__init__.py"],
        srcs_version = "PY2AND3",
        visibility = [
            "//tensorflow:__pkg__",
            "//tensorflow/compiler/aot/tests:__pkg__",  # TODO(b/34059704): remove when fixed
            "//tensorflow/contrib/learn:__pkg__",  # TODO(b/34059704): remove when fixed
            "//tensorflow/contrib/learn/python/learn/datasets:__pkg__",  # TODO(b/34059704): remove when fixed
            "//tensorflow/contrib/lite/toco/python:__pkg__",  # TODO(b/34059704): remove when fixed
            "//tensorflow/python/debug:__pkg__",  # TODO(b/34059704): remove when fixed
            "//tensorflow/python/tools:__pkg__",  # TODO(b/34059704): remove when fixed
            "//tensorflow/tools/api/generator:__pkg__",
            "//tensorflow/tools/quantization:__pkg__",  # TODO(b/34059704): remove when fixed
        ],
        deps = [
            ":no_contrib",
            "//tensorflow/contrib:contrib_py",
        ],
    )
    

    这里依赖于:no_contrib 这个target,那么我们关注下

    # 当前文件为tensorflow/python/BUILD
    py_library(
        name = "no_contrib",
        srcs = ["__init__.py"],
        srcs_version = "PY2AND3",
        visibility = [
            "//tensorflow:__pkg__",
        ],
        deps = [
            ":array_ops",
            ":bitwise_ops",
            ":boosted_trees_ops",
            ":check_ops",
            ":client",
            ":client_testlib",
            ":confusion_matrix",
            ":control_flow_ops",
            ":cudnn_rnn_ops_gen",
            ":errors",
            ":framework",
            ":framework_for_generated_wrappers",
            ":functional_ops",
            ":gradient_checker",
            ":graph_util",
            ":histogram_ops",
            ":image_ops",
            ":initializers_ns",
            ":io_ops",
            ":layers",
            ":lib",
            ":list_ops",
            ":manip_ops",
            ":math_ops",
            ":metrics",
            ":nn",
            ":ops",
            ":platform",
            ":pywrap_tensorflow",
            ":saver_test_utils",
            ":script_ops",
            ":session_ops",
            ":sets",
            ":sparse_ops",
            ":spectral_ops",
            ":spectral_ops_test_util",
            ":standard_ops",
            ":state_ops",
            ":string_ops",
            ":subscribe",
            ":summary",
            ":tensor_array_ops",
            ":test_ops",  # TODO: Break testing code out into separate rule.
            ":tf_cluster",
            ":tf_item",
            ":tf_optimizer",
            ":training",
            ":util",
            ":weights_broadcast_ops",
            "//tensorflow/core:protos_all_py",
            "//tensorflow/python/data",
            "//tensorflow/python/estimator:estimator_py",
            "//tensorflow/python/feature_column:feature_column_py",
            "//tensorflow/python/keras",
            "//tensorflow/python/ops/distributions",
            "//tensorflow/python/ops/linalg",
            "//tensorflow/python/ops/losses",
            "//tensorflow/python/profiler",
            "//tensorflow/python/saved_model",
            "//third_party/py/numpy",
        ],
    )
    

    我们也跟随.如何阅读TensorFlow源码去找pywrap_tensorflow这个部分,其中pywrap_tensorflow target依赖于pywrap_tensorflow_internal这个target的,而pywrap_tensorflow_internal就是通过swig从cc文件生成对应的python接口文件部分了

    # 当前文件为tensorflow/python/BUILD 3421行
    py_library(
        name = "pywrap_tensorflow",
        srcs = [
            "pywrap_tensorflow.py",
        ] + if_static(
            ["pywrap_dlopen_global_flags.py"],
            # Import will fail, indicating no global dlopen flags
            otherwise = [],
        ),
        srcs_version = "PY2AND3",
        deps = [":pywrap_tensorflow_internal"],
    )
    tf_py_wrap_cc(
        name = "pywrap_tensorflow_internal",
        srcs = ["tensorflow.i"],
        swig_includes = [
            "client/device_lib.i",
            "client/events_writer.i",
            "client/tf_session.i",
            "client/tf_sessionrun_wrapper.i",
            "framework/cpp_shape_inference.i",
            "framework/python_op_gen.i",
            "grappler/cluster.i",
            "grappler/cost_analyzer.i",
            "grappler/item.i",
            "grappler/model_analyzer.i",
            "grappler/tf_optimizer.i",
            "lib/core/bfloat16.i",
            "lib/core/py_exception_registry.i",
            "lib/core/py_func.i",
            "lib/core/strings.i",
            "lib/io/file_io.i",
            "lib/io/py_record_reader.i",
            "lib/io/py_record_writer.i",
            "platform/base.i",
            "platform/stacktrace_handler.i",
            "pywrap_tfe.i",
            "training/quantize_training.i",
            "training/server_lib.i",
            "util/kernel_registry.i",
            "util/port.i",
            "util/py_checkpoint_reader.i",
            "util/stat_summarizer.i",
            "util/tfprof.i",
            "util/transform_graph.i",
            "util/util.i",
        ],
        win_def_file = select({
            "//tensorflow:windows": ":pywrap_tensorflow_filtered_def_file",
            "//conditions:default": None,
        }),
        deps = [
            ":bfloat16_lib",
            ":cost_analyzer_lib",
            ":model_analyzer_lib",
            ":cpp_python_util",
            ":cpp_shape_inference",
            ":kernel_registry",
            ":numpy_lib",
            ":safe_ptr",
            ":py_exception_registry",
            ":py_func_lib",
            ":py_record_reader_lib",
            ":py_record_writer_lib",
            ":python_op_gen",
            ":tf_session_helper",
            "//tensorflow/c:c_api",
            "//tensorflow/c:checkpoint_reader",
            "//tensorflow/c:python_api",
            "//tensorflow/c:tf_status_helper",
            "//tensorflow/c/eager:c_api",
            "//tensorflow/core/distributed_runtime/rpc:grpc_rpc_factory_registration",
            "//tensorflow/core/distributed_runtime/rpc:grpc_server_lib",
            "//tensorflow/core/distributed_runtime/rpc:grpc_session",
            "//tensorflow/core/grappler:grappler_item",
            "//tensorflow/core/grappler:grappler_item_builder",
            "//tensorflow/core/grappler/clusters:cluster",
            "//tensorflow/core/grappler/clusters:single_machine",
            "//tensorflow/core/grappler/clusters:virtual_cluster",
            "//tensorflow/core/grappler/costs:graph_memory",
            "//tensorflow/core/grappler/optimizers:meta_optimizer",
            "//tensorflow/core:lib",
            "//tensorflow/core:reader_base",
            "//tensorflow/core/debug",
            "//tensorflow/core/distributed_runtime:server_lib",
            "//tensorflow/core/profiler/internal:print_model_analysis",
            "//tensorflow/tools/graph_transforms:transform_graph_lib",
            "//tensorflow/python/eager:pywrap_tfe_lib",
            "//tensorflow/python/eager:python_eager_op_gen",
            "//util/python:python_headers",
        ] + (tf_additional_lib_deps() +
             tf_additional_plugin_deps() +
             tf_additional_verbs_deps() +
             tf_additional_mpi_deps() +
             tf_additional_gdr_deps()),
    )
    

    而tf_py_wrap_cc不是在bazel内置的规则中,所以是tensorflow自定义的一个规则,通过

    load("//tensorflow:tensorflow.bzl", "tf_py_wrap_cc")
    

    找到其实现为

    # 此文件为tensorflow/tensorflow.bzl   1404行
    def tf_py_wrap_cc(name,
                                 srcs,
                                 swig_includes=[],
                                 deps=[],
                                 copts=[],
                                 **kwargs):
      module_name = name.split("/")[-1]
      # Convert a rule name such as foo/bar/baz to foo/bar/_baz.so
      # and use that as the name for the rule producing the .so file.
      cc_library_name = "/".join(name.split("/")[:-1] + ["_" + module_name + ".so"])
      cc_library_pyd_name = "/".join(
          name.split("/")[:-1] + ["_" + module_name + ".pyd"])
      extra_deps = []
      _py_wrap_cc(
          name=name + "_py_wrap",
          srcs=srcs,
          swig_includes=swig_includes,
          deps=deps + extra_deps,
          toolchain_deps=["//tools/defaults:crosstool"],
          module_name=module_name,
          py_module_name=name)
      vscriptname=name+"_versionscript"
      _append_init_to_versionscript(
          name=vscriptname,
          module_name=module_name,
          is_version_script=select({
              "@local_config_cuda//cuda:darwin":False,
              "//conditions:default":True,
              }),
          template_file=select({
              "@local_config_cuda//cuda:darwin":clean_dep("//tensorflow:tf_exported_symbols.lds"),
              "//conditions:default":clean_dep("//tensorflow:tf_version_script.lds")
          })
      )
      extra_linkopts = select({
          "@local_config_cuda//cuda:darwin": [
              "-Wl,-exported_symbols_list",
              "%s.lds"%vscriptname,
          ],
          clean_dep("//tensorflow:windows"): [],
          clean_dep("//tensorflow:windows_msvc"): [],
          "//conditions:default": [
              "-Wl,--version-script",
              "%s.lds"%vscriptname,
          ]
      })
      extra_deps += select({
          "@local_config_cuda//cuda:darwin": [
              "%s.lds"%vscriptname,
          ],
          clean_dep("//tensorflow:windows"): [],
          clean_dep("//tensorflow:windows_msvc"): [],
          "//conditions:default": [
              "%s.lds"%vscriptname,
          ]
      })
    
      tf_cc_shared_object(
          name=cc_library_name,
          srcs=[module_name + ".cc"],
          copts=(copts + if_not_windows([
              "-Wno-self-assign", "-Wno-sign-compare", "-Wno-write-strings"
          ]) + tf_extension_copts()),
          linkopts=tf_extension_linkopts() + extra_linkopts,
          linkstatic=1,
          deps=deps + extra_deps,
          **kwargs)
      native.genrule(
          name="gen_" + cc_library_pyd_name,
          srcs=[":" + cc_library_name],
          outs=[cc_library_pyd_name],
          cmd="cp $< $@",)
      native.py_library(
          name=name,
          srcs=[":" + name + ".py"],
          srcs_version="PY2AND3",
          data=select({
              clean_dep("//tensorflow:windows"): [":" + cc_library_pyd_name],
              "//conditions:default": [":" + cc_library_name],
          }))
    

    因为swig是你编写好对应的example.c文件和example.i文件,然后通过调用swig命令生成example_wrap.c文件,通过gcc编译这2个c文件,就能生成对应的o文件,通过连接生成so文件,这时候就能够被python导入了。
    上述自定义规则中

    • tf_cc_shared_object 负责生成 so文件;
    • 而native.py_library负责???

    _py_wrap_cc则负责执行swig的命令,该自定义规则在同文件的1122行

    # 此文件为tensorflow/tensorflow.bzl   1090行,下面的1122行就是_py_wrap_cc的位置
    # Bazel rules for building swig files.
    def _py_wrap_cc_impl(ctx):
      srcs = ctx.files.srcs
      if len(srcs) != 1:
        fail("Exactly one SWIG source file label must be specified.", "srcs")
      module_name = ctx.attr.module_name
      src = ctx.files.srcs[0]
      inputs = depset([src])
      inputs += ctx.files.swig_includes
      for dep in ctx.attr.deps:
        inputs += dep.cc.transitive_headers
      inputs += ctx.files._swiglib
      inputs += ctx.files.toolchain_deps
      swig_include_dirs = depset(_get_repository_roots(ctx, inputs))
      swig_include_dirs += sorted([f.dirname for f in ctx.files._swiglib])
      args = [
          "-c++", "-python", "-module", module_name, "-o", ctx.outputs.cc_out.path,
          "-outdir", ctx.outputs.py_out.dirname
      ]
      args += ["-l" + f.path for f in ctx.files.swig_includes]
      args += ["-I" + i for i in swig_include_dirs]
      args += [src.path]
      outputs = [ctx.outputs.cc_out, ctx.outputs.py_out]
      ctx.action(
          executable=ctx.executable._swig,
          arguments=args,
          inputs=list(inputs),
          outputs=outputs,
          mnemonic="PythonSwig",
          progress_message="SWIGing " + src.path)
      return struct(files=depset(outputs))
    
    _py_wrap_cc = rule(
        attrs = {
            "srcs": attr.label_list(
                mandatory = True,
                allow_files = True,
            ),
            "swig_includes": attr.label_list(
                cfg = "data",
                allow_files = True,
            ),
            "deps": attr.label_list(
                allow_files = True,
                providers = ["cc"],
            ),
            "toolchain_deps": attr.label_list(
                allow_files = True,
            ),
            "module_name": attr.string(mandatory = True),
            "py_module_name": attr.string(mandatory = True),
            "_swig": attr.label(
                default = Label("@swig//:swig"),
                executable = True,
                cfg = "host",
            ),
            "_swiglib": attr.label(
                default = Label("@swig//:templates"),
                allow_files = True,
            ),
        },
        outputs = {
            "cc_out": "%{module_name}.cc",
            "py_out": "%{py_module_name}.py",
        },
        implementation = _py_wrap_cc_impl,
    )
    

    上述中ctx.executable._swig 是为执行部分,其对应的

            "_swig": attr.label(
                default = Label("@swig//:swig"),
                executable = True,
                cfg = "host",
            ),
    

    而swig就位于third_party/swig.BUILD中

    licenses(["restricted"])  # GPLv3
    
    exports_files(["LICENSE"])
    
    cc_binary(
        name = "swig",
        srcs = [
            "Source/CParse/cparse.h",
            "Source/CParse/cscanner.c",
            "Source/CParse/parser.c",
            "Source/CParse/parser.h",
            "Source/CParse/templ.c",
            "Source/CParse/util.c",
            "Source/DOH/base.c",
            "Source/DOH/doh.h",
            "Source/DOH/dohint.h",
            "Source/DOH/file.c",
            "Source/DOH/fio.c",
            "Source/DOH/hash.c",
            "Source/DOH/list.c",
            "Source/DOH/memory.c",
            "Source/DOH/string.c",
            "Source/DOH/void.c",
            "Source/Include/swigconfig.h",
            "Source/Include/swigwarn.h",
            "Source/Modules/allocate.cxx",
            "Source/Modules/browser.cxx",
            "Source/Modules/contract.cxx",
            "Source/Modules/directors.cxx",
    ......
    

    可以看成swig是需要先定义生成的一个target。这样基本一个流程就串起来了

    • 先生成swig可执行文件
    • 再通过对应i文件生成对应的wrap文件,并进行编译生成对应的so文件和py文件
    • 就可以正常导入了

    1.2.4 如何从python端找到对应的c源码文件

    假设有python文件如下

    import tensorflow as tf
    import numpy as np
     
    x_data = np.random.rand(100).astype(np.float32)
    y_data = x_data * 0.1 + 0.3
     
    W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
    b = tf.Variable(tf.zeros([1]))
    y = W * x_data + b
     
    loss = tf.reduce_mean(tf.square(y - y_data))
    optimizer = tf.train.GradientDescentOptimizer(0.5)
    train = optimizer.minimize(loss)
     
    init = tf.initialize_all_variables()
     
    sess = tf.Session()
    sess.run(init)
     
    for step in range(0, 201):
        sess.run(train)
        if step % 20 == 0:
            print(step, sess.run(W), sess.run(b))
    

    假设想找到Session的位置,最后在/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py中找到对应的类

    1.2.5 python和cpp函数名的对应

    在底层cpp代码中,google采用的是驼峰形式去编写cpp的代码,如AbcDefGh,而前端语言python遵循的是小写下划线的方式,如abc_def_gh,所以这二者中,tensorflow内置了一个转换函数,在
    /tensorflow/python/framework/python_op_gen.cc

    string function_name;
        python_op_gen_internal::GenerateLowerCaseOpName(op_def.name(),
                                                        &function_name);
    

    在后续版本中,该函数的定义迁移到了/tensorflow/python/framework/python_op_gen_internal.cc

    void GenerateLowerCaseOpName(const string& str, string* result) {
      const char joiner = '_';
      const int last_index = str.size() - 1;
      for (int i = 0; i <= last_index; ++i) {
        const char c = str[i];
        // Emit a joiner only if a previous-lower-to-now-upper or a
        // now-upper-to-next-lower transition happens.
        if (isupper(c) && (i > 0)) {
          if (islower(str[i - 1]) || ((i < last_index) && islower(str[i + 1]))) {
            result->push_back(joiner);
          }
        }
        result->push_back(tolower(c));
      }
    }
    

    如我们要找tf.conv2d在cpp中的实现就是去找Conv2d。

    参考资料:

    1. .如何阅读TensorFlow源码
    2. .bazel
    3. .从源码编译tensorflow-官网
  • 相关阅读:
    Redis扩展功能
    Redis持久化
    redis-通讯协议及事件处理机制
    Redis-数据类型与底层数据结构
    分布式集群架构场景化解决方案
    MySQL-运维和第三方工具
    缓存原理&设计
    MySQL性能优化
    MySQL高级-日志常用的工具等等
    Mysql高级——优化
  • 原文地址:https://www.cnblogs.com/shouhuxianjian/p/9416934.html
Copyright © 2011-2022 走看看