diff --git a/tools/bazel.rc b/.bazelrc
similarity index 88%
rename from tools/bazel.rc
rename to .bazelrc
index 1fdf51f53e29c7111cf89c016400b710051cf9c6..17285afdb381018d0054e771475327b1f7ed9866 100644
--- a/tools/bazel.rc
+++ b/.bazelrc
@@ -25,12 +25,14 @@ build --define framework_shared_object=true
# If you would like to use a local MKL instead of downloading, please set the
# environment variable "TF_MKL_ROOT" every time before build.
build:mkl --define=build_with_mkl=true --define=enable_mkl=true
+build:mkl --define=tensorflow_mkldnn_contraction_kernel=0
build:mkl -c opt
# This config option is used to enable MKL-DNN open source library only,
# without depending on MKL binary version.
build:mkl_open_source_only --define=build_with_mkl_dnn_only=true
build:mkl_open_source_only --define=build_with_mkl=true --define=enable_mkl=true
+build:mkl_open_source_only --define=tensorflow_mkldnn_contraction_kernel=0
build:download_clang --crosstool_top=@local_config_download_clang//:toolchain
build:download_clang --define=using_clang=true
@@ -76,10 +78,9 @@ build:nonccl --define=no_nccl_support=true
build --define=use_fast_cpp_protos=true
build --define=allow_oversize_protos=true
-build --define=grpc_no_ares=true
build --spawn_strategy=standalone
-build --genrule_strategy=standalone
+build --strategy=Genrule=standalone
build -c opt
# Other build flags.
@@ -89,7 +90,21 @@ build --define=grpc_no_ares=true
build:dynamic_kernels --define=dynamic_loaded_kernels=true
build:dynamic_kernels --copt=-DAUTOLOAD_DYNAMIC_KERNELS
+# Build TF with C++ 17 features.
+build:c++17 --cxxopt=-std=c++1z
+build:c++17 --cxxopt=-stdlib=libc++
+build:c++1z --cxxopt=-std=c++1z
+build:c++1z --cxxopt=-stdlib=libc++
+
# Default paths for TF_SYSTEM_LIBS
build --define=PREFIX=/usr
build --define=LIBDIR=$(PREFIX)/lib
build --define=INCLUDEDIR=$(PREFIX)/include
+
+# Default options should come above this line
+
+# Options from ./configure
+try-import %workspace%/.tf_configure.bazelrc
+
+# Put user-specific options in .bazelrc.user
+try-import %workspace%/.bazelrc.user
diff --git a/.gitignore b/.gitignore
index 90324058600bee46af56e49028977971848a80de..e1d352c238a1b2d4febe0f5d4a30cfa0c942f7e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
.DS_Store
.ipynb_checkpoints
node_modules
-/.bazelrc
+/.bazelrc.user
/.tf_configure.bazelrc
/bazel-*
/bazel_pip
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4a296f265f7b9521c46d350cec26ff199f43eb6c..b978f89f9e1d79dd4f7481711a59c2b94e8bf01b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -150,41 +150,45 @@ may exist in your changes.
There are two ways to run TensorFlow unit tests.
-1. Using tools and libraries installed directly on your system.
+1. Using tools and libraries installed directly on your system.
- Refer to the
- [CPU-only developer Dockerfile](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/docker/Dockerfile.devel) and
- [GPU developer Dockerfile](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/docker/Dockerfile.devel-gpu)
- for the required packages. Alternatively, use the said
- [Docker images](https://hub.docker.com/r/tensorflow/tensorflow/tags/), e.g.,
- `tensorflow/tensorflow:nightly-devel` and `tensorflow/tensorflow:nightly-devel-gpu`
- for development to avoid installing the packages directly on your system.
+ Refer to the
+ [CPU-only developer Dockerfile](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/docker/Dockerfile.devel)
+ and
+ [GPU developer Dockerfile](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/docker/Dockerfile.devel-gpu)
+ for the required packages. Alternatively, use the said
+ [Docker images](https://hub.docker.com/r/tensorflow/tensorflow/tags/), e.g.,
+ `tensorflow/tensorflow:nightly-devel` and
+ `tensorflow/tensorflow:nightly-devel-gpu` for development to avoid
+ installing the packages directly on your system (in which case remember to
+ change directory from `/root` to `/tensorflow` once you get into the running
+ container so `bazel` can find the `tensorflow` workspace).
- Once you have the packages installed, you can run a specific unit test in
- bazel by doing as follows:
+ Once you have the packages installed, you can run a specific unit test in
+ bazel by doing as follows:
- If the tests are to be run on GPU, add CUDA paths to LD_LIBRARY_PATH and add
- the `cuda` option flag
+ If the tests are to be run on GPU, add CUDA paths to LD_LIBRARY_PATH and add
+ the `cuda` option flag
- ```bash
- export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH"
+ ```bash
+ export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH"
- export flags="--config=opt --config=cuda -k"
- ```
+ export flags="--config=opt --config=cuda -k"
+ ```
- For example, to run all tests under tensorflow/python, do:
+ For example, to run all tests under tensorflow/python, do:
- ```bash
- bazel test ${flags} //tensorflow/python/...
- ```
+ ```bash
+ bazel test ${flags} //tensorflow/python/...
+ ```
-2. Using [Docker](https://www.docker.com) and TensorFlow's CI scripts.
+2. Using [Docker](https://www.docker.com) and TensorFlow's CI scripts.
- ```bash
- # Install Docker first, then this will build and run cpu tests
- tensorflow/tools/ci_build/ci_build.sh CPU bazel test //tensorflow/...
- ```
-
- See
- [TensorFlow Builds](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tools/ci_build) for details.
+ ```bash
+ # Install Docker first, then this will build and run cpu tests
+ tensorflow/tools/ci_build/ci_build.sh CPU bazel test //tensorflow/...
+ ```
+ See
+ [TensorFlow Builds](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tools/ci_build)
+ for details.
diff --git a/README.md b/README.md
index 044174947a094d43a51f7140dd40ec0f17801d40..96a8ecf4f693d5634da63f4ecc6f4e9c35751f5b 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,8 @@ organization for the purposes of conducting machine learning and deep neural
networks research. The system is general enough to be applicable in a wide
variety of other domains, as well.
-TensorFlow provides stable Python API and C APIs as well as without API backwards compatibility guarantee like C++, Go, Java, JavaScript and Swift.
+TensorFlow provides stable Python and C APIs as well as non-guaranteed backwards
+compatible API's for C++, Go, Java, JavaScript and Swift.
Keep up to date with release announcements and security updates by
subscribing to
@@ -57,21 +58,24 @@ Simply run `pip install tf-nightly` or `pip install tf-nightly-gpu` in a clean
environment to install the nightly TensorFlow build. We support CPU and GPU
packages on Linux, Mac, and Windows.
-
#### *Try your first TensorFlow program*
+
```shell
$ python
```
+
```python
>>> import tensorflow as tf
>>> tf.enable_eager_execution()
->>> tf.add(1, 2)
+>>> tf.add(1, 2).numpy()
3
>>> hello = tf.constant('Hello, TensorFlow!')
>>> hello.numpy()
'Hello, TensorFlow!'
```
-Learn more examples about how to do specific tasks in TensorFlow at the [tutorials page of tensorflow.org](https://www.tensorflow.org/tutorials/).
+
+Learn more examples about how to do specific tasks in TensorFlow at the
+[tutorials page of tensorflow.org](https://www.tensorflow.org/tutorials/).
## Contribution guidelines
@@ -113,11 +117,12 @@ The TensorFlow project strives to abide by generally accepted best practices in
Build Type | Status | Artifacts
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------
**IBM s390x** | [](http://ibmz-ci.osuosl.org/job/TensorFlow_IBMZ_CI/) | TBA
-**IBM ppc64le CPU** | [](http://powerci.osuosl.org/job/TensorFlow_PPC64LE_CPU_Build/) | TBA
-**IBM ppc64le GPU** Nightly | [](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Nightly_Artifact/) | [Nightly](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Nightly_Artifact/)
-**IBM ppc64le GPU** Stable Release | [](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Release_Build/) | [Release](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Release_Build/)
+**Linux ppc64le CPU** Nightly | [](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_CPU_Build/) | [Nightly](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_CPU_Nightly_Artifact/)
+**Linux ppc64le CPU** Stable Release | [](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_CPU_Release_Build/) | [Release](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_CPU_Release_Build/)
+**Linux ppc64le GPU** Nightly | [](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Build/) | [Nightly](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Nightly_Artifact/)
+**Linux ppc64le GPU** Stable Release | [](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Release_Build/) | [Release](https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Release_Build/)
**Linux CPU with Intel® MKL-DNN** Nightly | [](https://tensorflow-ci.intel.com/job/tensorflow-mkl-linux-cpu/) | [Nightly](https://tensorflow-ci.intel.com/job/tensorflow-mkl-build-whl-nightly/)
-**Linux CPU with Intel® MKL-DNN** Python 2.7
**Linux CPU with Intel® MKL-DNN** Python 3.4
**Linux CPU with Intel® MKL-DNN** Python 3.5
**Linux CPU with Intel® MKL-DNN** Python 3.6 | [](https://tensorflow-ci.intel.com/job/tensorflow-mkl-build-release-whl/lastStableBuild) | [1.11.0 py2.7](https://storage.googleapis.com/intel-optimized-tensorflow/tensorflow-1.11.0-cp27-cp27mu-linux_x86_64.whl)
[1.11.0 py3.4](https://storage.googleapis.com/intel-optimized-tensorflow/tensorflow-1.11.0-cp34-cp34m-linux_x86_64.whl)
[1.11.0 py3.5](https://storage.googleapis.com/intel-optimized-tensorflow/tensorflow-1.11.0-cp35-cp35m-linux_x86_64.whl)
[1.11.0 py3.6](https://storage.googleapis.com/intel-optimized-tensorflow/tensorflow-1.11.0-cp36-cp36m-linux_x86_64.whl)
+**Linux CPU with Intel® MKL-DNN** Python 2.7
**Linux CPU with Intel® MKL-DNN** Python 3.4
**Linux CPU with Intel® MKL-DNN** Python 3.5
**Linux CPU with Intel® MKL-DNN** Python 3.6 | [](https://tensorflow-ci.intel.com/job/tensorflow-mkl-build-release-whl/lastStableBuild) | [1.12.0 py2.7](https://storage.googleapis.com/intel-optimized-tensorflow/tensorflow-1.12.0-cp27-cp27mu-linux_x86_64.whl)
[1.12.0 py3.4](https://storage.googleapis.com/intel-optimized-tensorflow/tensorflow-1.12.0-cp34-cp34m-linux_x86_64.whl)
[1.12.0 py3.5](https://storage.googleapis.com/intel-optimized-tensorflow/tensorflow-1.12.0-cp35-cp35m-linux_x86_64.whl)
[1.12.0 py3.6](https://storage.googleapis.com/intel-optimized-tensorflow/tensorflow-1.12.0-cp36-cp36m-linux_x86_64.whl)
## For more information
diff --git a/RELEASE.md b/RELEASE.md
index b13b071bd6cf4d3a260c8e248a67d23e1a688498..0a56e6909870e398c9d6349576cd2f8e6734f072 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -7,6 +7,8 @@
Serving.
* Keras models now support evaluating with a `tf.data.Dataset`.
* TensorFlow binaries are built with XLA support linked in by default.
+* Ignite Dataset added to contrib/ignite that allows to work with Apache
+ Ignite.
## Bug Fixes and Other Changes
@@ -280,50 +282,76 @@ Ag Ramesh, Alex Wiltschko, Alexander Pantyukhin, Amogh Mannekote, An Jiaoyang, A
## Bug Fixes and Other Changes
-* `tfe.Network` is deprecated. Please inherit from `tf.keras.Model`.
-* Layered variable names have changed in the following conditions:
- * Using `tf.keras.layers` with custom variable scopes.
- * Using `tf.layers` in a subclassed `tf.keras.Model` class. See
- [here](https://www.tensorflow.org/versions/r1.9/api_docs/python/tf/layers) for more details
-* `tf.data`:
- * `Dataset.from_generator()` now accepts an `args` list, in order to create nested generators.
- * `Dataset.list_files()` now produces determinstic results when `shuffle=False` or a `seed` is passed.
- * `tf.contrib.data.sample_from_datasets()` and `tf.contrib.data.choose_from_datasets()` make it easier to sample or deterministically choose elements from multiple datasets.
- * `tf.contrib.data.make_csv_dataset()` now supports line breaks in quoted strings, and two infrequently used arguments removed.
- * (C++) `DatasetBase::DebugString()` is now `const`.
- * (C++) `DatasetBase::MakeIterator()` has been renamed to `DatasetBase::MakeIteratorInternal()`.
- * (C++) `IteratorBase::Initialize()` method was added to support raising errors during iterator construction.
-* Eager Execution:
- * Added the ability to pause recording operations for gradient computation via `tf.GradientTape.stop_recording`.
- * Updated documentation, introductory notebooks.
-* `tf.keras`:
- * Move Keras code out of _impl folder and remove API files.
- * `tf.keras.Model.save_weights` now saves in TensorFlow format by default.
- * Enable dataset iterators to be passed to `tf.keras.Model` training/eval methods.
-* TensorFlow Debugger (tfdbg) CLI: fix an issue in which the TensorBoard Debugger Plugin could not handle total source file size exceeding gRPC message size limit (4 MB).
-* `tf.contrib`:
- * `tf.contrib.framework.zero_initializer` supports ResourceVariable.
- * Adding "constrained_optimization" to tensorflow/contrib.
-* Other:
- * Add GCS Configuration Ops.
- * Changing signature of `MakeIterator` to enable propagating error status.
- * KL divergence for two Dirichlet distributions.
- * More consistent GcsFileSystem behavior for certain reads past EOF.
- * Update benchmark for tf.scan to match ranges across eager and graph modes.
- * Fixed bug in `tf.reduce_prod gradient` for complex dtypes.
- * Allow the use of '.' in variables (e.g. "hparams.parse('a.b=1.0')"), which would previously raise an error. This will correspond to an attribute name with an embedded '.' symbol (e.g. 'a.b'), which can only be accessed indirectly (e.g. through getattr and setattr). To set this up the user will first need to explicitly add the variable to the hparam object (e.g. "hparams.add_hparam(name='a.b', value=0.0)").
- * Benchmark for tf.scan in graph and eager modes.
- * Added complex128 support to FFT, FFT2D, FFT3D, IFFT, IFFT2D, and IFFT3D.
- * Making ids unique in `nn.embedding_lookup_sparse`. This helps to reduce RPC calls for looking up the embeddings when there are repeated ids in the batch.
- * Support indicator column in boosted trees.
- * Prevent `tf.gradients()` from backpropagating through integer tensors.
- * LinearOperator[1D,2D,3D]Circulant added to `tensorflow.linalg`.
- * Conv3D, Conv3DBackpropInput, Conv3DBackpropFilter now supports arbitrary.
- * Added `tf.train.Checkpoint` for reading/writing object-based checkpoints.
- * Added LinearOperatorKronecker, a dense-free implementation of the Kronecker Product.
- * Allow LinearOperator to broadcast.
- * SavedModelBuilder will now deduplicate asset names that point to files with the same basename and the same contents. Note that this may result in new asset files included in SavedModels in cases where assets with the same name but different contents were previously overwriting each other.
-
+* `tfe.Network` is deprecated. Please inherit from `tf.keras.Model`.
+* Layered variable names have changed in the following conditions:
+ * Using `tf.keras.layers` with custom variable scopes.
+ * Using `tf.layers` in a subclassed `tf.keras.Model` class. See
+ [here](https://www.tensorflow.org/versions/r1.9/api_docs/python/tf/layers)
+ for more details
+* `tf.data`:
+ * `Dataset.from_generator()` now accepts an `args` list, in order to
+ create nested generators.
+ * `Dataset.list_files()` now produces deterministic results when
+ `shuffle=False` or a `seed` is passed.
+ * `tf.contrib.data.sample_from_datasets()` and
+ `tf.contrib.data.choose_from_datasets()` make it easier to sample or
+ deterministically choose elements from multiple datasets.
+ * `tf.contrib.data.make_csv_dataset()` now supports line breaks in quoted
+ strings, and two infrequently used arguments removed.
+ * (C++) `DatasetBase::DebugString()` is now `const`.
+ * (C++) `DatasetBase::MakeIterator()` has been renamed to
+ `DatasetBase::MakeIteratorInternal()`.
+ * (C++) `IteratorBase::Initialize()` method was added to support raising
+ errors during iterator construction.
+* Eager Execution:
+ * Added the ability to pause recording operations for gradient computation
+ via `tf.GradientTape.stop_recording`.
+ * Updated documentation, introductory notebooks.
+* `tf.keras`:
+ * Move Keras code out of _impl folder and remove API files.
+ * `tf.keras.Model.save_weights` now saves in TensorFlow format by default.
+ * Enable dataset iterators to be passed to `tf.keras.Model` training/eval
+ methods.
+* TensorFlow Debugger (tfdbg) CLI: fix an issue in which the TensorBoard
+ Debugger Plugin could not handle total source file size exceeding gRPC
+ message size limit (4 MB).
+* `tf.contrib`:
+ * `tf.contrib.framework.zero_initializer` supports ResourceVariable.
+ * Adding "constrained_optimization" to tensorflow/contrib.
+* Other:
+ * Add GCS Configuration Ops.
+ * Changing signature of `MakeIterator` to enable propagating error status.
+ * KL divergence for two Dirichlet distributions.
+ * More consistent GcsFileSystem behavior for certain reads past EOF.
+ * Update benchmark for tf.scan to match ranges across eager and graph
+ modes.
+ * Fixed bug in `tf.reduce_prod gradient` for complex dtypes.
+ * Allow the use of '.' in variables (e.g. "hparams.parse('a.b=1.0')"),
+ which would previously raise an error. This will correspond to an
+ attribute name with an embedded '.' symbol (e.g. 'a.b'), which can only
+ be accessed indirectly (e.g. through getattr and setattr). To set this
+ up the user will first need to explicitly add the variable to the hparam
+ object (e.g. "hparams.add_hparam(name='a.b', value=0.0)").
+ * Benchmark for tf.scan in graph and eager modes.
+ * Added complex128 support to FFT, FFT2D, FFT3D, IFFT, IFFT2D, and IFFT3D.
+ * Making ids unique in `nn.embedding_lookup_sparse`. This helps to reduce
+ RPC calls for looking up the embeddings when there are repeated ids in
+ the batch.
+ * Support indicator column in boosted trees.
+ * Prevent `tf.gradients()` from backpropagating through integer tensors.
+ * LinearOperator[1D,2D,3D]Circulant added to `tensorflow.linalg`.
+ * Conv3D, Conv3DBackpropInput, Conv3DBackpropFilter now supports
+ arbitrary.
+ * Added `tf.train.Checkpoint` for reading/writing object-based
+ checkpoints.
+ * Added LinearOperatorKronecker, a dense-free implementation of the
+ Kronecker Product.
+ * Allow LinearOperator to broadcast.
+ * SavedModelBuilder will now deduplicate asset names that point to files
+ with the same basename and the same contents. Note that this may result
+ in new asset files included in SavedModels in cases where assets with
+ the same name but different contents were previously overwriting each
+ other.
## Thanks to our Contributors
@@ -821,7 +849,7 @@ answered questions, and were part of inspiring discussions.
* Remove `tf.contrib.data.Iterator.from_dataset()` method. Use
`Dataset.make_initializable_iterator()` instead.
* Remove seldom used and unnecessary `tf.contrib.data.Iterator.dispose_op()`.
-* Reorder some TFGAN loss functions in a non-backwards compatible way.
+* Reorder some TF-GAN loss functions in a non-backwards compatible way.
## Known Issues
* In Python 3, `Dataset.from_generator()` does not support Unicode strings.
diff --git a/WORKSPACE b/WORKSPACE
index 7cc08e0164a202581ad7ebbe107a9e19410e70e4..9f07b9fd47136d058cc4039ed6948db539485039 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,14 +1,14 @@
workspace(name = "org_tensorflow")
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
http_archive(
name = "io_bazel_rules_closure",
- sha256 = "a38539c5b5c358548e75b44141b4ab637bba7c4dc02b46b1f62a96d6433f56ae",
- strip_prefix = "rules_closure-dbb96841cc0a5fb2664c37822803b06dab20c7d1",
+ sha256 = "43c9b882fa921923bcba764453f4058d102bece35a37c9f6383c713004aacff1",
+ strip_prefix = "rules_closure-9889e2348259a5aad7e805547c1a0cf311cfcd91",
urls = [
- "https://mirror.bazel.build/github.com/bazelbuild/rules_closure/archive/dbb96841cc0a5fb2664c37822803b06dab20c7d1.tar.gz",
- "https://github.com/bazelbuild/rules_closure/archive/dbb96841cc0a5fb2664c37822803b06dab20c7d1.tar.gz", # 2018-04-13
+ "https://mirror.bazel.build/github.com/bazelbuild/rules_closure/archive/9889e2348259a5aad7e805547c1a0cf311cfcd91.tar.gz",
+ "https://github.com/bazelbuild/rules_closure/archive/9889e2348259a5aad7e805547c1a0cf311cfcd91.tar.gz", # 2018-12-21
],
)
@@ -16,38 +16,52 @@ load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories")
closure_repositories()
-http_archive(
- name = "base_images_docker",
- sha256 = "e2b1b7254270bb7605e814a9dbf6d1e4ae04a11136ff1714fbfdabe3f87f7cf9",
- strip_prefix = "base-images-docker-12801524f867e657fbb5d1a74f31618aff181ac6",
- urls = ["https://github.com/GoogleCloudPlatform/base-images-docker/archive/12801524f867e657fbb5d1a74f31618aff181ac6.tar.gz"],
-)
+load("//third_party/toolchains/preconfig/generate:archives.bzl",
+ "bazel_toolchains_archive")
-http_archive(
- name = "bazel_toolchains",
- sha256 = "15b5858b1b5541ec44df31b94c3b8672815b31d71215a98398761ea9f4c4eedb",
- strip_prefix = "bazel-toolchains-6200b238c9c2d137c0d9a7262c80cc71d98e692b",
- urls = [
- "https://github.com/bazelbuild/bazel-toolchains/archive/6200b238c9c2d137c0d9a7262c80cc71d98e692b.tar.gz",
- ],
+bazel_toolchains_archive()
+
+load(
+ "@bazel_toolchains//repositories:repositories.bzl",
+ bazel_toolchains_repositories = "repositories",
)
-http_archive(
- name = "io_bazel_rules_docker",
- sha256 = "29d109605e0d6f9c892584f07275b8c9260803bf0c6fcb7de2623b2bedc910bd",
- strip_prefix = "rules_docker-0.5.1",
- urls = ["https://github.com/bazelbuild/rules_docker/archive/v0.5.1.tar.gz"],
+bazel_toolchains_repositories()
+
+load(
+ "@io_bazel_rules_docker//repositories:repositories.bzl",
+ container_repositories = "repositories",
)
-load("//third_party/toolchains/preconfig/generate:workspace.bzl", "remote_config_workspace")
+container_repositories()
+
+load("//third_party/toolchains/preconfig/generate:workspace.bzl",
+ "remote_config_workspace")
remote_config_workspace()
+# Apple and Swift rules.
+http_archive(
+ name = "build_bazel_rules_apple",
+ sha256 = "73b4980a318d203d3307f850e27e66ec5cc8d223147a3475a6f11597eb6438a5",
+ strip_prefix = "rules_apple-0.13.0",
+ urls = ["https://github.com/bazelbuild/rules_apple/archive/0.13.0.tar.gz"],
+)
+http_file(
+ name = "xctestrunner",
+ executable = 1,
+ urls = ["https://github.com/google/xctestrunner/releases/download/0.2.6/ios_test_runner.par"],
+)
+load("@build_bazel_rules_apple//apple:repositories.bzl", "apple_rules_dependencies")
+apple_rules_dependencies()
+load("@build_bazel_rules_swift//swift:repositories.bzl", "swift_rules_dependencies")
+swift_rules_dependencies()
+
# We must check the bazel version before trying to parse any other BUILD
# files, in case the parsing of those build files depends on the bazel
# version we require here.
load("//tensorflow:version_check.bzl", "check_bazel_version_at_least")
-check_bazel_version_at_least("0.15.0")
+check_bazel_version_at_least("0.19.0")
load("//tensorflow:workspace.bzl", "tf_workspace")
@@ -108,4 +122,3 @@ http_archive(
"http://download.tensorflow.org/models/speech_commands_v0.01.zip",
],
)
-
diff --git a/tensorflow/opensource_only/arm_compiler.BUILD b/arm_compiler.BUILD
similarity index 100%
rename from tensorflow/opensource_only/arm_compiler.BUILD
rename to arm_compiler.BUILD
diff --git a/configure.py b/configure.py
index 6c905a0be3d685b5921dfbc5bddfbe6471a82625..3eb09a1ae905b70dc5d02fab7c316f73c79633dd 100644
--- a/configure.py
+++ b/configure.py
@@ -33,7 +33,7 @@ except ImportError:
from distutils.spawn import find_executable as which
# pylint: enable=g-import-not-at-top
-_DEFAULT_CUDA_VERSION = '9.0'
+_DEFAULT_CUDA_VERSION = '10.0'
_DEFAULT_CUDNN_VERSION = '7'
_DEFAULT_CUDA_COMPUTE_CAPABILITIES = '3.5,7.0'
_DEFAULT_CUDA_PATH = '/usr/local/cuda'
@@ -55,6 +55,12 @@ NCCL_LIB_PATHS = [
'lib64/', 'lib/powerpc64le-linux-gnu/', 'lib/x86_64-linux-gnu/', ''
]
+# List of files to be configured for using Bazel on Apple platforms.
+APPLE_BAZEL_FILES = [
+ 'tensorflow/lite/experimental/objc/BUILD',
+ 'tensorflow/lite/experimental/swift/BUILD'
+]
+
if platform.machine() == 'ppc64le':
_DEFAULT_TENSORRT_PATH_LINUX = '/usr/lib/powerpc64le-linux-gnu/'
else:
@@ -255,18 +261,7 @@ def setup_python(environ_cp):
def reset_tf_configure_bazelrc():
"""Reset file that contains customized config settings."""
open(_TF_BAZELRC, 'w').close()
- bazelrc_path = os.path.join(_TF_WORKSPACE_ROOT, '.bazelrc')
-
- data = []
- if os.path.exists(bazelrc_path):
- with open(bazelrc_path, 'r') as f:
- data = f.read().splitlines()
- with open(bazelrc_path, 'w') as f:
- for l in data:
- if _TF_BAZELRC_FILENAME in l:
- continue
- f.write('%s\n' % l)
- f.write('import %%workspace%%/%s\n' % _TF_BAZELRC_FILENAME)
+
def cleanup_makefile():
"""Delete any leftover BUILD files from the Makefile build.
@@ -488,11 +483,14 @@ def check_bazel_version(min_version, max_version):
if curr_version_int < min_version_int:
print('Please upgrade your bazel installation to version %s or higher to '
'build TensorFlow!' % min_version)
- sys.exit(0)
- if curr_version_int > max_version_int:
+ sys.exit(1)
+ if (curr_version_int > max_version_int and
+ 'TF_IGNORE_MAX_BAZEL_VERSION' not in os.environ):
print('Please downgrade your bazel installation to version %s or lower to '
- 'build TensorFlow!' % max_version)
- sys.exit(0)
+ 'build TensorFlow! To downgrade: download the installer for the old '
+ 'version (from https://github.com/bazelbuild/bazel/releases) then '
+ 'run the installer.' % max_version)
+ sys.exit(1)
return curr_version
@@ -794,8 +792,7 @@ def set_gcc_host_compiler_path(environ_cp):
environ_cp,
var_name='GCC_HOST_COMPILER_PATH',
var_default=default_gcc_host_compiler_path,
- ask_for_var=
- 'Please specify which gcc should be used by nvcc as the host compiler.',
+ ask_for_var='Please specify which gcc should be used by nvcc as the host compiler.',
check_success=os.path.exists,
error_msg='Invalid gcc path. %s cannot be found.',
)
@@ -1246,6 +1243,7 @@ def set_tf_nccl_install_path(environ_cp):
environ_cp['TF_NCCL_VERSION'] = tf_nccl_version
write_action_env_to_bazelrc('TF_NCCL_VERSION', tf_nccl_version)
+
def get_native_cuda_compute_capabilities(environ_cp):
"""Get native cuda compute capabilities.
@@ -1282,13 +1280,15 @@ def set_tf_cuda_compute_capabilities(environ_cp):
ask_cuda_compute_capabilities = (
'Please specify a list of comma-separated '
- 'Cuda compute capabilities you want to '
+ 'CUDA compute capabilities you want to '
'build with.\nYou can find the compute '
'capability of your device at: '
'https://developer.nvidia.com/cuda-gpus.\nPlease'
' note that each additional compute '
'capability significantly increases your '
- 'build time and binary size. [Default is: %s]: ' %
+ 'build time and binary size, and that '
+ 'TensorFlow only supports compute '
+ 'capabilities >= 3.5 [Default is: %s]: ' %
default_cuda_compute_capabilities)
tf_cuda_compute_capabilities = get_from_env_or_user_or_default(
environ_cp, 'TF_CUDA_COMPUTE_CAPABILITIES',
@@ -1301,12 +1301,14 @@ def set_tf_cuda_compute_capabilities(environ_cp):
for compute_capability in tf_cuda_compute_capabilities.split(','):
m = re.match('[0-9]+.[0-9]+', compute_capability)
if not m:
- print('Invalid compute capability: ' % compute_capability)
+ print('Invalid compute capability: %s' % compute_capability)
all_valid = False
else:
- ver = int(m.group(0).split('.')[0])
- if ver < 3:
- print('Only compute capabilities 3.0 or higher are supported.')
+ ver = float(m.group(0))
+ if ver < 3.5:
+ print('ERROR: TensorFlow only supports CUDA compute capabilities 3.5 '
+ 'and higher. Please re-specify the list of compute '
+ 'capabilities excluding version %s.' % ver)
all_valid = False
if all_valid:
@@ -1491,7 +1493,35 @@ def set_other_mpi_vars(environ_cp):
else:
raise ValueError(
'Cannot find the MPI library file in %s/lib or %s/lib64 or %s/lib32' %
- mpi_home, mpi_home, mpi_home)
+ (mpi_home, mpi_home, mpi_home))
+
+def system_specific_test_config(env):
+ """Add default test flags required for TF tests to bazelrc."""
+ write_to_bazelrc('test --flaky_test_attempts=3')
+ write_to_bazelrc('test --test_size_filters=small,medium')
+ write_to_bazelrc(
+ 'test --test_tag_filters=-benchmark-test,-no_oss,-oss_serial')
+ write_to_bazelrc('test --build_tag_filters=-benchmark-test,-no_oss')
+ if is_windows():
+ if env.get('TF_NEED_CUDA', None) == 1:
+ write_to_bazelrc(
+ 'test --test_tag_filters=-no_windows,-no_windows_gpu,-no_gpu')
+ write_to_bazelrc(
+ 'test --build_tag_filters=-no_windows,-no_windows_gpu,-no_gpu')
+ else:
+ write_to_bazelrc('test --test_tag_filters=-no_windows,-gpu')
+ write_to_bazelrc('test --build_tag_filters=-no_windows,-gpu')
+ elif is_macos():
+ write_to_bazelrc('test --test_tag_filters=-gpu,-nomac,-no_mac')
+ write_to_bazelrc('test --build_tag_filters=-gpu,-nomac,-no_mac')
+ elif is_linux():
+ if env.get('TF_NEED_CUDA', None) == 1:
+ write_to_bazelrc('test --test_tag_filters=-no_gpu')
+ write_to_bazelrc('test --build_tag_filters=-no_gpu')
+ write_to_bazelrc('test --test_env=LD_LIBRARY_PATH')
+ else:
+ write_to_bazelrc('test --test_tag_filters=-gpu')
+ write_to_bazelrc('test --build_tag_filters=-gpu')
def set_system_libs_flag(environ_cp):
@@ -1522,10 +1552,6 @@ def set_windows_build_flags(environ_cp):
# The host and target platforms are the same in Windows build. So we don't
# have to distinct them. This avoids building the same targets twice.
write_to_bazelrc('build --distinct_host_configuration=false')
- # Enable short object file path to avoid long path issue on Windows.
- # TODO(pcloudy): Remove this flag when upgrading Bazel to 0.16.0
- # Short object file path will be enabled by default.
- write_to_bazelrc('build --experimental_shortened_obj_file_path=true')
if get_var(
environ_cp, 'TF_OVERRIDE_EIGEN_STRONG_INLINE', 'Eigen strong inline',
@@ -1546,6 +1572,23 @@ def config_info_line(name, help_text):
print('\t--config=%-12s\t# %s' % (name, help_text))
+def configure_apple_bazel_rules():
+ """Configures Bazel rules for building on Apple platforms.
+
+ Enables analyzing and building Apple Bazel rules on Apple platforms. This
+ function will only be executed if `is_macos()` is true.
+ """
+ if not is_macos():
+ return
+ for filepath in APPLE_BAZEL_FILES:
+ print(
+ 'Configuring %s file to analyze and build Bazel rules on Apple platforms.'
+ % filepath)
+ existing_filepath = os.path.join(_TF_WORKSPACE_ROOT, filepath + '.apple')
+ renamed_filepath = os.path.join(_TF_WORKSPACE_ROOT, filepath)
+ os.rename(existing_filepath, renamed_filepath)
+
+
def main():
global _TF_WORKSPACE_ROOT
global _TF_BAZELRC
@@ -1565,11 +1608,9 @@ def main():
# environment variables.
environ_cp = dict(os.environ)
- check_bazel_version('0.15.0', '0.20.0')
+ check_bazel_version('0.19.0', '0.22.0')
reset_tf_configure_bazelrc()
- # Explicitly import tools/bazel.rc, this is needed for Bazel 0.19.0 or later
- write_to_bazelrc('import %workspace%/tools/bazel.rc')
cleanup_makefile()
setup_python(environ_cp)
@@ -1588,6 +1629,8 @@ def main():
if is_macos():
environ_cp['TF_NEED_TENSORRT'] = '0'
+ else:
+ environ_cp['TF_CONFIGURE_APPLE_BAZEL_RULES'] = '0'
# The numpy package on ppc64le uses OpenBLAS which has multi-threading
# issues that lead to incorrect answers. Set OMP_NUM_THREADS=1 at
@@ -1690,6 +1733,16 @@ def main():
create_android_ndk_rule(environ_cp)
create_android_sdk_rule(environ_cp)
+ system_specific_test_config(os.environ)
+
+ if get_var(
+ environ_cp, 'TF_CONFIGURE_APPLE_BAZEL_RULES',
+ 'Configure Bazel rules for Apple platforms', False,
+ ('Would you like to configure Bazel rules for building on Apple platforms?'
+ ), 'Configuring Bazel rules for Apple platforms.',
+ 'Not configuring Bazel rules for Apple platforms.'):
+ configure_apple_bazel_rules()
+
print('Preconfigured Bazel build configs. You can use any of the below by '
'adding "--config=<>" to your build command. See .bazelrc for more '
'details.')
@@ -1698,14 +1751,15 @@ def main():
config_info_line('gdr', 'Build with GDR support.')
config_info_line('verbs', 'Build with libverbs support.')
config_info_line('ngraph', 'Build with Intel nGraph support.')
- config_info_line('dynamic_kernels',
- '(Experimental) Build kernels into separate shared objects.')
+ config_info_line(
+ 'dynamic_kernels',
+ '(Experimental) Build kernels into separate shared objects.')
print('Preconfigured Bazel build configs to DISABLE default on features:')
config_info_line('noaws', 'Disable AWS S3 filesystem support.')
config_info_line('nogcp', 'Disable GCP support.')
config_info_line('nohdfs', 'Disable HDFS support.')
- config_info_line('noignite', 'Disable Apacha Ignite support.')
+ config_info_line('noignite', 'Disable Apache Ignite support.')
config_info_line('nokafka', 'Disable Apache Kafka support.')
config_info_line('nonccl', 'Disable NVIDIA NCCL support.')
diff --git a/tensorflow/BUILD b/tensorflow/BUILD
index fd4b94202aad24a82abef8abd16431f61a8326f0..f53982f1efc9885cc12dcc672ad819c762aca378 100644
--- a/tensorflow/BUILD
+++ b/tensorflow/BUILD
@@ -40,12 +40,16 @@ load(
# @unused
TENSORFLOW_API_INIT_FILES_V2 = (
- TENSORFLOW_API_INIT_FILES + get_compat_files(TENSORFLOW_API_INIT_FILES_V1, 1)
+ TENSORFLOW_API_INIT_FILES +
+ get_compat_files(TENSORFLOW_API_INIT_FILES, 2) +
+ get_compat_files(TENSORFLOW_API_INIT_FILES_V1, 1)
)
# @unused
-TENSORFLOW_API_INIT_FILES_V1_WITH_COMPAT = (
- TENSORFLOW_API_INIT_FILES_V1 + get_compat_files(TENSORFLOW_API_INIT_FILES_V1, 1)
+TENSORFLOW_API_INIT_FILES_V1 = (
+ TENSORFLOW_API_INIT_FILES_V1 +
+ get_compat_files(TENSORFLOW_API_INIT_FILES, 2) +
+ get_compat_files(TENSORFLOW_API_INIT_FILES_V1, 1)
)
# Config setting used when building for products
@@ -90,6 +94,12 @@ config_setting(
visibility = ["//visibility:public"],
)
+config_setting(
+ name = "emscripten",
+ values = {"crosstool_top": "//external:android/emscripten"},
+ visibility = ["//visibility:public"],
+)
+
config_setting(
name = "raspberry_pi_armeabi",
values = {
@@ -202,6 +212,12 @@ config_setting(
visibility = ["//visibility:public"],
)
+config_setting(
+ name = "arm",
+ values = {"cpu": "arm"},
+ visibility = ["//visibility:public"],
+)
+
config_setting(
name = "freebsd",
values = {"cpu": "freebsd"},
@@ -267,6 +283,15 @@ config_setting(
visibility = ["//visibility:public"],
)
+# By default, XLA GPU is compiled into tensorflow when building with
+# --config=cuda even when `with_xla_support` is false. The config setting
+# here allows us to override the behavior if needed.
+config_setting(
+ name = "no_xla_deps_in_cuda",
+ define_values = {"no_xla_deps_in_cuda": "true"},
+ visibility = ["//visibility:public"],
+)
+
config_setting(
name = "with_gdr_support",
define_values = {"with_gdr_support": "true"},
@@ -328,6 +353,13 @@ config_setting(
},
)
+config_setting(
+ name = "using_rocm_hipcc",
+ define_values = {
+ "using_rocm_hipcc": "true",
+ },
+)
+
config_setting(
name = "with_mpi_support",
values = {"define": "with_mpi_support=true"},
@@ -355,17 +387,18 @@ config_setting(
define_values = {"tf_api_version": "2"},
)
+# This flag is defined for select statements that match both
+# on 'windows' and 'api_version_2'. In this case, bazel requires
+# having a flag which is a superset of these two.
+config_setting(
+ name = "windows_and_api_version_2",
+ define_values = {"tf_api_version": "2"},
+ values = {"cpu": "x64_windows"},
+)
+
package_group(
name = "internal",
- packages = [
- "-//third_party/tensorflow/python/estimator",
- "//learning/meta_rank/...",
- "//tensorflow/...",
- "//tensorflow_estimator/contrib/...",
- "//tensorflow_fold/llgtm/...",
- "//tensorflow_text/...",
- "//third_party/py/tensor2tensor/...",
- ],
+ packages = ["//tensorflow/..."],
)
load(
@@ -429,8 +462,7 @@ tf_cc_shared_object(
"//tensorflow:darwin": [],
"//tensorflow:windows": [],
"//conditions:default": [
- "-Wl,--version-script", # This line must be directly followed by the version_script.lds file
- "$(location //tensorflow:tf_framework_version_script.lds)",
+ "-Wl,--version-script,$(location //tensorflow:tf_framework_version_script.lds)",
],
}),
linkstatic = 1,
@@ -464,15 +496,13 @@ tf_cc_shared_object(
name = "libtensorflow.so",
linkopts = select({
"//tensorflow:darwin": [
- "-Wl,-exported_symbols_list", # This line must be directly followed by the exported_symbols.lds file
- "$(location //tensorflow/c:exported_symbols.lds)",
+ "-Wl,-exported_symbols_list,$(location //tensorflow/c:exported_symbols.lds)",
"-Wl,-install_name,@rpath/libtensorflow.so",
],
"//tensorflow:windows": [],
"//conditions:default": [
"-z defs",
- "-Wl,--version-script", # This line must be directly followed by the version_script.lds file
- "$(location //tensorflow/c:version_script.lds)",
+ "-Wl,--version-script,$(location //tensorflow/c:version_script.lds)",
],
}),
visibility = ["//visibility:public"],
@@ -490,14 +520,12 @@ tf_cc_shared_object(
name = "libtensorflow_cc.so",
linkopts = select({
"//tensorflow:darwin": [
- "-Wl,-exported_symbols_list", # This line must be directly followed by the exported_symbols.lds file
- "$(location //tensorflow:tf_exported_symbols.lds)",
+ "-Wl,-exported_symbols_list,$(location //tensorflow:tf_exported_symbols.lds)",
],
"//tensorflow:windows": [],
"//conditions:default": [
"-z defs",
- "-Wl,--version-script", # This line must be directly followed by the version_script.lds file
- "$(location //tensorflow:tf_version_script.lds)",
+ "-Wl,--version-script,$(location //tensorflow:tf_version_script.lds)",
],
}),
visibility = ["//visibility:public"],
@@ -574,13 +602,20 @@ gen_api_init_files(
name = "tf_python_api_gen_v1",
srcs = [
"api_template_v1.__init__.py",
+ "compat_template.__init__.py",
"compat_template_v1.__init__.py",
],
api_version = 1,
- compat_api_versions = [1],
- compat_init_templates = ["compat_template_v1.__init__.py"],
+ compat_api_versions = [
+ 1,
+ 2,
+ ],
+ compat_init_templates = [
+ "compat_template_v1.__init__.py",
+ "compat_template.__init__.py",
+ ],
output_dir = "_api/v1/",
- output_files = TENSORFLOW_API_INIT_FILES_V1_WITH_COMPAT,
+ output_files = TENSORFLOW_API_INIT_FILES_V1,
output_package = "tensorflow._api.v1",
root_file_name = "v1.py",
root_init_template = "api_template_v1.__init__.py",
@@ -590,11 +625,18 @@ gen_api_init_files(
name = "tf_python_api_gen_v2",
srcs = [
"api_template.__init__.py",
+ "compat_template.__init__.py",
"compat_template_v1.__init__.py",
],
api_version = 2,
- compat_api_versions = [1],
- compat_init_templates = ["compat_template_v1.__init__.py"],
+ compat_api_versions = [
+ 1,
+ 2,
+ ],
+ compat_init_templates = [
+ "compat_template_v1.__init__.py",
+ "compat_template.__init__.py",
+ ],
output_dir = "_api/v2/",
output_files = TENSORFLOW_API_INIT_FILES_V2,
output_package = "tensorflow._api.v2",
@@ -606,9 +648,11 @@ py_library(
name = "tensorflow_py",
srcs_version = "PY2AND3",
visibility = ["//visibility:public"],
- deps = [
+ deps = select({
+ "api_version_2": [],
+ "//conditions:default": ["//tensorflow/contrib:contrib_py"],
+ }) + [
":tensorflow_py_no_contrib",
- "//tensorflow/contrib:contrib_py",
"//tensorflow/python/estimator:estimator_py",
],
)
@@ -618,7 +662,11 @@ py_library(
srcs = select({
"api_version_2": [":tf_python_api_gen_v2"],
"//conditions:default": [":tf_python_api_gen_v1"],
- }) + [":root_init_gen"],
+ }) + [":root_init_gen"] + [
+ "//tensorflow/python/keras/api:keras_python_api_gen",
+ "//tensorflow/python/keras/api:keras_python_api_gen_compat_v1",
+ "//tensorflow/python/keras/api:keras_python_api_gen_compat_v2",
+ ],
srcs_version = "PY2AND3",
visibility = ["//visibility:public"],
deps = ["//tensorflow/python:no_contrib"],
diff --git a/tensorflow/api_template.__init__.py b/tensorflow/api_template.__init__.py
index d81cf067eb07e88e2b8a86cf5643674235eb3f3b..ddcacfcbe2d4d8b089f10f1a771384dc8c4fd199 100644
--- a/tensorflow/api_template.__init__.py
+++ b/tensorflow/api_template.__init__.py
@@ -18,27 +18,84 @@ from __future__ import absolute_import as _absolute_import
from __future__ import division as _division
from __future__ import print_function as _print_function
+import distutils as _distutils
+import inspect as _inspect
import os as _os
-
-# pylint: disable=g-bad-import-order
-from tensorflow.python.tools import component_api_helper as _component_api_helper
-_component_api_helper.package_hook(
- parent_package_str=__name__,
- child_package_str=('tensorflow_estimator.python.estimator.api.estimator'))
+import site as _site
+import sys as _sys
# API IMPORTS PLACEHOLDER
# Make sure directory containing top level submodules is in
# the __path__ so that "from tensorflow.foo import bar" works.
# We're using bitwise, but there's nothing special about that.
-_tf_api_dir = _os.path.dirname(_os.path.dirname(bitwise.__file__)) # pylint: disable=undefined-variable
-if _tf_api_dir not in __path__:
+_API_MODULE = bitwise # pylint: disable=undefined-variable
+_current_module = _sys.modules[__name__]
+_tf_api_dir = _os.path.dirname(_os.path.dirname(_API_MODULE.__file__))
+if not hasattr(_current_module, '__path__'):
+ __path__ = [_tf_api_dir]
+elif _tf_api_dir not in __path__:
__path__.append(_tf_api_dir)
+# pylint: disable=g-bad-import-order
+from tensorflow.python.tools import component_api_helper as _component_api_helper
+_component_api_helper.package_hook(
+ parent_package_str=__name__,
+ child_package_str=('tensorboard.summary._tf.summary'),
+ error_msg="Limited tf.summary API due to missing TensorBoard installation")
+_component_api_helper.package_hook(
+ parent_package_str=__name__,
+ child_package_str=(
+ 'tensorflow_estimator.python.estimator.api._v2.estimator'))
+
+if not hasattr(_current_module, 'estimator'):
+ _component_api_helper.package_hook(
+ parent_package_str=__name__,
+ child_package_str=(
+ 'tensorflow_estimator.python.estimator.api.estimator'))
+_component_api_helper.package_hook(
+ parent_package_str=__name__,
+ child_package_str=('tensorflow.python.keras.api._v2.keras'))
+
# Enable TF2 behaviors
-from tensorflow.python.compat import compat as _compat # pylint: disable=g-import-not-at-top
+from tensorflow.python.compat import v2_compat as _compat # pylint: disable=g-import-not-at-top
_compat.enable_v2_behavior()
+
+# Load all plugin libraries from site-packages/tensorflow-plugins if we are
+# running under pip.
+# TODO(gunan): Enable setting an environment variable to define arbitrary plugin
+# directories.
+# TODO(gunan): Find a better location for this code snippet.
+from tensorflow.python.framework import load_library as _ll
+from tensorflow.python.lib.io import file_io as _fi
+
+# Get sitepackages directories for the python installation.
+_site_packages_dirs = []
+_site_packages_dirs += [_site.USER_SITE]
+_site_packages_dirs += [_p for _p in _sys.path if 'site-packages' in _p]
+if 'getsitepackages' in dir(_site):
+ _site_packages_dirs += _site.getsitepackages()
+
+if 'sysconfig' in dir(_distutils):
+ _site_packages_dirs += [_distutils.sysconfig.get_python_lib()]
+
+_site_packages_dirs = list(set(_site_packages_dirs))
+
+# Find the location of this exact file.
+_current_file_location = _inspect.getfile(_inspect.currentframe())
+
+def _running_from_pip_package():
+ return any(
+ _current_file_location.startswith(dir_) for dir_ in _site_packages_dirs)
+
+if _running_from_pip_package():
+ for s in _site_packages_dirs:
+ # TODO(gunan): Add sanity checks to loaded modules here.
+ plugin_dir = _os.path.join(s, 'tensorflow-plugins')
+ if _fi.file_exists(plugin_dir):
+ _ll.load_library(plugin_dir)
+
# These symbols appear because we import the python package which
# in turn imports from tensorflow.core and tensorflow.python. They
# must come from this module. So python adds these symbols for the
@@ -59,4 +116,11 @@ try:
del compiler
except NameError:
pass
+
+# Add module aliases
+if hasattr(_current_module, 'keras'):
+ losses = keras.losses
+ metrics = keras.metrics
+ optimizers = keras.optimizers
+
# pylint: enable=undefined-variable
diff --git a/tensorflow/api_template_v1.__init__.py b/tensorflow/api_template_v1.__init__.py
index 65bdb6cb1b5e6fb0656a12b932d767aeacfccd29..5eb25a81b7f765f551bc4f1b7ba99b35dbc6b7bb 100644
--- a/tensorflow/api_template_v1.__init__.py
+++ b/tensorflow/api_template_v1.__init__.py
@@ -18,20 +18,42 @@ from __future__ import absolute_import as _absolute_import
from __future__ import division as _division
from __future__ import print_function as _print_function
+import distutils as _distutils
+import inspect as _inspect
import os as _os
+import site as _site
+import sys as _sys
# pylint: disable=g-bad-import-order
from tensorflow.python import pywrap_tensorflow # pylint: disable=unused-import
+# API IMPORTS PLACEHOLDER
+
from tensorflow.python.tools import component_api_helper as _component_api_helper
_component_api_helper.package_hook(
parent_package_str=__name__,
- child_package_str=('tensorflow_estimator.python.estimator.api.estimator'))
-
-# API IMPORTS PLACEHOLDER
+ child_package_str=(
+ 'tensorflow_estimator.python.estimator.api._v1.estimator'))
+_current_module = _sys.modules[__name__]
+if not hasattr(_current_module, 'estimator'):
+ _component_api_helper.package_hook(
+ parent_package_str=__name__,
+ child_package_str=(
+ 'tensorflow_estimator.python.estimator.api.estimator'))
+_component_api_helper.package_hook(
+ parent_package_str=__name__,
+ child_package_str=('tensorflow.python.keras.api._v1.keras'))
from tensorflow.python.util.lazy_loader import LazyLoader # pylint: disable=g-import-not-at-top
-contrib = LazyLoader('contrib', globals(), 'tensorflow.contrib')
+_CONTRIB_WARNING = """
+WARNING: The TensorFlow contrib module will not be included in TensorFlow 2.0.
+For more information, please see:
+ * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md
+ * https://github.com/tensorflow/addons
+If you depend on functionality not listed there, please file an issue.
+"""
+contrib = LazyLoader('contrib', globals(), 'tensorflow.contrib',
+ _CONTRIB_WARNING)
del LazyLoader
# The templated code that replaces the placeholder above sometimes
# sets the __all__ variable. If it does, we have to be sure to add
@@ -40,14 +62,53 @@ if '__all__' in vars():
vars()['__all__'].append('contrib')
from tensorflow.python.platform import flags # pylint: disable=g-import-not-at-top
+# The 'app' module will be imported as part of the placeholder section above.
app.flags = flags # pylint: disable=undefined-variable
+# Also use 'app' module (choice is arbitrary) to derive the API directory below.
+_API_MODULE = app # pylint: disable=undefined-variable
+
# Make sure directory containing top level submodules is in
# the __path__ so that "from tensorflow.foo import bar" works.
-_tf_api_dir = _os.path.dirname(_os.path.dirname(app.__file__)) # pylint: disable=undefined-variable
-if _tf_api_dir not in __path__:
+_tf_api_dir = _os.path.dirname(_os.path.dirname(_API_MODULE.__file__))
+if not hasattr(_current_module, '__path__'):
+ __path__ = [_tf_api_dir]
+elif _tf_api_dir not in __path__:
__path__.append(_tf_api_dir)
+# Load all plugin libraries from site-packages/tensorflow-plugins if we are
+# running under pip.
+# TODO(gunan): Enable setting an environment variable to define arbitrary plugin
+# directories.
+# TODO(gunan): Find a better location for this code snippet.
+from tensorflow.python.framework import load_library as _ll
+from tensorflow.python.lib.io import file_io as _fi
+
+# Get sitepackages directories for the python installation.
+_site_packages_dirs = []
+_site_packages_dirs += [_site.USER_SITE]
+_site_packages_dirs += [_p for _p in _sys.path if 'site-packages' in _p]
+if 'getsitepackages' in dir(_site):
+ _site_packages_dirs += _site.getsitepackages()
+
+if 'sysconfig' in dir(_distutils):
+ _site_packages_dirs += [_distutils.sysconfig.get_python_lib()]
+
+_site_packages_dirs = list(set(_site_packages_dirs))
+
+# Find the location of this exact file.
+_current_file_location = _inspect.getfile(_inspect.currentframe())
+
+def _running_from_pip_package():
+ return any(
+ _current_file_location.startswith(dir_) for dir_ in _site_packages_dirs)
+
+if _running_from_pip_package():
+ for s in _site_packages_dirs:
+ # TODO(gunan): Add sanity checks to loaded modules here.
+ plugin_dir = _os.path.join(s, 'tensorflow-plugins')
+ if _fi.file_exists(plugin_dir):
+ _ll.load_library(plugin_dir)
# These symbols appear because we import the python package which
# in turn imports from tensorflow.core and tensorflow.python. They
diff --git a/tensorflow/c/BUILD b/tensorflow/c/BUILD
index 25df970ecab0757f23465ab19e7f45de0c759458..ef7863dc0d5cbd57da30baa6e04278c2a0354b25 100644
--- a/tensorflow/c/BUILD
+++ b/tensorflow/c/BUILD
@@ -67,6 +67,23 @@ tf_cuda_library(
tf_cuda_library(
name = "c_api",
+ hdrs = ["c_api.h"],
+ copts = tf_copts(),
+ visibility = ["//visibility:public"],
+ deps = [
+ ":c_api_no_xla",
+ ":c_api_internal",
+ ] + select({
+ "//tensorflow:with_xla_support": [
+ "//tensorflow/compiler/tf2xla:xla_compiler",
+ "//tensorflow/compiler/jit",
+ ],
+ "//conditions:default": [],
+ }),
+)
+
+tf_cuda_library(
+ name = "c_api_no_xla",
srcs = [
"c_api.cc",
"c_api_function.cc",
@@ -75,15 +92,13 @@ tf_cuda_library(
"c_api.h",
],
copts = tf_copts(),
- visibility = ["//visibility:public"],
- deps = select({
+ visibility = ["//tensorflow/c:__subpackages__"],
+ deps = [":c_api_internal"] + select({
"//tensorflow:android": [
- ":c_api_internal",
"//tensorflow/core:android_tensorflow_lib_lite",
],
"//conditions:default": [
- ":c_api_internal",
- "//tensorflow/cc/saved_model:loader",
+ "//tensorflow/cc/saved_model:loader_lite",
"//tensorflow/cc:gradients",
"//tensorflow/cc:ops",
"//tensorflow/cc:grad_ops",
@@ -97,13 +112,8 @@ tf_cuda_library(
"//tensorflow/core:lib",
"//tensorflow/core:lib_internal",
"//tensorflow/core/distributed_runtime:server_lib",
+ "//tensorflow/core/kernels:logging_ops",
],
- }) + select({
- "//tensorflow:with_xla_support": [
- "//tensorflow/compiler/tf2xla:xla_compiler",
- "//tensorflow/compiler/jit",
- ],
- "//conditions:default": [],
}),
)
@@ -123,13 +133,13 @@ tf_cuda_library(
"//tensorflow/c/eager:c_api",
"//tensorflow/c/eager:c_api_internal",
"//tensorflow/compiler/jit:flags",
- "//tensorflow/contrib/tpu:all_ops",
"//tensorflow/core:core_cpu",
"//tensorflow/core:framework",
"//tensorflow/core:lib",
"//tensorflow/core:lib_platform",
"//tensorflow/core:protos_all_cc",
"//tensorflow/core/common_runtime/eager:attr_builder",
+ "@com_google_absl//absl/strings",
],
)
@@ -156,8 +166,8 @@ tf_cuda_library(
hdrs = ["tf_status_helper.h"],
visibility = ["//visibility:public"],
deps = [
- ":c_api",
":c_api_internal",
+ ":c_api_no_xla",
"//tensorflow/core:lib",
],
)
@@ -190,14 +200,12 @@ tf_cuda_library(
":c_api",
":tf_status_helper",
"//tensorflow/core:android_tensorflow_lib_lite",
- "//tensorflow/core:platform_env",
"//tensorflow/core:lib",
],
"//conditions:default": [
":c_api",
":tf_status_helper",
"//tensorflow/core:framework",
- "//tensorflow/core:platform_env",
"//tensorflow/core:lib",
],
}) + [":c_api_internal"],
@@ -215,13 +223,13 @@ tf_cuda_library(
visibility = ["//visibility:public"],
deps = select({
"//tensorflow:android": [
- ":c_api",
+ ":c_api_no_xla",
":c_api_internal",
":tf_status_helper",
"//tensorflow/core:android_tensorflow_lib_lite",
],
"//conditions:default": [
- ":c_api",
+ ":c_api_no_xla",
":c_api_internal",
":tf_status_helper",
"//tensorflow/core:framework",
@@ -251,6 +259,18 @@ tf_cuda_library(
],
)
+tf_cc_test(
+ name = "c_test",
+ srcs = ["c_test.c"],
+ extra_copts = ["-std=c11"],
+ deps = [
+ ":c_api",
+ ":c_api_experimental",
+ ":env",
+ ":kernels",
+ ],
+)
+
tf_cuda_cc_test(
name = "c_api_test",
size = "small",
@@ -279,13 +299,23 @@ tf_cuda_cc_test(
"//tensorflow/cc/saved_model:signature_constants",
"//tensorflow/cc/saved_model:tag_constants",
"//tensorflow/compiler/jit",
+ "//tensorflow/core:array_ops_op_lib",
+ "//tensorflow/core:bitwise_ops_op_lib",
+ "//tensorflow/core:control_flow_ops_op_lib",
"//tensorflow/core:core_cpu_internal",
"//tensorflow/core:direct_session",
"//tensorflow/core:framework",
"//tensorflow/core:framework_internal",
+ "//tensorflow/core:functional_ops_op_lib",
"//tensorflow/core:lib",
+ "//tensorflow/core:math_ops_op_lib",
+ "//tensorflow/core:nn_ops_op_lib",
+ "//tensorflow/core:no_op_op_lib",
"//tensorflow/core:proto_text",
"//tensorflow/core:protos_all_cc",
+ "//tensorflow/core:sendrecv_ops_op_lib",
+ "//tensorflow/core:spectral_ops_op_lib",
+ "//tensorflow/core:state_ops_op_lib",
"//tensorflow/core:test",
"//tensorflow/core:test_main",
"//tensorflow/core/kernels:array",
@@ -309,6 +339,7 @@ tf_cc_test(
deps = [
":c_api",
":c_api_experimental",
+ ":c_api_internal",
":c_test_util",
"//tensorflow/c/eager:c_api",
"//tensorflow/c/eager:c_api_test_util",
@@ -325,6 +356,7 @@ tf_cc_test(
srcs = ["c_api_function_test.cc"],
deps = [
":c_api",
+ ":c_api_internal",
":c_test_util",
"//tensorflow/core:lib",
"//tensorflow/core:lib_internal",
diff --git a/tensorflow/c/c_api.cc b/tensorflow/c/c_api.cc
index 94d18eb8b04e3534be547aca5cfbb32da40ffbf6..245d7ba2b186895532953aa61ebfc3fc6bf635a7 100644
--- a/tensorflow/c/c_api.cc
+++ b/tensorflow/c/c_api.cc
@@ -27,6 +27,7 @@ limitations under the License.
#include "tensorflow/cc/ops/while_loop.h"
#include "tensorflow/cc/saved_model/loader.h"
#include "tensorflow/core/framework/op_gen_lib.h"
+#include "tensorflow/core/kernels/logging_ops.h"
#endif
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/core/common_runtime/device_mgr.h"
@@ -257,6 +258,74 @@ int64_t TF_Dim(const TF_Tensor* t, int dim_index) {
size_t TF_TensorByteSize(const TF_Tensor* t) { return t->buffer->size(); }
void* TF_TensorData(const TF_Tensor* t) { return t->buffer->data(); }
+int64_t TF_TensorElementCount(const TF_Tensor* t) {
+ int64_t result = 1;
+ int rank = TF_NumDims(t);
+ for (int dim = 0; dim < rank; ++dim) {
+ result *= TF_Dim(t, dim);
+ }
+ return result;
+}
+
+// Returns the number of elements that would be present in a tensor with the
+// given shape.
+static int64_t ShapeNumElements(const int64_t* dims, int num_dims) {
+ int64_t result = 1;
+ for (int dim = 0; dim < num_dims; ++dim) {
+ result *= dims[dim];
+ }
+ return result;
+}
+
+static void UnrefIfNonNull(::tensorflow::TensorBuffer* buf) {
+ if (buf != nullptr) {
+ buf->Unref();
+ }
+}
+
+static void RefIfNonNull(::tensorflow::TensorBuffer* buf) {
+ if (buf != nullptr) {
+ buf->Ref();
+ }
+}
+
+void TF_TensorBitcastFrom(const TF_Tensor* from, TF_DataType type,
+ TF_Tensor* to, const int64_t* new_dims,
+ int num_new_dims, TF_Status* status) {
+ TF_SetStatus(status, TF_OK, "");
+ size_t in_size = TF_DataTypeSize(TF_TensorType(from));
+ if (in_size == 0) {
+ TF_SetStatus(status, TF_INVALID_ARGUMENT,
+ "input tensor has a zero-sized data type");
+ return;
+ }
+ size_t out_size = TF_DataTypeSize(type);
+ if (out_size == 0) {
+ TF_SetStatus(status, TF_INVALID_ARGUMENT,
+ "output tensor has a zero-sized data type");
+ return;
+ }
+
+ if (ShapeNumElements(new_dims, num_new_dims) * out_size !=
+ TF_TensorElementCount(from) * in_size) {
+ TF_SetStatus(status, TF_INVALID_ARGUMENT,
+ "input tensor is not compatible with output shape");
+ return;
+ }
+
+ tensorflow::TensorShapeProto p;
+ for (int i = 0; i < num_new_dims; ++i) {
+ p.add_dim()->set_size(new_dims[i]);
+ }
+ to->shape = tensorflow::TensorShape(p);
+ to->dtype = type;
+ if (to->buffer != from->buffer) {
+ UnrefIfNonNull(to->buffer);
+ to->buffer = from->buffer;
+ RefIfNonNull(to->buffer);
+ }
+}
+
// --------------------------------------------------------------------------
size_t TF_StringEncode(const char* src, size_t src_len, char* dst,
size_t dst_len, TF_Status* status) {
@@ -488,6 +557,7 @@ static TF_Tensor* EmptyTensor(TF_DataType dtype, const TensorShape& shape) {
// Non-static for testing.
TF_Tensor* TF_TensorFromTensor(const tensorflow::Tensor& src,
TF_Status* status) {
+ TF_SetStatus(status, TF_OK, "");
if (!src.IsInitialized()) {
status->status = FailedPrecondition(
"attempt to use a tensor with an uninitialized value");
@@ -571,7 +641,7 @@ TF_Tensor* TF_TensorFromTensor(const tensorflow::Tensor& src,
dimvec.size(), base, size, DeleteArray, base);
}
-Status MessageToBuffer(const tensorflow::protobuf::Message& in,
+Status MessageToBuffer(const tensorflow::protobuf::MessageLite& in,
TF_Buffer* out) {
if (out->data != nullptr) {
return InvalidArgument("Passing non-empty TF_Buffer is invalid.");
@@ -1241,6 +1311,13 @@ void TF_SetAttrTypeList(TF_OperationDescription* desc, const char* attr_name,
reinterpret_cast(values), num_values));
}
+void TF_SetAttrPlaceholder(TF_OperationDescription* desc, const char* attr_name,
+ const char* placeholder) {
+ tensorflow::AttrValue attr_value;
+ attr_value.set_placeholder(placeholder);
+ desc->node_builder.Attr(attr_name, attr_value);
+}
+
void TF_SetAttrFuncName(TF_OperationDescription* desc, const char* attr_name,
const char* value, size_t length) {
tensorflow::NameAttrList func_name;
@@ -2880,6 +2957,16 @@ const char* TF_ServerTarget(TF_Server* server) {
#endif
}
-void TF_DeleteServer(TF_Server* server) { delete server; }
+void TF_DeleteServer(TF_Server* server) {
+#ifndef __ANDROID__
+ delete server;
+#endif
+}
+
+void TF_RegisterLogListener(void (*listener)(const char*)) {
+#ifndef __ANDROID__
+ tensorflow::logging::RegisterListener(listener);
+#endif
+}
} // end extern "C"
diff --git a/tensorflow/c/c_api.h b/tensorflow/c/c_api.h
index c7abba85521fccec07983cd5ab4f94a8368d6181..051de3a7dc0f8c630b6c81d2cfa960e5279c93c0 100644
--- a/tensorflow/c/c_api.h
+++ b/tensorflow/c/c_api.h
@@ -272,6 +272,39 @@ TF_CAPI_EXPORT extern size_t TF_TensorByteSize(const TF_Tensor*);
// Return a pointer to the underlying data buffer.
TF_CAPI_EXPORT extern void* TF_TensorData(const TF_Tensor*);
+// Returns the number of elements in the tensor.
+TF_CAPI_EXPORT extern int64_t TF_TensorElementCount(const TF_Tensor* tensor);
+
+// Copy the internal data representation of `from` to `to`. `new_dims` and
+// `num_new_dims` specify the new shape of the `to` tensor, `type` specifies its
+// data type. On success, *status is set to TF_OK and the two tensors share the
+// same data buffer.
+//
+// This call requires that the `from` tensor and the given type and shape (dims
+// and num_dims) are "compatible" (i.e. they occupy the same number of bytes).
+// Specifically, given from_type_size = TF_DataTypeSize(TF_TensorType(from)):
+//
+// ShapeElementCount(dims, num_dims) * TF_DataTypeSize(type)
+//
+// must equal
+//
+// TF_TensorElementCount(from) * from_type_size
+//
+// where TF_ShapeElementCount would be the number of elements in a tensor with
+// the given shape.
+//
+// In addition, this function requires:
+// * TF_DataTypeSize(TF_TensorType(from)) != 0
+// * TF_DataTypeSize(type) != 0
+//
+// If any of the requirements are not met, *status is set to
+// TF_INVALID_ARGUMENT.
+TF_CAPI_EXPORT extern void TF_TensorBitcastFrom(const TF_Tensor* from,
+ TF_DataType type, TF_Tensor* to,
+ const int64_t* new_dims,
+ int num_new_dims,
+ TF_Status* status);
+
// --------------------------------------------------------------------------
// Encode the string `src` (`src_len` bytes long) into `dst` in the format
// required by TF_STRING tensors. Does not write to memory more than `dst_len`
@@ -516,6 +549,10 @@ TF_CAPI_EXPORT extern void TF_SetAttrTypeList(TF_OperationDescription* desc,
const char* attr_name,
const TF_DataType* values,
int num_values);
+TF_CAPI_EXPORT extern void TF_SetAttrPlaceholder(TF_OperationDescription* desc,
+ const char* attr_name,
+ const char* placeholder);
+
// Set a 'func' attribute to the specified name.
// `value` must point to a string of length `length` bytes.
TF_CAPI_EXPORT extern void TF_SetAttrFuncName(TF_OperationDescription* desc,
@@ -1277,6 +1314,28 @@ TF_CAPI_EXPORT extern TF_Function* TF_GraphToFunction(
int noutputs, const TF_Output* outputs, const char* const* output_names,
const TF_FunctionOptions* opts, const char* description, TF_Status* status);
+// Similar to TF_GraphToFunction but allows specifying control outputs of the
+// function.
+//
+// The arguments of TF_GraphToFunction have the same meaning, but the new
+// arguments are as follows:
+//
+// ncontrol_outputs: Number of control outputs of the function.
+// control_outputs: vector of TF_Operation objects to be marked as control
+// outputs of the function. Operations marked as control outputs are
+// guaranteed to execute.
+// control_output_names: Optional. If not nullptr, vector of strings, one
+// per control output, with their names to be added to the function's
+// OpDef.
+TF_CAPI_EXPORT extern TF_Function* TF_GraphToFunctionWithControlOutputs(
+ const TF_Graph* fn_body, const char* fn_name,
+ unsigned char append_hash_to_fn_name, int num_opers,
+ const TF_Operation* const* opers, int ninputs, const TF_Output* inputs,
+ int noutputs, const TF_Output* outputs, const char* const* output_names,
+ int ncontrol_outputs, const TF_Operation* const* control_outputs,
+ const char* const* control_output_names, const TF_FunctionOptions* opts,
+ const char* description, TF_Status* status);
+
// Returns the name of the graph function.
// The return value points to memory that is only usable until the next
// mutation to *func.
@@ -1710,6 +1769,14 @@ TF_CAPI_EXPORT extern const char* TF_ServerTarget(TF_Server* server);
// it will be stopped and joined.
TF_CAPI_EXPORT extern void TF_DeleteServer(TF_Server* server);
+// Register a listener method that processes printed messages.
+//
+// If any listeners are registered, the print operator will call all listeners
+// with the printed messages and immediately return without writing to the
+// logs.
+TF_CAPI_EXPORT extern void TF_RegisterLogListener(
+ void (*listener)(const char*));
+
#ifdef __cplusplus
} /* end extern "C" */
#endif
diff --git a/tensorflow/c/c_api_experimental.cc b/tensorflow/c/c_api_experimental.cc
index 38e29aa74a90f4e85d1369b6928a5a58c531b2da..7ff4084decc686b067226ecaecf2af29d51d42f2 100644
--- a/tensorflow/c/c_api_experimental.cc
+++ b/tensorflow/c/c_api_experimental.cc
@@ -15,6 +15,7 @@ limitations under the License.
#include "tensorflow/c/c_api_experimental.h"
+#include "absl/strings/substitute.h"
#include "tensorflow/c/c_api.h"
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/eager/c_api.h"
@@ -66,7 +67,8 @@ void TF_EnableXLACompilation(TF_SessionOptions* options, unsigned char enable) {
}
TF_Buffer* TF_CreateConfig(unsigned char enable_xla_compilation,
- unsigned char gpu_memory_allow_growth) {
+ unsigned char gpu_memory_allow_growth,
+ unsigned int num_cpu_devices) {
tensorflow::ConfigProto config;
auto* optimizer_options =
config.mutable_graph_options()->mutable_optimizer_options();
@@ -87,6 +89,8 @@ TF_Buffer* TF_CreateConfig(unsigned char enable_xla_compilation,
auto* gpu_options = config.mutable_gpu_options();
gpu_options->set_allow_growth(gpu_memory_allow_growth);
+ (*config.mutable_device_count())["CPU"] = num_cpu_devices;
+
// TODO(b/113217601): This is needed for EagerContext::runner_ to use a
// threadpool, so that we avoid the possibility of running the runner_ in the
// threadpool of GPU event mgr, as that can trigger more callbacks to be
@@ -125,6 +129,14 @@ const char* TF_GraphDebugString(TF_Graph* graph, size_t* len) {
return ret;
}
+char* TF_FunctionDebugString(TF_Function* func, size_t* len) {
+ const auto& debug_str = func->fdef.DebugString();
+ *len = debug_str.size();
+ char* ret = static_cast(malloc(*len + 1));
+ memcpy(ret, debug_str.c_str(), *len + 1);
+ return ret;
+}
+
// On success, returns a set of TF_Function instances from `text_proto` of
// GraphDef type. These functions must be deleted by calling TF_DeleteFunction.
//
@@ -8535,8 +8547,9 @@ TFE_Context* TFE_CreateContextFromSession(TF_Session* session,
// Reduce GPU memory allocation, and set appropriate config options for TFE
// context.
- auto* config =
- TF_CreateConfig(/*xla*/ false, /* gpu_memory_allow_growth */ true);
+ auto* config = TF_CreateConfig(
+ /*xla*/ false, /* gpu_memory_allow_growth */ true, /* num_cpu_devices */
+ 10);
TFE_ContextOptionsSetConfig(opts, config->data, config->length, status);
if (!status->status.ok()) {
CHECK(!config);
@@ -8733,6 +8746,12 @@ static void CheckOk(TF_Status* status) {
void TFE_TensorHandlePrintDebugString(TFE_TensorHandle* handle) {
auto* status = TF_NewStatus();
+ if (!TFE_TensorHandleIsConcrete(handle)) {
+ VLOG(1) << "Symbolic tensor: " << handle;
+ TF_DeleteStatus(status);
+ return;
+ }
+
TF_Tensor* t = TFE_TensorHandleResolve(handle, status);
CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
@@ -8744,6 +8763,11 @@ void TFE_TensorHandlePrintDebugString(TFE_TensorHandle* handle) {
TF_DeleteStatus(status);
}
+void TFE_OpPrintDebugString(TFE_Op* op) {
+ VLOG(1) << "TFE_OpPrintDebugString() over " << op;
+ LOG(INFO) << op->operation.DebugString();
+}
+
struct TFE_ExecuteOpNotification {
TFE_ExecuteOpNotification() : status(TF_NewStatus(), TF_DeleteStatus) {}
tensorflow::Notification n;
@@ -8886,3 +8910,240 @@ TFE_TensorHandle* TFE_NewTensorHandleFromScalar(TF_DataType dtype_arg,
std::memcpy(tensorflow::TensorCApi::Buffer(tensor)->data(), data, len);
return new TFE_TensorHandle(tensor, nullptr, nullptr);
}
+
+namespace {
+tensorflow::Status EnableCollectiveOps(const tensorflow::ServerDef& server_def,
+ TFE_Context* ctx) {
+ // We don't use the TF_RETURN_IF_ERROR macro directly since that destroys the
+ // server object (which currently CHECK-fails) and we miss the error, instead,
+ // we log the error, and then return to allow the user to see the error
+ // message.
+#define LOG_AND_RETURN_IF_ERROR(...) \
+ do { \
+ const ::tensorflow::Status _status = (__VA_ARGS__); \
+ if (TF_PREDICT_FALSE(!_status.ok())) { \
+ LOG(ERROR) << _status.error_message(); \
+ return _status; \
+ } \
+ } while (0);
+
+ std::unique_ptr server;
+ LOG_AND_RETURN_IF_ERROR(tensorflow::NewServer(server_def, &server));
+
+ tensorflow::GrpcServer* grpc_server =
+ dynamic_cast(server.get());
+ if (grpc_server == nullptr) {
+ LOG_AND_RETURN_IF_ERROR(tensorflow::errors::Internal(
+ "Currently, TFE_NewContext only supports tensorflow::GrpcServer."));
+ }
+
+ LOG_AND_RETURN_IF_ERROR(grpc_server->Start());
+
+ LOG_AND_RETURN_IF_ERROR(ctx->context.StoreCollectiveOpsServer(
+ std::move(server), grpc_server->worker_env()->device_mgr,
+ grpc_server->worker_env()->collective_executor_mgr));
+
+ return tensorflow::Status::OK();
+#undef LOG_AND_RETURN_IF_ERROR
+}
+} // namespace
+
+// Set server_def on the context, possibly updating it.
+TF_CAPI_EXPORT extern void TFE_EnableCollectiveOps(TFE_Context* ctx,
+ const void* proto,
+ size_t proto_len,
+ TF_Status* status) {
+ tensorflow::ServerDef server_def;
+ if (!server_def.ParseFromArray(proto, proto_len)) {
+ status->status = tensorflow::errors::InvalidArgument(
+ "Invalid tensorflow.ServerDef protocol buffer");
+ return;
+ }
+ status->status = EnableCollectiveOps(server_def, ctx);
+}
+
+std::string tensorflow::getTF_OutputDebugString(TF_Output node) {
+ return absl::Substitute("TF_Output($0, $1)", node.oper, node.index);
+}
+
+using tensorflow::getTF_OutputDebugString;
+
+TFE_TensorHandle* TFE_NewTensorHandleFromTFOutput(TF_Output t,
+ TF_DataType dtype) {
+ auto ret = new TFE_TensorHandle(t, dtype);
+ VLOG(1) << "Storing TFOutput " << getTF_OutputDebugString(t)
+ << " into tensor handle " << ret << " with internal handle "
+ << ret->handle;
+ return ret;
+}
+
+unsigned char TFE_TensorHandleIsConcrete(TFE_TensorHandle* handle) {
+ assert(handle->handle != nullptr);
+ return handle->handle->getSymbolicTensor() == nullptr;
+}
+
+TF_Output TFE_GetTFOutputFromTensorHandle(TFE_TensorHandle* handle,
+ TF_Status* status) {
+ if (TFE_TensorHandleIsConcrete(handle)) {
+ status->status =
+ tensorflow::errors::Internal("Not a symbolic tensor: ", handle);
+ return TF_Output{nullptr, -1};
+ }
+
+ auto* sym_tensor = handle->handle->getSymbolicTensor();
+ CHECK(sym_tensor != nullptr);
+ auto ret = TF_Output{sym_tensor->oper, sym_tensor->index};
+ VLOG(1) << "Retrieving " << getTF_OutputDebugString(ret)
+ << " from tensor handle " << handle;
+ CHECK_GE(sym_tensor->index, 0);
+ return ret;
+}
+
+TFE_TraceContext* TFE_NewTraceContext(TF_Graph* graph) {
+ return new TFE_TraceContext(graph);
+}
+
+void TFE_DeleteTraceContext(TFE_TraceContext* trace_ctx) { delete trace_ctx; }
+
+// If `handle` is already symbolic, return it. Otherwise map it to a new
+// symbolic tensor (a PlaceHolder op) and return that.
+static TF_Output getOrCreateSymbolicTensor(TFE_TraceContext* trace_ctx,
+ tensorflow::TensorHandle* handle,
+ TF_Status* status) {
+ VLOG(1) << "Getting symbolic tensor for input tensor handle " << handle
+ << ": " << handle->DebugString();
+
+ auto* sym_tensor = handle->getSymbolicTensor();
+ if (sym_tensor != nullptr) {
+ auto ret = TF_Output{sym_tensor->oper, sym_tensor->index};
+ VLOG(1) << "This handle is a symbolic tensor " << sym_tensor << ": "
+ << getTF_OutputDebugString(ret);
+ return ret;
+ }
+
+ auto find_it = trace_ctx->input_tensor_map.find(handle);
+ if (find_it != trace_ctx->input_tensor_map.end()) {
+ VLOG(1) << "There exists a map entry from this concrete tensor to: "
+ << getTF_OutputDebugString(find_it->second);
+ return find_it->second;
+ }
+
+ auto node_name = tensorflow::strings::StrCat("additional_input_",
+ trace_ctx->node_counter++);
+ VLOG(1) << "Adding a place holder node named " << node_name;
+ auto* desc =
+ TF_NewOperation(trace_ctx->graph, "Placeholder", node_name.c_str());
+ TF_SetAttrType(desc, "dtype",
+ static_cast(handle->dtype) /*TF_FLOAT*/);
+ auto* result = TF_FinishOperation(desc, status);
+ if (!status->status.ok()) {
+ return TF_Output{nullptr, -1};
+ }
+
+ auto ret = TF_Output{result, 0};
+ VLOG(1) << "Creating a new map entry to map to: "
+ << getTF_OutputDebugString(ret);
+ trace_ctx->input_tensor_map[handle] = ret;
+ // `handle` could be destroyed before it's read from `input_tensor_map` (say
+ // during a subsequent TFE_FinalizeInputTensorsFromTraceContext() call), so we
+ // increment its ref count to extend its life span to that of `trace_ctx`.
+ handle->Ref();
+ VLOG(1) << "Ref count for handle " << handle
+ << " is 1?: " << handle->RefCountIsOne();
+ return ret;
+}
+
+TF_Operation* TFE_AddEagerOpToGraph(TFE_Op* op, TFE_TraceContext* trace_ctx,
+ TFE_TensorHandle** retvals,
+ int* num_retvals, TF_Status* status) {
+ VLOG(1) << "Calling TFE_AddEagerOpToGraph() with op " << op << ": "
+ << op->operation.DebugString();
+
+ const auto& op_type = op->operation.Name();
+ auto op_name =
+ tensorflow::strings::StrCat(op_type, "_", trace_ctx->node_counter++);
+ auto* desc =
+ TF_NewOperation(trace_ctx->graph, op_type.c_str(), op_name.c_str());
+
+ VLOG(1) << "Adding attrs.";
+ tensorflow::AttrValueMap attrs;
+ op->operation.Attrs().FillAttrValueMap(&attrs);
+ for (const auto& attr : attrs) {
+ desc->node_builder.Attr(attr.first, attr.second);
+ }
+
+ VLOG(1) << "Adding inputs.";
+ const auto& inputs = op->operation.Inputs();
+ size_t inputIndex = 0;
+ const tensorflow::OpDef& op_def = desc->node_builder.op_def();
+ for (const tensorflow::OpDef::ArgDef& input_arg : op_def.input_arg()) {
+ // TODO(bgogul): Add support for number attributes.
+ DCHECK(input_arg.number_attr().empty())
+ << "Number attributes is not implemented yet.";
+ if (input_arg.type_list_attr().empty()) {
+ auto symbolic_input =
+ getOrCreateSymbolicTensor(trace_ctx, inputs[inputIndex++], status);
+ if (!status->status.ok()) return nullptr;
+ TF_AddInput(desc, symbolic_input);
+ continue;
+ }
+ const std::string& type_list_attr = input_arg.type_list_attr();
+ const auto& attr_value = attrs[type_list_attr];
+ DCHECK(attr_value.value_case() == tensorflow::AttrValue::kList)
+ << "Type list attribute should be a list!";
+ std::vector list_inputs(attr_value.list().type_size());
+ for (TF_Output& list_input : list_inputs) {
+ list_input =
+ getOrCreateSymbolicTensor(trace_ctx, inputs[inputIndex++], status);
+ if (!status->status.ok()) return nullptr;
+ }
+ TF_AddInputList(desc, list_inputs.data(), list_inputs.size());
+ }
+
+ auto* graph_op = TF_FinishOperation(desc, status);
+ if (!status->status.ok()) return nullptr;
+
+ VLOG(1) << "Op finalized; setting return tensors.";
+ *num_retvals = TF_OperationNumOutputs(graph_op);
+ VLOG(1) << "This op has " << *num_retvals << " outputs.";
+ for (int i = 0; i < *num_retvals; ++i) {
+ auto output = TF_Output{graph_op, i};
+ auto dtype = TF_OperationOutputType(output);
+ retvals[i] = TFE_NewTensorHandleFromTFOutput(output, dtype);
+ }
+ return graph_op;
+}
+
+int TFE_FinalizeInputTensorsFromTraceContext(TFE_TraceContext* trace_ctx) {
+ if (trace_ctx->input_tensors == nullptr) {
+ trace_ctx->input_tensors =
+ new std::vector>();
+ trace_ctx->input_tensors->reserve(trace_ctx->input_tensor_map.size());
+
+ for (auto input : trace_ctx->input_tensor_map) {
+ trace_ctx->input_tensors->emplace_back(input.first, input.second);
+ }
+ }
+ return trace_ctx->input_tensor_map.size();
+}
+
+TF_Output TFE_GetInputGraphNodeFromTraceContext(TFE_TraceContext* trace_ctx,
+ unsigned int idx) {
+ CHECK(trace_ctx->input_tensors != nullptr);
+ CHECK(trace_ctx->input_tensors->size() > idx);
+ return trace_ctx->input_tensors->at(idx).second;
+}
+
+TFE_TensorHandle* TFE_ConsumeInputConcreteTensorFromTraceContext(
+ TFE_TraceContext* trace_ctx, unsigned int idx) {
+ CHECK(trace_ctx->input_tensors != nullptr);
+ CHECK(trace_ctx->input_tensors->size() > idx);
+ auto* handle = trace_ctx->input_tensors->at(idx).first;
+ VLOG(1) << "Ref count for internal handle " << handle
+ << " is 1?: " << handle->RefCountIsOne();
+ handle->Ref();
+ auto* ret = new TFE_TensorHandle(handle);
+ VLOG(1) << "Returning a new tensor handle " << ret << ": "
+ << handle->DebugString();
+ return ret;
+}
diff --git a/tensorflow/c/c_api_experimental.h b/tensorflow/c/c_api_experimental.h
index 3e3a485eb763b871b0551414c4ef04746b2ed9a3..8d1a8b82fbaf9901b6d9aecf6d092ae298c8dba3 100644
--- a/tensorflow/c/c_api_experimental.h
+++ b/tensorflow/c/c_api_experimental.h
@@ -67,9 +67,10 @@ TF_CAPI_EXPORT extern void TF_EnableXLACompilation(TF_SessionOptions* options,
// a) ConfigProto.optimizer_options.global_jit_level is set to to ON_1 if
// `enable_xla_compilation` is non-zero, and OFF otherwise.
// b) ConfigProto.gpu_options.allow_growth is set to `gpu_memory_allow_growth`.
+// c) ConfigProto.device_count is set to `num_cpu_devices`.
TF_CAPI_EXPORT extern TF_Buffer* TF_CreateConfig(
- unsigned char enable_xla_compilation,
- unsigned char gpu_memory_allow_growth);
+ unsigned char enable_xla_compilation, unsigned char gpu_memory_allow_growth,
+ unsigned int num_cpu_devices);
// Create a serialized tensorflow.RunOptions proto, where RunOptions.trace_level
// is set to FULL_TRACE if `enable_full_trace` is non-zero, and NO_TRACE
@@ -83,6 +84,15 @@ TF_CAPI_EXPORT extern TF_Buffer* TF_CreateRunOptions(
TF_CAPI_EXPORT extern const char* TF_GraphDebugString(TF_Graph* graph,
size_t* len);
+// Returns the function content in a human-readable format, with length set in
+// `len`. The format is subject to change in the future.
+// The returned string is heap-allocated, and caller should call free() on it.
+//
+// Do not return const char*, because some foreign language binding
+// (e.g. swift) cannot then call free() on the returned pointer.
+TF_CAPI_EXPORT extern char* TF_FunctionDebugString(TF_Function* func,
+ size_t* len);
+
// Creates a stack of data set + iterator nodes, currently hard-coded to return
// a sequence of 3 float values <42.0, 43.0, 44.0> over 3 calls. On success,
// returns the IteratorGetNext node, which caller can run or feed into an node.
@@ -180,6 +190,8 @@ TF_CAPI_EXPORT extern TFE_TensorHandle* TFE_DequeueVariantTensor(
TF_CAPI_EXPORT extern void TFE_TensorHandlePrintDebugString(
TFE_TensorHandle* handle);
+TF_CAPI_EXPORT extern void TFE_OpPrintDebugString(TFE_Op* op);
+
typedef struct TFE_ExecuteOpNotification TFE_ExecuteOpNotification;
// Allows invoking a kernel asynchronously, and explicitly returns a
@@ -246,6 +258,62 @@ TF_CAPI_EXPORT int TF_PickUnusedPortOrDie(void);
TF_CAPI_EXPORT extern TFE_TensorHandle* TFE_NewTensorHandleFromScalar(
TF_DataType dtype, void* scalar, size_t len);
+// Specify the server_def that enables collective ops.
+// This is different to the above function in that it doesn't create remote
+// contexts, and remotely executing ops is not possible. It just enables
+// communication for collective ops.
+TF_CAPI_EXPORT extern void TFE_EnableCollectiveOps(TFE_Context* ctx,
+ const void* proto,
+ size_t proto_len,
+ TF_Status* status);
+
+// Create a symbolic tensor from the input graph node.
+TF_CAPI_EXPORT extern TFE_TensorHandle* TFE_NewTensorHandleFromTFOutput(
+ TF_Output t, TF_DataType data_type);
+
+// Returns 0 if the input tensor handle represents a symbolic tensor (i.e., a
+// graph node). Otherwise returns non-0.
+TF_CAPI_EXPORT extern unsigned char TFE_TensorHandleIsConcrete(
+ TFE_TensorHandle* handle);
+
+// If `handle` is a symbolic tensor, return the corresponding graph node
+// represented by TF_Output. Otherwise, return an error status.
+TF_CAPI_EXPORT extern TF_Output TFE_GetTFOutputFromTensorHandle(
+ TFE_TensorHandle* handle, TF_Status* status);
+
+typedef struct TFE_TraceContext TFE_TraceContext;
+
+// A trace context contains a trace graph, to which TFE_AddEagerOpToGraph()
+// calls add graph nodes as a way to symbolically execute the eager ops.
+//
+// It also contains a hash map from concrete input tensors to symbolic
+// tensors. That map will be used to create input tensors to the trace graph.
+TF_CAPI_EXPORT extern TFE_TraceContext* TFE_NewTraceContext(TF_Graph* graph);
+
+TF_CAPI_EXPORT extern void TFE_DeleteTraceContext(TFE_TraceContext* trace_ctx);
+
+// Symbolically executes `op`, by adding a corresponding node to the graph
+// associated with `trace_ctx`. This graph node outputs a set of symbolic
+// tensors in `retvals` and `num_retvals`. Returns the corresponding graph
+// operation on success, otherwise returns nullptr.
+TF_CAPI_EXPORT extern TF_Operation* TFE_AddEagerOpToGraph(
+ TFE_Op* op, TFE_TraceContext* trace_ctx, TFE_TensorHandle** retvals,
+ int* num_retvals, TF_Status* status);
+
+// Finalizes the trace graph and its inputs, and returns the number of inputs.
+// After this call, the next two APIs can be called to iterate over the input
+// tensors.
+TF_CAPI_EXPORT extern int TFE_FinalizeInputTensorsFromTraceContext(
+ TFE_TraceContext* trace_ctx);
+
+TF_CAPI_EXPORT extern TF_Output TFE_GetInputGraphNodeFromTraceContext(
+ TFE_TraceContext* trace_ctx, unsigned int idx);
+
+// Each input tensor should be consumed at most once.
+TF_CAPI_EXPORT extern TFE_TensorHandle*
+TFE_ConsumeInputConcreteTensorFromTraceContext(TFE_TraceContext* trace_ctx,
+ unsigned int idx);
+
#ifdef __cplusplus
} /* end extern "C" */
#endif
diff --git a/tensorflow/c/c_api_experimental_test.cc b/tensorflow/c/c_api_experimental_test.cc
index daa7701b7fe7e8ce757b6504329cf6434ad39778..c54021a7517ebbdd00405cbfa9cee8f3f6616cca 100644
--- a/tensorflow/c/c_api_experimental_test.cc
+++ b/tensorflow/c/c_api_experimental_test.cc
@@ -14,6 +14,7 @@ limitations under the License.
==============================================================================*/
#include "tensorflow/c/c_api_experimental.h"
+#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/c_test_util.h"
#include "tensorflow/c/eager/c_api.h"
#include "tensorflow/c/eager/c_api_test_util.h"
@@ -296,5 +297,178 @@ TEST(CAPI_EXPERIMENTAL, TFE_ExecuteOpInNewThreadTest_Blocking) {
TF_DeleteStatus(status);
}
+TEST(CAPI_EXPERIMENTAL, SymbolicTensor) {
+ TF_Status* status = TF_NewStatus();
+ auto node = TF_Output{nullptr, 1};
+ auto* sym_handle = TFE_NewTensorHandleFromTFOutput(node, TF_FLOAT);
+ TFE_TensorHandlePrintDebugString(sym_handle);
+ CHECK_EQ(TFE_TensorHandleDataType(sym_handle), TF_FLOAT);
+ ASSERT_FALSE(TFE_TensorHandleIsConcrete(sym_handle));
+ auto same_node = TFE_GetTFOutputFromTensorHandle(sym_handle, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ ASSERT_EQ(same_node.oper, node.oper);
+ ASSERT_EQ(same_node.index, node.index);
+ TFE_DeleteTensorHandle(sym_handle);
+
+ TFE_TensorHandle* m = TestMatrixTensorHandle();
+ ASSERT_TRUE(TFE_TensorHandleIsConcrete(m));
+ (void)TFE_GetTFOutputFromTensorHandle(m, status);
+ CHECK_EQ(TF_INTERNAL, TF_GetCode(status)) << TF_Message(status);
+ TFE_DeleteTensorHandle(m);
+
+ TF_DeleteStatus(status);
+}
+
+class AddEagerOpToGraphTest : public ::testing::Test {
+ protected:
+ AddEagerOpToGraphTest()
+ : status_(TF_NewStatus()),
+ eager_ctx_(nullptr),
+ graph_(TF_NewGraph()),
+ trace_ctx_(TFE_NewTraceContext(graph_)) {
+ TFE_ContextOptions* opts = TFE_NewContextOptions();
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ eager_ctx_ = TFE_NewContext(opts, status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ TFE_DeleteContextOptions(opts);
+ }
+
+ ~AddEagerOpToGraphTest() override {
+ TFE_DeleteTraceContext(trace_ctx_);
+ TF_DeleteGraph(graph_);
+ TFE_DeleteContext(eager_ctx_);
+ TF_DeleteStatus(status_);
+ }
+
+ template
+ void AddEagerOpToGraphAndCheck(TFE_Op* op, Callable checker) {
+ TFE_TensorHandle* retvals[5];
+ int num_retvals = 5;
+ // Symbolically execute this op, which adds a graph node to `trace_ctx_`.
+ TF_Operation* graph_op =
+ TFE_AddEagerOpToGraph(op, trace_ctx_, retvals, &num_retvals, status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ CHECK_NOTNULL(graph_op);
+ // Check the expectations.
+ checker(graph_op);
+ for (int i = 0; i < num_retvals; ++i) {
+ TFE_DeleteTensorHandle(retvals[i]);
+ }
+ }
+
+ TF_Status* status_;
+ TFE_Context* eager_ctx_;
+ TF_Graph* graph_;
+ TFE_TraceContext* trace_ctx_;
+};
+
+TEST_F(AddEagerOpToGraphTest, DebugPrintAndSymbolicExecution) {
+ TFE_TensorHandle* m = TestMatrixTensorHandle();
+ TFE_Op* op = MatMulOp(eager_ctx_, m, m);
+
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ TFE_OpPrintDebugString(op);
+
+ TFE_TensorHandle* retvals[5];
+ int num_retvals = 5;
+ // Symbolically execute this op, which adds a graph node to `trace_ctx`.
+ TFE_AddEagerOpToGraph(op, trace_ctx_, retvals, &num_retvals, status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+
+ int num_inputs = TFE_FinalizeInputTensorsFromTraceContext(trace_ctx_);
+ CHECK_EQ(num_inputs, 1);
+ auto input_sym_tensor = TFE_GetInputGraphNodeFromTraceContext(trace_ctx_,
+ /*idx*/ 0);
+
+ LOG(INFO) << tensorflow::getTF_OutputDebugString(input_sym_tensor);
+ auto handle = TFE_ConsumeInputConcreteTensorFromTraceContext(trace_ctx_,
+ /*idx*/ 0);
+ TFE_TensorHandlePrintDebugString(handle);
+ TFE_DeleteTensorHandle(handle);
+
+ CHECK_EQ(num_retvals, 1);
+ CHECK_EQ(TFE_TensorHandleDataType(retvals[0]), TF_FLOAT);
+
+ TFE_DeleteTensorHandle(retvals[0]);
+ TFE_DeleteTensorHandle(m);
+ TFE_DeleteOp(op);
+}
+
+TEST_F(AddEagerOpToGraphTest, ValueAttributesArePreserved) {
+ // Create MinOp
+ TFE_TensorHandle* axis = TestAxisTensorHandle();
+ TFE_Op* op = MinOp(eager_ctx_, axis, axis);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+
+ // Check the attributes set by the call to MinOp above.
+ AddEagerOpToGraphAndCheck(op, [this, &axis](TF_Operation* graph_op) {
+ unsigned char value;
+ TF_OperationGetAttrBool(graph_op, "keep_dims", &value, status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ CHECK_EQ(value, 1);
+ TF_DataType dtype;
+ TF_OperationGetAttrType(graph_op, "Tidx", &dtype, status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ CHECK_EQ(dtype, TF_INT32);
+ TF_OperationGetAttrType(graph_op, "T", &dtype, status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ CHECK_EQ(dtype, TFE_TensorHandleDataType(axis));
+ });
+ TFE_DeleteTensorHandle(axis);
+ TFE_DeleteOp(op);
+}
+
+TEST_F(AddEagerOpToGraphTest, ListAttributesArePreserved) {
+ // Create a "Squeeze" operator with list attributes.
+ TFE_TensorHandle* axis = TestAxisTensorHandle();
+ TFE_Op* squeeze = TFE_NewOp(eager_ctx_, "Squeeze", status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ TFE_OpAddInput(squeeze, axis, status_);
+ TFE_OpSetAttrType(squeeze, "T", TF_INT32);
+ std::vector boundaries = {1, 2, 3, 4};
+ TFE_OpSetAttrIntList(squeeze, "squeeze_dims", boundaries.data(),
+ boundaries.size());
+ // Check attributes are preserved.
+ AddEagerOpToGraphAndCheck(
+ squeeze, [this, &boundaries](TF_Operation* squeeze_graph_op) {
+ TF_DataType dtype;
+ TF_OperationGetAttrType(squeeze_graph_op, "T", &dtype, status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ CHECK_EQ(dtype, TF_INT32);
+ std::unique_ptr list(new int64_t[boundaries.size()]);
+ TF_OperationGetAttrIntList(squeeze_graph_op, "squeeze_dims", list.get(),
+ boundaries.size(), status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ EXPECT_TRUE(std::equal(list.get(), list.get() + boundaries.size(),
+ boundaries.begin()));
+ });
+ TFE_DeleteTensorHandle(axis);
+ TFE_DeleteOp(squeeze);
+}
+
+TEST_F(AddEagerOpToGraphTest, ListInputsAreAddedCorrectly) {
+ TFE_TensorHandle* scalar = TestScalarTensorHandle();
+ TFE_Op* identityn = TFE_NewOp(eager_ctx_, "IdentityN", status_);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ constexpr size_t kNumInputs = 3;
+ for (size_t i = 0; i < kNumInputs; ++i) {
+ TFE_OpAddInput(identityn, scalar, status_);
+ }
+ TF_DataType types[kNumInputs] = {TF_FLOAT, TF_FLOAT, TF_FLOAT};
+ TFE_OpSetAttrTypeList(identityn, "T", types, kNumInputs);
+ AddEagerOpToGraphAndCheck(
+ identityn, [this, kNumInputs](TF_Operation* graph_op) {
+ EXPECT_EQ(TF_OperationNumInputs(graph_op), kNumInputs);
+ EXPECT_EQ(TF_OperationInputListLength(graph_op, "input", status_),
+ kNumInputs);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ EXPECT_EQ(TF_OperationOutputListLength(graph_op, "output", status_),
+ kNumInputs);
+ CHECK_EQ(TF_OK, TF_GetCode(status_)) << TF_Message(status_);
+ });
+ TFE_DeleteTensorHandle(scalar);
+ TFE_DeleteOp(identityn);
+}
+
} // namespace
} // namespace tensorflow
diff --git a/tensorflow/c/c_api_function.cc b/tensorflow/c/c_api_function.cc
index 28b9f8df9c873ee394eb6a241dd9ac06ba6c8796..03d65ecefd4a9ba5a23a94ed902dfba6dd4fbda9 100644
--- a/tensorflow/c/c_api_function.cc
+++ b/tensorflow/c/c_api_function.cc
@@ -162,6 +162,11 @@ Status FillFunctionBody(
const std::vector& body_nodes,
const std::unordered_map& tensor_renaming,
FunctionDef* fdef) {
+ std::unordered_set func_attr_names;
+ for (const auto& func_attr : fdef->signature().attr()) {
+ func_attr_names.insert(func_attr.name());
+ }
+
std::vector in_edges;
std::vector control_edges;
for (const Node* node : body_nodes) {
@@ -243,6 +248,48 @@ Status FillFunctionBody(
if (node->op_def().is_stateful()) {
fdef->mutable_signature()->set_is_stateful(true);
}
+
+ // If this node has any attributes with placeholder value, add the
+ // attribute to FunctionDef signature.
+ for (const auto& iter : node->attrs()) {
+ if (iter.second.placeholder().empty()) {
+ continue;
+ }
+
+ // If we already added the attribute, skip it.
+ string func_attr_name = iter.second.placeholder();
+ if (func_attr_names.find(func_attr_name) != func_attr_names.end()) {
+ continue;
+ }
+
+ // This node's attribute is a placeholder value, so it does not have type
+ // information. We check node's OpDef for attribute type.
+ string node_attr_name = iter.first;
+ const OpDef::AttrDef* node_attr_def = nullptr;
+ for (const auto& node_attr : node->op_def().attr()) {
+ if (node_attr.name() == node_attr_name) {
+ node_attr_def = &node_attr;
+ }
+ }
+ if (!node_attr_def) {
+#ifdef TENSORFLOW_LITE_PROTOS
+ return errors::Unimplemented(
+ "Placeholder value is not supported for attributes not in OpDef. "
+ "Attribute: ",
+ node_attr_name);
+#else
+ return errors::Unimplemented(
+ "Placeholder value is not supported for attributes not in OpDef. "
+ "Attribute: ",
+ node_attr_name, ", OpDef: ", node->op_def().DebugString());
+#endif
+ }
+ OpDef::AttrDef* attr_def = fdef->mutable_signature()->add_attr();
+ attr_def->set_name(func_attr_name);
+ attr_def->set_type(node_attr_def->type());
+
+ func_attr_names.insert(func_attr_name);
+ }
}
return Status::OK();
}
@@ -255,6 +302,8 @@ Status GraphToFunctionDef(const Graph& fn_body, const string& fn_name,
const std::vector& inputs,
const std::vector& outputs,
const std::vector& output_names,
+ const std::vector& control_outputs,
+ const std::vector& control_output_names,
const char* description, FunctionDef* fdef) {
if (!output_names.empty()) {
DCHECK_EQ(output_names.size(), outputs.size());
@@ -378,6 +427,29 @@ Status GraphToFunctionDef(const Graph& fn_body, const string& fn_name,
fdef->mutable_signature()->set_name(fn_name);
}
+ if (!control_output_names.empty() &&
+ (control_outputs.size() != control_output_names.size())) {
+ return InvalidArgument(
+ "Expected number of control outputs (", control_outputs.size(),
+ ") and the number of control output names (",
+ control_output_names.size(), ") to match but they do not.");
+ }
+ std::unordered_set control_output_names_set;
+ for (int i = 0; i < control_outputs.size(); ++i) {
+ string signature_name;
+ if (!control_output_names.empty()) {
+ signature_name = control_output_names[i];
+ } else {
+ signature_name = control_outputs[i]->name();
+ }
+ if (!control_output_names_set.insert(signature_name).second) {
+ return errors::InvalidArgument("Repeated control output name: ",
+ signature_name);
+ }
+ fdef->mutable_signature()->add_control_output(signature_name);
+ (*fdef->mutable_control_ret())[signature_name] = control_outputs[i]->name();
+ }
+
return Status::OK();
}
@@ -485,14 +557,14 @@ Status ComputeBodyNodes(
using tensorflow::Node;
using tensorflow::string;
-TF_Function* TF_GraphToFunction(const TF_Graph* fn_body, const char* fn_name,
- unsigned char append_hash_to_fn_name,
- int num_opers, const TF_Operation* const* opers,
- int ninputs, const TF_Output* inputs,
- int noutputs, const TF_Output* outputs,
- const char* const* output_names,
- const TF_FunctionOptions* opts,
- const char* description, TF_Status* status) {
+TF_Function* TF_GraphToFunctionWithControlOutputs(
+ const TF_Graph* fn_body, const char* fn_name,
+ unsigned char append_hash_to_fn_name, int num_opers,
+ const TF_Operation* const* opers, int ninputs, const TF_Output* inputs,
+ int noutputs, const TF_Output* outputs, const char* const* output_names,
+ int ncontrol_outputs, const TF_Operation* const* control_outputs,
+ const char* const* control_output_names, const TF_FunctionOptions* opts,
+ const char* description, TF_Status* status) {
tensorflow::mutex_lock l(*const_cast(&fn_body->mu));
// Process inputs.
@@ -517,19 +589,34 @@ TF_Function* TF_GraphToFunction(const TF_Graph* fn_body, const char* fn_name,
}
}
+ // Process control output names.
+ std::vector control_output_names_vec;
+ if (control_output_names) {
+ control_output_names_vec.reserve(ncontrol_outputs);
+ for (int i = 0; i < ncontrol_outputs; ++i) {
+ control_output_names_vec.push_back(string(output_names[i]));
+ }
+ }
+
// Compute body nodes.
std::vector body_nodes;
status->status = tensorflow::ComputeBodyNodes(
fn_body, fn_name, num_opers, opers, input_nodes, &body_nodes);
if (!status->status.ok()) return nullptr;
+ // Compute body nodes.
+ std::vector control_output_nodes;
+ for (int i = 0; i < ncontrol_outputs; ++i) {
+ control_output_nodes.push_back(&control_outputs[i]->node);
+ }
+
// Do the actual function creation.
TF_Function* tf_function = new TF_Function();
DCHECK(append_hash_to_fn_name <= 1);
status->status = tensorflow::GraphToFunctionDef(
fn_body->graph, fn_name, append_hash_to_fn_name != 0, body_nodes,
- input_tensors, output_tensors, output_names_vec, description,
- &tf_function->fdef);
+ input_tensors, output_tensors, output_names_vec, control_output_nodes,
+ control_output_names_vec, description, &tf_function->fdef);
if (!status->status.ok()) {
TF_DeleteFunction(tf_function);
return nullptr;
@@ -537,6 +624,20 @@ TF_Function* TF_GraphToFunction(const TF_Graph* fn_body, const char* fn_name,
return tf_function;
}
+TF_Function* TF_GraphToFunction(const TF_Graph* fn_body, const char* fn_name,
+ unsigned char append_hash_to_fn_name,
+ int num_opers, const TF_Operation* const* opers,
+ int ninputs, const TF_Output* inputs,
+ int noutputs, const TF_Output* outputs,
+ const char* const* output_names,
+ const TF_FunctionOptions* opts,
+ const char* description, TF_Status* status) {
+ return TF_GraphToFunctionWithControlOutputs(
+ fn_body, fn_name, append_hash_to_fn_name, num_opers, opers, ninputs,
+ inputs, noutputs, outputs, output_names, 0, nullptr, nullptr, opts,
+ description, status);
+}
+
const char* TF_FunctionName(TF_Function* func) {
return func->fdef.signature().name().c_str();
}
diff --git a/tensorflow/c/c_api_function_test.cc b/tensorflow/c/c_api_function_test.cc
index 73fe73769bc1219ce865149d67d333c53371ccc5..946f8c4a2c3fb25f908d809e00bf579b40a8668b 100644
--- a/tensorflow/c/c_api_function_test.cc
+++ b/tensorflow/c/c_api_function_test.cc
@@ -15,6 +15,7 @@ limitations under the License.
#include "tensorflow/c/c_api.h"
+#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/c_test_util.h"
#include "tensorflow/core/framework/function.pb.h"
#include "tensorflow/core/framework/op_def.pb.h"
@@ -1230,6 +1231,53 @@ void DefineFunction(const char* name, TF_Function** func,
ASSERT_NE(*func, nullptr);
}
+REGISTER_OP("CustomOp")
+ .Output("output: float32")
+ .Attr("index: int")
+ .SetShapeFn(tensorflow::shape_inference::UnknownShape);
+
+void NodeWithPlaceholderAttrHelper(TF_Graph* graph, TF_Status* s,
+ const char* name, const char* placeholder,
+ TF_Operation** op) {
+ TF_OperationDescription* desc = TF_NewOperation(graph, "CustomOp", name);
+ TF_SetAttrPlaceholder(desc, "index", placeholder);
+ *op = TF_FinishOperation(desc, s);
+ ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
+ ASSERT_NE(*op, nullptr);
+}
+
+TEST_F(CApiFunctionTest, GraphToFunctionDefWithPlaceholderAttr) {
+ std::unique_ptr func_graph(
+ TF_NewGraph(), TF_DeleteGraph);
+ std::unique_ptr s(TF_NewStatus(),
+ TF_DeleteStatus);
+
+ TF_Operation *node1, *node2, *node3;
+ NodeWithPlaceholderAttrHelper(func_graph.get(), s.get(), "node1", "v1",
+ &node1);
+ NodeWithPlaceholderAttrHelper(func_graph.get(), s.get(), "node2", "v1",
+ &node2);
+ NodeWithPlaceholderAttrHelper(func_graph.get(), s.get(), "node3", "v2",
+ &node3);
+
+ TF_Output inputs[] = {};
+ TF_Output outputs[] = {{node1, 0}, {node2, 0}, {node3, 0}};
+ func_ = TF_GraphToFunction(
+ func_graph.get(), "func", /*append_hash_to_fn_name=*/false, -1,
+ /*opers=*/nullptr, 0, inputs, 3, outputs,
+ /*output_names=*/nullptr,
+ /*opts=*/nullptr, /*description=*/nullptr, s.get());
+ ASSERT_EQ(TF_OK, TF_GetCode(s.get())) << TF_Message(s.get());
+ ASSERT_NE(func_, nullptr);
+
+ // Verify that FunctionDef has 2 attributes, "v1" and "v2".
+ ASSERT_EQ(func_->fdef.signature().attr().size(), 2);
+ EXPECT_EQ(func_->fdef.signature().attr(0).name(), "v1");
+ EXPECT_EQ(func_->fdef.signature().attr(0).type(), "int");
+ EXPECT_EQ(func_->fdef.signature().attr(1).name(), "v2");
+ EXPECT_EQ(func_->fdef.signature().attr(1).type(), "int");
+}
+
TEST_F(CApiFunctionTest, SetGradientAndRun) {
// Define the function and its grad
DefineFunction(func_name_, &func_);
diff --git a/tensorflow/c/c_api_internal.h b/tensorflow/c/c_api_internal.h
index 5ba26d3c585350aa510f9970cbfc246a9a108543..d520b6b76849e562def6abd8be0510d3b4797e8c 100644
--- a/tensorflow/c/c_api_internal.h
+++ b/tensorflow/c/c_api_internal.h
@@ -204,7 +204,8 @@ Status TF_TensorToTensor(const TF_Tensor* src, Tensor* dst);
TF_Tensor* TF_TensorFromTensor(const Tensor& src, TF_Status* status);
-Status MessageToBuffer(const tensorflow::protobuf::Message& in, TF_Buffer* out);
+Status MessageToBuffer(const tensorflow::protobuf::MessageLite& in,
+ TF_Buffer* out);
// Set the shapes and types of the output's handle.
//
@@ -228,6 +229,8 @@ void RecordMutation(TF_Graph* graph, const TF_Operation& op,
bool ExtendSessionGraphHelper(TF_Session* session, TF_Status* status)
LOCKS_EXCLUDED(session->graph->mu, session->mu);
+std::string getTF_OutputDebugString(TF_Output node);
+
} // end namespace tensorflow
#endif // TENSORFLOW_C_C_API_INTERNAL_H_
diff --git a/tensorflow/c/c_api_test.cc b/tensorflow/c/c_api_test.cc
index d5934a10395ae094f65d3bc8b6cd7b94dbd32410..2be03bf0de6277fc63c353ad6dc63bec096a6993 100644
--- a/tensorflow/c/c_api_test.cc
+++ b/tensorflow/c/c_api_test.cc
@@ -163,6 +163,7 @@ TEST(CAPI, AllocateTensor) {
EXPECT_EQ(dims[0], TF_Dim(t, 0));
EXPECT_EQ(dims[1], TF_Dim(t, 1));
EXPECT_EQ(num_bytes, TF_TensorByteSize(t));
+ EXPECT_EQ(6, TF_TensorElementCount(t));
TF_DeleteTensor(t);
}
@@ -1467,6 +1468,41 @@ TEST(CAPI, DeletingNullPointerIsSafe) {
TF_DeleteStatus(status);
}
+TEST(CAPI, TestBitcastFrom_Reshape) {
+ int64_t dims[] = {2, 3};
+ TF_Tensor* a =
+ TF_AllocateTensor(TF_UINT64, dims, 2, 6 * TF_DataTypeSize(TF_UINT64));
+ TF_Tensor* b =
+ TF_AllocateTensor(TF_UINT64, nullptr, 0, TF_DataTypeSize(TF_UINT64));
+ EXPECT_NE(a, nullptr);
+ EXPECT_NE(b, nullptr);
+
+ EXPECT_EQ(6, TF_TensorElementCount(a));
+ EXPECT_EQ(1, TF_TensorElementCount(b));
+ EXPECT_EQ(6 * TF_DataTypeSize(TF_UINT64), TF_TensorByteSize(a));
+ EXPECT_EQ(TF_DataTypeSize(TF_UINT64), TF_TensorByteSize(b));
+
+ int64_t new_dims[] = {3, 2};
+ TF_Status* status = TF_NewStatus();
+ TF_TensorBitcastFrom(a, TF_UINT64, b, new_dims, 2, status);
+ ASSERT_EQ(TF_OK, TF_GetCode(status));
+ TF_DeleteStatus(status);
+
+ EXPECT_EQ(6, TF_TensorElementCount(a));
+ EXPECT_EQ(6, TF_TensorElementCount(b));
+ EXPECT_EQ(6 * TF_DataTypeSize(TF_UINT64), TF_TensorByteSize(a));
+ EXPECT_EQ(6 * TF_DataTypeSize(TF_UINT64), TF_TensorByteSize(b));
+
+ // Check that a write to one tensor shows up in the other.
+ *(static_cast(TF_TensorData(a))) = 4;
+ EXPECT_EQ(4, *(static_cast(TF_TensorData(b))));
+ *(static_cast(TF_TensorData(b))) = 6;
+ EXPECT_EQ(6, *(static_cast(TF_TensorData(a))));
+
+ TF_DeleteTensor(a);
+ TF_DeleteTensor(b);
+}
+
REGISTER_OP("TestOpWithNoGradient")
.Input("x: T")
.Output("y: T")
diff --git a/tensorflow/c/c_test.c b/tensorflow/c/c_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..7468122cd567270c8454f886e478be34c2c15cbf
--- /dev/null
+++ b/tensorflow/c/c_test.c
@@ -0,0 +1,93 @@
+/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tensorflow/c/c_api.h"
+#include "tensorflow/c/c_api_experimental.h"
+#include "tensorflow/c/env.h"
+#include "tensorflow/c/kernels.h"
+
+// A create function. This will never actually get called in this test, it's
+// just nice to know that it compiles.
+void* create(TF_OpKernelConstruction* ctx) {
+ TF_DataType type;
+ TF_Status* s = TF_NewStatus();
+ TF_OpKernelConstruction_GetAttrType(ctx, "foobar", &type, s);
+ TF_DeleteStatus(s);
+ return NULL;
+}
+
+// A compute function. This will never actually get called in this test, it's
+// just nice to know that it compiles.
+void compute(void* kernel, TF_OpKernelContext* ctx) {
+ TF_Tensor* input;
+ TF_Status* s = TF_NewStatus();
+ TF_GetInput(ctx, 0, &input, s);
+ TF_DeleteTensor(input);
+ TF_DeleteStatus(s);
+}
+
+// Exercises tensorflow's C API.
+int main(int argc, char** argv) {
+ TF_InitMain(argv[0], &argc, &argv);
+
+ struct TF_StringStream* s = TF_GetLocalTempDirectories();
+ const char* path;
+
+ if (!TF_StringStreamNext(s, &path)) {
+ fprintf(stderr, "TF_GetLocalTempDirectories returned no results\n");
+ return 1;
+ }
+
+ char file_name[100];
+ struct timeval t;
+ if (gettimeofday(&t, NULL)) {
+ perror("gettimeofday failed");
+ return 1;
+ }
+ snprintf(file_name, sizeof(file_name), "test-%d-%ld.txt", getpid(), t.tv_sec);
+
+ size_t length = 2 + strlen(path) + strlen(file_name);
+ char* full_path = malloc(length);
+ snprintf(full_path, length, "%s/%s", path, file_name);
+
+ TF_WritableFileHandle* h;
+ TF_Status* status = TF_NewStatus();
+ TF_NewWritableFile(full_path, &h, status);
+ if (TF_GetCode(status) != TF_OK) {
+ fprintf(stderr, "TF_NewWritableFile failed: %s\n", TF_Message(status));
+ return 1;
+ }
+ fprintf(stderr, "wrote %s\n", full_path);
+ free(full_path);
+ TF_CloseWritableFile(h, status);
+ if (TF_GetCode(status) != TF_OK) {
+ fprintf(stderr, "TF_CloseWritableFile failed: %s\n", TF_Message(status));
+ }
+ TF_StringStreamDone(s);
+
+ TF_KernelBuilder* b =
+ TF_NewKernelBuilder("SomeOp", "SomeDevice", &create, &compute, NULL);
+ TF_RegisterKernelBuilder("someKernel", b, status);
+
+ TF_DeleteStatus(status);
+ return 0;
+}
diff --git a/tensorflow/c/eager/BUILD b/tensorflow/c/eager/BUILD
index c34a84fcfee9b6ba9a7be86ae16e2856a2d343c7..282f0da302fac89c6fae9f8b5aa4b3c33ab93532 100644
--- a/tensorflow/c/eager/BUILD
+++ b/tensorflow/c/eager/BUILD
@@ -3,11 +3,19 @@ licenses(["notice"]) # Apache 2.0
load(
"//tensorflow:tensorflow.bzl",
- "tf_cuda_cc_test",
- "tf_cc_test",
"tf_copts",
- "tfe_xla_copts",
+ "tf_cuda_cc_test",
"tf_cuda_library",
+ "tfe_xla_copts",
+)
+load(
+ "//tensorflow/core:platform/default/build_config.bzl",
+ "tf_additional_device_tracer_test_flags",
+ "tf_kernel_tests_linkstatic",
+)
+load(
+ "//tensorflow/core:platform/default/build_config_root.bzl",
+ "tf_cuda_tests_tags",
)
tf_cuda_library(
@@ -62,6 +70,7 @@ tf_cuda_library(
"//tensorflow/core/distributed_runtime:remote_device",
"//tensorflow/core/distributed_runtime:server_lib",
"//tensorflow/core/distributed_runtime:worker_env",
+ "//tensorflow/core/profiler/lib:profiler_session",
"//tensorflow/core:gpu_runtime",
],
)
@@ -101,6 +110,7 @@ tf_cuda_library(
"//tensorflow/core/distributed_runtime/rpc:grpc_worker_service",
"//tensorflow/core/distributed_runtime/rpc:rpc_rendezvous_mgr",
"//tensorflow/core/distributed_runtime/rpc/eager:grpc_eager_client",
+ "//tensorflow/core/profiler/lib:profiler_session",
],
)
@@ -148,6 +158,88 @@ tf_cuda_cc_test(
],
)
+tf_cuda_library(
+ name = "c_api_experimental",
+ srcs = [
+ "c_api_experimental.cc",
+ ],
+ hdrs = ["c_api_experimental.h"],
+ copts = tf_copts() + tfe_xla_copts(),
+ visibility = ["//visibility:public"],
+ deps = select({
+ "//tensorflow:android": [
+ "//tensorflow/core:android_tensorflow_lib_lite",
+ ],
+ "//conditions:default": [
+ ":c_api",
+ ":c_api_internal",
+ "//tensorflow/c:c_api",
+ "//tensorflow/c:c_api_internal",
+ "//tensorflow/core:core_cpu",
+ "//tensorflow/core/common_runtime/eager:attr_builder",
+ "//tensorflow/core/common_runtime/eager:context",
+ "//tensorflow/core/common_runtime/eager:eager_executor",
+ "//tensorflow/core/common_runtime/eager:execute",
+ "//tensorflow/core/common_runtime/eager:kernel_and_device",
+ "//tensorflow/core/common_runtime/eager:tensor_handle",
+ "//tensorflow/core/common_runtime/eager:copy_to_device_node",
+ "//tensorflow/core:core_cpu_internal",
+ "//tensorflow/core:framework",
+ "//tensorflow/core:framework_internal",
+ "//tensorflow/core:lib",
+ "//tensorflow/core:lib_internal",
+ "//tensorflow/core:protos_all_cc",
+ ],
+ }) + select({
+ "//tensorflow:with_xla_support": [
+ "//tensorflow/compiler/tf2xla:xla_compiler",
+ "//tensorflow/compiler/jit",
+ "//tensorflow/compiler/jit:xla_device",
+ ],
+ "//conditions:default": [],
+ }) + [
+ "@com_google_absl//absl/memory",
+ "//tensorflow/core/common_runtime/eager:eager_operation",
+ "//tensorflow/core/distributed_runtime/eager:eager_client",
+ "//tensorflow/core/distributed_runtime/rpc/eager:grpc_eager_client",
+ "//tensorflow/core/distributed_runtime/rpc:grpc_channel",
+ "//tensorflow/core/distributed_runtime/rpc:grpc_server_lib",
+ "//tensorflow/core/distributed_runtime/rpc:grpc_worker_cache",
+ "//tensorflow/core/distributed_runtime/rpc:grpc_worker_service",
+ "//tensorflow/core/distributed_runtime/rpc:rpc_rendezvous_mgr",
+ "//tensorflow/core/distributed_runtime:remote_device",
+ "//tensorflow/core/distributed_runtime:server_lib",
+ "//tensorflow/core/distributed_runtime:worker_env",
+ "//tensorflow/core/profiler/rpc:profiler_server",
+ "//tensorflow/core/profiler/rpc/client:capture_profile",
+ "//tensorflow/core:gpu_runtime",
+ ],
+)
+
+tf_cuda_cc_test(
+ name = "c_api_experimental_test",
+ size = "small",
+ srcs = [
+ "c_api_experimental_test.cc",
+ ],
+ args =
+ ["--heap_check=local"] + tf_additional_device_tracer_test_flags(),
+ linkstatic = tf_kernel_tests_linkstatic(),
+ tags = tf_cuda_tests_tags() + ["nomac"],
+ deps = [
+ ":c_api_experimental",
+ ":c_api_test_util",
+ "//tensorflow/c:c_test_util",
+ "//tensorflow/cc/profiler",
+ "//tensorflow/core:lib",
+ "//tensorflow/core:protos_all_cc",
+ "//tensorflow/core:test",
+ "//tensorflow/core:test_main",
+ "//tensorflow/core/profiler:protos_all_cc",
+ "@com_google_absl//absl/strings",
+ ],
+)
+
cc_library(
name = "tape",
hdrs = ["tape.h"],
diff --git a/tensorflow/c/eager/c_api.cc b/tensorflow/c/eager/c_api.cc
index 027d752f420238da867cb9d8c116640e1730caaa..45701c7fcf02d5e6ec464ae10d4d20f20ba1d9f0 100755
--- a/tensorflow/c/eager/c_api.cc
+++ b/tensorflow/c/eager/c_api.cc
@@ -356,6 +356,8 @@ TFE_TensorHandle* TFE_NewTensorHandle(TF_Tensor* t, TF_Status* status) {
void TFE_DeleteTensorHandle(TFE_TensorHandle* h) {
if (h == nullptr) return;
+ VLOG(1) << "Deleting tensor handle " << h << " with internal handle "
+ << h->handle;
if (h->handle) {
h->handle->Unref();
}
@@ -443,15 +445,15 @@ TF_Tensor* TFE_TensorHandleResolve(TFE_TensorHandle* h, TF_Status* status) {
return nullptr;
}
// TODO(agarwal): move this implementation inside TFE_TensorHandle.
- tensorflow::Device* d = nullptr;
- tensorflow::Device* op_device = nullptr;
const tensorflow::Tensor* t = nullptr;
- status->status = h->handle->TensorAndDevice(&t, &d, &op_device);
- if (!status->status.ok()) return nullptr;
tensorflow::TensorHandle* h_cpu = nullptr;
- if (!IsCPU(d)) {
- status->status = h->handle->CopyToDevice(
- h->handle->Context(), h->handle->Context()->HostCPU(), &h_cpu);
+ tensorflow::Device* d = nullptr;
+ tensorflow::Device* op_device = nullptr;
+
+ if (h->handle->IsRemote()) {
+ status->status = EagerCopyToDevice(
+ h->handle, h->handle->Context(),
+ h->handle->Context()->HostCPU()->name().c_str(), &h_cpu);
if (!status->status.ok()) {
return nullptr;
}
@@ -460,6 +462,22 @@ TF_Tensor* TFE_TensorHandleResolve(TFE_TensorHandle* h, TF_Status* status) {
h_cpu->Unref();
return nullptr;
}
+ } else {
+ status->status = h->handle->TensorAndDevice(&t, &d, &op_device);
+ if (!status->status.ok()) return nullptr;
+
+ if (!IsCPU(d)) {
+ status->status = h->handle->CopyToDevice(
+ h->handle->Context(), h->handle->Context()->HostCPU(), &h_cpu);
+ if (!status->status.ok()) {
+ return nullptr;
+ }
+ status->status = h_cpu->TensorAndDevice(&t, &d, &op_device);
+ if (!status->status.ok()) {
+ h_cpu->Unref();
+ return nullptr;
+ }
+ }
}
TF_Tensor* retval = tensorflow::TF_TensorFromTensor(*t, status);
if (h_cpu != nullptr) {
@@ -696,6 +714,7 @@ void TFE_OpSetAttrFunctionList(TFE_Op* op, const char* attr_name,
void TFE_Execute(TFE_Op* op, TFE_TensorHandle** retvals, int* num_retvals,
TF_Status* status) {
+ VLOG(1) << "Calling TFE_Execute() on op " << op;
tensorflow::gtl::InlinedVector handle_retvals(
*num_retvals);
status->status =
@@ -738,12 +757,18 @@ void TFE_ContextAddFunction(TFE_Context* ctx, TF_Function* function,
status->status = ctx->context.AddFunctionDef(function->fdef);
}
+unsigned char TFE_ContextHasFunction(TFE_Context* ctx, const char* name) {
+ return ctx->context.FindFunctionDef(name) != nullptr;
+}
+
void TFE_ContextEnableRunMetadata(TFE_Context* ctx) {
- ctx->context.SetShouldStoreMetadata(true);
+ ctx->context.SetShouldStoreGraphs(true);
+ ctx->context.SetShouldStoreStepStats(true);
}
void TFE_ContextDisableRunMetadata(TFE_Context* ctx) {
- ctx->context.SetShouldStoreMetadata(false);
+ ctx->context.SetShouldStoreGraphs(false);
+ ctx->context.SetShouldStoreStepStats(false);
}
} // extern "C"
@@ -774,7 +799,7 @@ void TFE_ContextExportRunMetadata(TFE_Context* ctx, TF_Buffer* buf,
if (!status->status.ok()) return;
tensorflow::mutex_lock ml(*ctx->context.MetadataMu());
status->status = MessageToBuffer(*ctx->context.RunMetadataProto(), buf);
- ctx->context.RunMetadataProto()->Clear();
+ ctx->context.ClearRunMetadata();
}
namespace {
diff --git a/tensorflow/c/eager/c_api.h b/tensorflow/c/eager/c_api.h
index f80ae5a6d02d4d613c95cf8486e0fc0aeed3affc..044dfb7415b027b707af05a197fdb41fe1f6d2e5 100755
--- a/tensorflow/c/eager/c_api.h
+++ b/tensorflow/c/eager/c_api.h
@@ -170,23 +170,11 @@ TF_CAPI_EXPORT extern int64_t TFE_TensorHandleDim(TFE_TensorHandle* h,
int dim_index,
TF_Status* status);
-// Returns the device of the operation that produced `h`.
-// If `h` was produced by a copy, returns the destination device of
-// the copy. Note that returned device name is not always the device
-// holding the tensor handle's memory. If you want the latter, use
-// TFE_TensorHandleBackingDeviceName.
-// This function will block till the operation that produces `h` has completed.
-//
-// Device on which the kernel of the operation that produced `h` ran.
-//
-// If `h` was produced by a copy, returns the destination device of
-// the copy.
-//
-// Note that returned device name is not always the device that owns the memory
-// that backs the tensor handle. For the latter see
-// TFE_TensorHandleBackingDeviceName.
-//
-// This function will block till the operation that produces `h` has completed.
+// Returns the device of the operation that produced `h`. If `h` was produced by
+// a copy, returns the destination device of the copy. Note that the returned
+// device name is not always the device holding the tensor handle's memory. If
+// you want the latter, use TFE_TensorHandleBackingDeviceName. This function
+// will block till the operation that produces `h` has completed.
TF_CAPI_EXPORT extern const char* TFE_TensorHandleDeviceName(
TFE_TensorHandle* h, TF_Status* status);
@@ -405,6 +393,10 @@ TF_CAPI_EXPORT extern void TFE_ContextAddFunction(TFE_Context* ctx,
TF_Function* function,
TF_Status* status);
+// Checks whether a function is registered under `name`.
+TF_CAPI_EXPORT unsigned char TFE_ContextHasFunction(TFE_Context* ctx,
+ const char* name);
+
// Enables tracing of RunMetadata on the ops executed from this context.
TF_CAPI_EXPORT extern void TFE_ContextEnableRunMetadata(TFE_Context* ctx);
diff --git a/tensorflow/c/eager/c_api_debug.cc b/tensorflow/c/eager/c_api_debug.cc
index 52b0824552855860dfb138f3ac9a5d3afa7dc965..ffcd5ace0b98597363abe63201bf6c328a03212f 100644
--- a/tensorflow/c/eager/c_api_debug.cc
+++ b/tensorflow/c/eager/c_api_debug.cc
@@ -83,7 +83,7 @@ TF_CAPI_EXPORT extern TFE_TensorDebugInfo* TFE_TensorHandleTensorDebugInfo(
}
}
- if (xla::ShapeUtil::IsTuple(padded_shape)) {
+ if (padded_shape.IsTuple()) {
if (xla::ShapeUtil::TupleElementCount(padded_shape) != 2) {
// Currently, the only case of XlaTensor containing a tuple shape is to
// represent 64 bit ints, doubles, and complex numbers (we don't support
@@ -99,7 +99,7 @@ TF_CAPI_EXPORT extern TFE_TensorDebugInfo* TFE_TensorHandleTensorDebugInfo(
xla::Shape shape0 = xla::ShapeUtil::GetTupleElementShape(padded_shape, 0);
const xla::Shape& shape1 =
xla::ShapeUtil::GetTupleElementShape(padded_shape, 1);
- if (xla::ShapeUtil::IsTuple(shape0) || xla::ShapeUtil::IsTuple(shape1)) {
+ if (shape0.IsTuple() || shape1.IsTuple()) {
status->status = tensorflow::errors::InvalidArgument(
"XlaTensors should not contain nested tuples. Shape: ",
padded_shape.DebugString());
diff --git a/tensorflow/c/eager/c_api_experimental.cc b/tensorflow/c/eager/c_api_experimental.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ff798593b5f2f77339b668668ff6dafb9f44a2b3
--- /dev/null
+++ b/tensorflow/c/eager/c_api_experimental.cc
@@ -0,0 +1,93 @@
+/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include "tensorflow/c/eager/c_api_experimental.h"
+
+#include "tensorflow/c/c_api.h"
+#include "tensorflow/c/eager/c_api_internal.h"
+#include "tensorflow/core/profiler/rpc/client/capture_profile.h"
+#include "tensorflow/core/profiler/rpc/profiler_server.h"
+
+using tensorflow::string;
+
+void TFE_OpConsumeInput(TFE_Op* op, TFE_TensorHandle* h, TF_Status* status) {
+ op->operation.ConsumeInput(h->handle);
+}
+
+TFE_Profiler* TFE_NewProfiler(TFE_ProfilerContext* ctx) {
+ return new TFE_Profiler(ctx);
+}
+
+bool TFE_ProfilerIsOk(TFE_Profiler* profiler) {
+ return profiler->profiler->Status().ok();
+}
+
+void TFE_DeleteProfiler(TFE_Profiler* profiler) { delete profiler; }
+
+void TFE_ProfilerSerializeToString(TFE_Context* ctx, TFE_Profiler* profiler,
+ TF_Buffer* buf, TF_Status* status) {
+ TFE_ContextAsyncWait(ctx, status);
+ if (!status->status.ok()) return;
+ string content;
+ status->status = profiler->profiler->SerializeToString(&content);
+ void* data = tensorflow::port::Malloc(content.length());
+ content.copy(static_cast(data), content.length(), 0);
+ buf->data = data;
+ buf->length = content.length();
+ buf->data_deallocator = [](void* data, size_t length) {
+ tensorflow::port::Free(data);
+ };
+}
+
+TFE_ProfilerContext* TFE_NewProfilerContext() {
+ return new TFE_ProfilerContext;
+}
+
+void TFE_ProfilerContextSetEagerContext(TFE_ProfilerContext* profiler_context,
+ TFE_Context* eager_context) {
+ profiler_context->profiler_context.eager_context = &eager_context->context;
+}
+
+void TFE_DeleteProfilerContext(TFE_ProfilerContext* profiler_context) {
+ delete profiler_context;
+}
+
+void TFE_StartProfilerServer(TFE_ProfilerContext* context, int port) {
+ // Release child thread intentionally. The child thread can be terminate by
+ // terminating the main thread.
+ tensorflow::StartProfilerServer(&context->profiler_context, port).release();
+}
+
+void TFE_ContextEnableGraphCollection(TFE_Context* ctx) {
+ ctx->context.SetShouldStoreGraphs(true);
+}
+
+void TFE_ContextDisableGraphCollection(TFE_Context* ctx) {
+ ctx->context.SetShouldStoreGraphs(false);
+}
+
+bool TFE_ProfilerClientStartTracing(char* service_addr, char* logdir,
+ char* worker_list, bool include_dataset_ops,
+ int duration_ms, int num_tracing_attempts) {
+ tensorflow::Status s =
+ tensorflow::profiler::client::ValidateHostPortPair(service_addr);
+ if (!s.ok()) {
+ return false;
+ }
+ s = tensorflow::profiler::client::StartTracing(
+ service_addr, logdir, worker_list, include_dataset_ops, duration_ms,
+ num_tracing_attempts);
+ return s.ok();
+}
diff --git a/tensorflow/c/eager/c_api_experimental.h b/tensorflow/c/eager/c_api_experimental.h
new file mode 100644
index 0000000000000000000000000000000000000000..89523793d37b89ee49c4db844a85f019381ff730
--- /dev/null
+++ b/tensorflow/c/eager/c_api_experimental.h
@@ -0,0 +1,94 @@
+/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+#ifndef TENSORFLOW_C_EAGER_C_API_EXPERIMENTAL_H_
+#define TENSORFLOW_C_EAGER_C_API_EXPERIMENTAL_H_
+
+#include "tensorflow/c/c_api.h"
+#include "tensorflow/c/eager/c_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+TF_CAPI_EXPORT extern void TFE_OpConsumeInput(TFE_Op* op, TFE_TensorHandle* h,
+ TF_Status* status);
+
+typedef struct TFE_ProfilerContext TFE_ProfilerContext;
+
+// A profiler which will start profiling when creating the object and will stop
+// when the object is destroyed. It will profile all operations run under the
+// given TFE_Context. Multiple instance of it can be created, but at most one
+// of them will profile for each TFE_Context.
+// Thread-safety: TFE_Profiler is thread-safe.
+typedef struct TFE_Profiler TFE_Profiler;
+
+TF_CAPI_EXPORT extern TFE_Profiler* TFE_NewProfiler(TFE_ProfilerContext* ctx);
+TF_CAPI_EXPORT extern bool TFE_ProfilerIsOk(TFE_Profiler* profiler);
+TF_CAPI_EXPORT extern void TFE_DeleteProfiler(TFE_Profiler* profiler);
+
+// The output string is a binary string of tensorflow.tpu.Trace. User can write
+// the string to file for offline analysis by tensorboard.
+TF_CAPI_EXPORT extern void TFE_ProfilerSerializeToString(TFE_Context* ctx,
+ TFE_Profiler* profiler,
+ TF_Buffer* buf,
+ TF_Status* status);
+
+// Return a new profiler context object.
+TF_CAPI_EXPORT extern TFE_ProfilerContext* TFE_NewProfilerContext(void);
+
+// Set the eager context in TFE_ProfilerServerOptions
+TF_CAPI_EXPORT extern void TFE_ProfilerContextSetEagerContext(
+ TFE_ProfilerContext* profiler_context, TFE_Context* eager_context);
+
+// Destroy a profiler context object.
+TF_CAPI_EXPORT extern void TFE_DeleteProfilerContext(
+ TFE_ProfilerContext* profiler_context);
+
+// Start a profiler grpc server which listens to specified port. It will start
+// the server on its own thread. It can be shutdown by terminating tensorflow.
+// It can be used in both Eager mode and graph mode. Creating multiple profiler
+// server is allowed. The service defined in
+// tensorflow/contrib/tpu/profiler/tpu_profiler.proto. Please use
+// tensorflow/contrib/tpu/profiler/capture_tpu_profile to capture tracable
+// file following
+// https://cloud.google.com/tpu/docs/cloud-tpu-tools#capture_trace.
+TF_CAPI_EXPORT extern void TFE_StartProfilerServer(TFE_ProfilerContext* context,
+ int port);
+
+// Enables only graph collection in RunMetadata on the functions executed from
+// this context.
+TF_CAPI_EXPORT extern void TFE_ContextEnableGraphCollection(TFE_Context* ctx);
+
+// Disables only graph collection in RunMetadata on the functions executed from
+// this context.
+TF_CAPI_EXPORT extern void TFE_ContextDisableGraphCollection(TFE_Context* ctx);
+
+// Send a grpc request to profiler server (service_addr) to perform on-demand
+// profiling and save the result into logdir which can be visualized by
+// TensorBoard. worker_list is the list of worker TPUs separated by ','. Set
+// include_dataset_opts to false to profile longer traces. It will block the
+// caller thread until receives tracing result.
+// This API is designed for TensorBoard, for end user, please use
+// tensorflow/contrib/tpu/profiler/capture_tpu_profile instead following
+// https://cloud.google.com/tpu/docs/cloud-tpu-tools#capture_trace.
+TF_CAPI_EXPORT extern bool TFE_ProfilerClientStartTracing(
+ char* service_addr, char* logdir, char* worker_list,
+ bool include_dataset_ops, int duration_ms, int num_tracing_attempts);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif // TENSORFLOW_C_EAGER_C_API_EXPERIMENTAL_H_
diff --git a/tensorflow/c/eager/c_api_experimental_test.cc b/tensorflow/c/eager/c_api_experimental_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d85048caa7c7f727271352883cb834a2575bd251
--- /dev/null
+++ b/tensorflow/c/eager/c_api_experimental_test.cc
@@ -0,0 +1,129 @@
+/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include "tensorflow/c/eager/c_api_experimental.h"
+
+#include
+#include "tensorflow/c/eager/c_api_test_util.h"
+#include "tensorflow/cc/profiler/profiler.h"
+#include "tensorflow/core/lib/strings/str_util.h"
+#include "tensorflow/core/platform/logging.h"
+#include "tensorflow/core/platform/protobuf.h"
+#include "tensorflow/core/platform/test.h"
+#include "tensorflow/core/platform/test_benchmark.h"
+#include "tensorflow/core/profiler/trace_events.pb.h"
+
+using tensorflow::string;
+
+namespace tensorflow {
+namespace {
+
+static bool HasSubstr(absl::string_view base, absl::string_view substr) {
+ bool ok = str_util::StrContains(base, substr);
+ EXPECT_TRUE(ok) << base << ", expected substring " << substr;
+ return ok;
+}
+
+void ExecuteWithProfiling(bool async) {
+ TF_Status* status = TF_NewStatus();
+ TFE_ContextOptions* opts = TFE_NewContextOptions();
+ TFE_ContextOptionsSetAsync(opts, static_cast(async));
+ TFE_Context* ctx = TFE_NewContext(opts, status);
+ TFE_ProfilerContext* profiler_context = TFE_NewProfilerContext();
+ TFE_ProfilerContextSetEagerContext(profiler_context, ctx);
+ TFE_Profiler* profiler = TFE_NewProfiler(profiler_context);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_DeleteContextOptions(opts);
+ TFE_DeleteProfilerContext(profiler_context);
+
+ TFE_TensorHandle* m = TestMatrixTensorHandle();
+ TFE_Op* matmul = MatMulOp(ctx, m, m);
+ TFE_TensorHandle* retvals[1] = {nullptr};
+ int num_retvals = 1;
+
+ // Run op on GPU if it is present.
+ string gpu_device_name;
+ if (GetDeviceName(ctx, &gpu_device_name, "GPU")) {
+ TFE_OpSetDevice(matmul, gpu_device_name.c_str(), status);
+ ASSERT_TRUE(TF_GetCode(status) == TF_OK) << TF_Message(status);
+ const char* device_name = TFE_OpGetDevice(matmul, status);
+ ASSERT_TRUE(strstr(device_name, "GPU:0") != nullptr);
+ }
+
+ TFE_Execute(matmul, &retvals[0], &num_retvals, status);
+ EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_DeleteOp(matmul);
+ TFE_DeleteTensorHandle(m);
+
+ ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ ASSERT_EQ(1, num_retvals);
+ TF_Buffer* profiler_result = TF_NewBuffer();
+ TFE_ProfilerSerializeToString(ctx, profiler, profiler_result, status);
+ TFE_DeleteProfiler(profiler);
+ ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ profiler::Trace profile_proto;
+ EXPECT_TRUE(profile_proto.ParseFromString(
+ {reinterpret_cast(profiler_result->data),
+ profiler_result->length}));
+ string profile_proto_str = profile_proto.DebugString();
+ if (!gpu_device_name.empty()) {
+ EXPECT_TRUE(HasSubstr(profile_proto_str, "GPU:0"));
+ // device name with "stream:all" is collected by Device Tracer.
+ EXPECT_TRUE(HasSubstr(profile_proto_str, "stream:all"));
+ }
+ EXPECT_TRUE(HasSubstr(profile_proto_str, "CPU:0"));
+ TF_DeleteBuffer(profiler_result);
+
+ TF_Tensor* t = TFE_TensorHandleResolve(retvals[0], status);
+ TFE_DeleteTensorHandle(retvals[0]);
+ TFE_DeleteContext(ctx);
+ ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ float product[4] = {0};
+ EXPECT_EQ(sizeof(product), TF_TensorByteSize(t));
+ memcpy(&product[0], TF_TensorData(t), TF_TensorByteSize(t));
+ TF_DeleteTensor(t);
+ EXPECT_EQ(7, product[0]);
+ EXPECT_EQ(10, product[1]);
+ EXPECT_EQ(15, product[2]);
+ EXPECT_EQ(22, product[3]);
+ TF_DeleteStatus(status);
+}
+TEST(CAPI, ExecuteWithTracing) { ExecuteWithProfiling(false); }
+TEST(CAPI, ExecuteWithTracingAsync) { ExecuteWithProfiling(true); }
+
+TEST(CAPI, MultipleProfilerSession) {
+ TF_Status* status = TF_NewStatus();
+ TFE_ContextOptions* opts = TFE_NewContextOptions();
+ TFE_ContextOptionsSetAsync(opts, static_cast(false));
+ TFE_Context* ctx = TFE_NewContext(opts, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_DeleteContextOptions(opts);
+
+ TFE_ProfilerContext* profiler_context = TFE_NewProfilerContext();
+ TFE_ProfilerContextSetEagerContext(profiler_context, ctx);
+
+ TFE_Profiler* profiler1 = TFE_NewProfiler(profiler_context);
+ EXPECT_TRUE(TFE_ProfilerIsOk(profiler1));
+
+ TFE_Profiler* profiler2 = TFE_NewProfiler(profiler_context);
+ EXPECT_FALSE(TFE_ProfilerIsOk(profiler2));
+
+ TFE_DeleteProfiler(profiler1);
+ TFE_DeleteProfiler(profiler2);
+ TFE_DeleteProfilerContext(profiler_context);
+}
+
+} // namespace
+} // namespace tensorflow
diff --git a/tensorflow/c/eager/c_api_internal.h b/tensorflow/c/eager/c_api_internal.h
index 67bc1bcd24605f8363d6a7c8d5d6a0836a42fc82..a563e4b8f50f2a90497736f4cb9ca234400bfa04 100644
--- a/tensorflow/c/eager/c_api_internal.h
+++ b/tensorflow/c/eager/c_api_internal.h
@@ -52,6 +52,7 @@ limitations under the License.
#include "tensorflow/core/lib/gtl/stl_util.h"
#include "tensorflow/core/platform/mutex.h"
#include "tensorflow/core/platform/thread_annotations.h"
+#include "tensorflow/core/profiler/lib/profiler_session.h"
#include "tensorflow/core/public/version.h"
struct TFE_ContextOptions {
@@ -82,6 +83,12 @@ struct TFE_TensorHandle {
TFE_TensorHandle(tensorflow::TensorHandle* handle) : handle(handle) {}
tensorflow::TensorHandle* handle;
+
+ // Create a symbolic tensor.
+ TFE_TensorHandle(TF_Output t, TF_DataType dtype)
+ : handle(new tensorflow::TensorHandle(
+ tensorflow::OutputGraphNode{t.oper, t.index},
+ static_cast(dtype))) {}
};
struct TFE_TensorDebugInfo {
@@ -100,6 +107,18 @@ struct TFE_Op {
tensorflow::EagerOperation operation;
};
+struct TFE_ProfilerContext {
+ tensorflow::ProfilerContext profiler_context;
+};
+
+struct TFE_Profiler {
+ TFE_Profiler(TFE_ProfilerContext* ctx) {
+ profiler = tensorflow::ProfilerSession::Create(&ctx->profiler_context);
+ }
+
+ std::unique_ptr profiler;
+};
+
namespace tensorflow {
// Set an AttrValue on the op. Doesn't handle the list types.
void SetOpAttrValueScalar(TFE_Context* ctx, TFE_Op* op,
@@ -107,4 +126,24 @@ void SetOpAttrValueScalar(TFE_Context* ctx, TFE_Op* op,
const char* attr_name, TF_Status* status);
} // namespace tensorflow
+struct TFE_TraceContext {
+ TF_Graph* const graph;
+
+ unsigned int node_counter = 0;
+ // Each tensor handle will have its ref count incremented when it's added as a
+ // map key, and decremented when this object is destroyed.
+ std::map input_tensor_map;
+ std::vector>* input_tensors =
+ nullptr;
+
+ TFE_TraceContext(TF_Graph* graph) : graph(graph) {}
+
+ ~TFE_TraceContext() {
+ delete input_tensors;
+ for (auto input : input_tensor_map) {
+ input.first->Unref();
+ }
+ }
+};
+
#endif // TENSORFLOW_C_EAGER_C_API_INTERNAL_H_
diff --git a/tensorflow/c/eager/c_api_test.cc b/tensorflow/c/eager/c_api_test.cc
index 6b39b79ee82f9c7baaf856e573a42b7da65691e5..3d1ca4fb4b561a03ea9d879b1876fb1fd08a3139 100644
--- a/tensorflow/c/eager/c_api_test.cc
+++ b/tensorflow/c/eager/c_api_test.cc
@@ -175,13 +175,8 @@ void TestRemoteExecute(bool async) {
TFE_Execute(matmul, &retvals[0], &num_retvals, status);
EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
- auto* retval_task0 = TFE_TensorHandleCopyToDevice(
- retvals[0], ctx, "/job:localhost/replica:0/task:0/device:CPU:0", status);
- ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
-
- TF_Tensor* t = TFE_TensorHandleResolve(retval_task0, status);
+ TF_Tensor* t = TFE_TensorHandleResolve(retvals[0], status);
ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
- TFE_DeleteTensorHandle(retval_task0);
float product[4] = {0};
EXPECT_EQ(sizeof(product), TF_TensorByteSize(t));
memcpy(&product[0], TF_TensorData(t), TF_TensorByteSize(t));
diff --git a/tensorflow/c/env.cc b/tensorflow/c/env.cc
index 07b9e8b940c55caf62ae0b81b884bf313d335459..1c35ff9001d0ee1ab0fbae9e1bcc07116fab1065 100644
--- a/tensorflow/c/env.cc
+++ b/tensorflow/c/env.cc
@@ -159,3 +159,25 @@ TF_CAPI_EXPORT extern uint64_t TF_NowMicros(void) {
TF_CAPI_EXPORT extern uint64_t TF_NowSeconds(void) {
return ::tensorflow::Env::Default()->NowSeconds();
}
+
+void TF_DefaultThreadOptions(TF_ThreadOptions* options) {
+ options->stack_size = 0;
+ options->guard_size = 0;
+ options->numa_node = -1;
+}
+
+TF_Thread* TF_StartThread(const TF_ThreadOptions* options,
+ const char* thread_name, void (*work_func)(void*),
+ void* param) {
+ ::tensorflow::ThreadOptions cc_options;
+ cc_options.stack_size = options->stack_size;
+ cc_options.guard_size = options->guard_size;
+ cc_options.numa_node = options->numa_node;
+ return reinterpret_cast(::tensorflow::Env::Default()->StartThread(
+ cc_options, thread_name, [=]() { (*work_func)(param); }));
+}
+
+void TF_JoinThread(TF_Thread* thread) {
+ // ::tensorflow::Thread joins on destruction
+ delete reinterpret_cast<::tensorflow::Thread*>(thread);
+}
diff --git a/tensorflow/c/env.h b/tensorflow/c/env.h
index 9d27c5da37735042c7476b591e57486dbde33152..73078fcbbc5ae4c042f4a992655072a838e42915 100644
--- a/tensorflow/c/env.h
+++ b/tensorflow/c/env.h
@@ -13,6 +13,10 @@ See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
+#include
+#include
+#include
+
#ifndef TENSORFLOW_C_ENV_H_
#define TENSORFLOW_C_ENV_H_
@@ -21,13 +25,14 @@ limitations under the License.
// --------------------------------------------------------------------------
// C API for tensorflow::Env.
-struct TF_WritableFileHandle;
-struct TF_StringStream;
-
#ifdef __cplusplus
extern "C" {
#endif
+typedef struct TF_WritableFileHandle TF_WritableFileHandle;
+typedef struct TF_StringStream TF_StringStream;
+typedef struct TF_Thread TF_Thread;
+
typedef struct TF_FileStatistics {
// The length of the file in bytes.
int64_t length;
@@ -37,6 +42,20 @@ typedef struct TF_FileStatistics {
bool is_directory;
} TF_FileStatistics;
+typedef struct TF_ThreadOptions {
+ // Thread stack size to use (in bytes), zero implies that the system default
+ // will be used.
+ size_t stack_size;
+
+ // Guard area size to use near thread stacks to use (in bytes), zero implies
+ // that the system default will be used.
+ size_t guard_size;
+
+ // The NUMA node to use, -1 implies that there should be no NUMA affinity for
+ // this thread.
+ int numa_node;
+} TF_ThreadOptions;
+
// Creates the specified directory. Typical status code are:
// * TF_OK - successfully created the directory
// * TF_ALREADY_EXISTS - directory already exists
@@ -150,6 +169,25 @@ TF_CAPI_EXPORT extern uint64_t TF_NowMicros(void);
// Returns the number of seconds since the Unix epoch.
TF_CAPI_EXPORT extern uint64_t TF_NowSeconds(void);
+// Populates a TF_ThreadOptions struct with system-default values.
+TF_CAPI_EXPORT extern void TF_DefaultThreadOptions(TF_ThreadOptions* options);
+
+// Returns a new thread that is running work_func and is identified
+// (for debugging/performance-analysis) by thread_name.
+//
+// The given param (which may be null) is passed to work_func when the thread
+// starts. In this way, data may be passed from the thread back to the caller.
+//
+// Caller takes ownership of the result and must call TF_JoinThread on it
+// eventually.
+TF_CAPI_EXPORT extern TF_Thread* TF_StartThread(const TF_ThreadOptions* options,
+ const char* thread_name,
+ void (*work_func)(void*),
+ void* param);
+
+// Waits for the given thread to finish execution, then deletes it.
+TF_CAPI_EXPORT extern void TF_JoinThread(TF_Thread* thread);
+
#ifdef __cplusplus
}
#endif
diff --git a/tensorflow/c/env_test.cc b/tensorflow/c/env_test.cc
index e2206c6befd2167346c64032940d6e8c631e4a3e..687ad024137352662759ec1f43df87e89faca353 100644
--- a/tensorflow/c/env_test.cc
+++ b/tensorflow/c/env_test.cc
@@ -17,6 +17,7 @@ limitations under the License.
#include "tensorflow/c/c_api.h"
#include "tensorflow/core/lib/io/path.h"
+#include "tensorflow/core/platform/mutex.h"
#include "tensorflow/core/platform/test.h"
#include "tensorflow/core/platform/types.h"
@@ -98,3 +99,29 @@ TEST(TestEnv, TestTimeFunctions) {
ASSERT_GE(TF_NowMicros(), 946684800 * 1e6);
ASSERT_GE(TF_NowNanos(), 946684800 * 1e9);
}
+
+namespace {
+
+struct SomeThreadData {
+ ::tensorflow::mutex mu;
+ bool did_work = false;
+};
+
+void SomeThreadFunc(void* data) {
+ auto* real_data = static_cast(data);
+ ::tensorflow::mutex_lock l(real_data->mu);
+ real_data->did_work = true;
+}
+
+} // namespace
+
+TEST(TestEnv, TestThreads) {
+ TF_ThreadOptions options;
+ TF_DefaultThreadOptions(&options);
+ SomeThreadData data;
+ TF_Thread* thread =
+ TF_StartThread(&options, "SomeThreadName", &SomeThreadFunc, &data);
+ TF_JoinThread(thread);
+ ::tensorflow::mutex_lock l(data.mu);
+ ASSERT_TRUE(data.did_work);
+}
diff --git a/tensorflow/c/kernels.cc b/tensorflow/c/kernels.cc
index 2a4eaecb6cf2740a522b1e849d1306ebde6c4577..71181ae430ab64106e2a75937bd54fbf2efc61ac 100644
--- a/tensorflow/c/kernels.cc
+++ b/tensorflow/c/kernels.cc
@@ -48,9 +48,10 @@ TF_KernelBuilder* TF_NewKernelBuilder(
}
void TF_DeleteKernelBuilder(TF_KernelBuilder* builder) {
- DCHECK_NE(builder, nullptr);
- delete builder->cc_builder;
- delete builder;
+ if (builder != nullptr) {
+ delete builder->cc_builder;
+ delete builder;
+ }
}
namespace tensorflow {
@@ -158,3 +159,41 @@ void TF_SetOutput(TF_OpKernelContext* ctx, int i, const TF_Tensor* tensor,
cc_ctx->set_output(i, cc_tensor);
}
}
+
+void TF_OpKernelConstruction_Failure(TF_OpKernelConstruction* ctx,
+ TF_Status* status) {
+ auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelConstruction*>(ctx);
+ ::tensorflow::Status s(::tensorflow::StatusFromTF_Status(status));
+ cc_ctx->CtxFailure(s);
+}
+
+void TF_OpKernelContext_Failure(TF_OpKernelContext* ctx, TF_Status* status) {
+ auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx);
+ ::tensorflow::Status s(::tensorflow::StatusFromTF_Status(status));
+ cc_ctx->CtxFailure(s);
+}
+
+#define DEFINE_TF_GETATTR(func, c_type, cc_type) \
+ void TF_OpKernelConstruction_GetAttr##func(TF_OpKernelConstruction* ctx, \
+ const char* attr_name, \
+ c_type* val, TF_Status* status) { \
+ TF_SetStatus(status, TF_OK, ""); \
+ cc_type v; \
+ auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelConstruction*>(ctx); \
+ ::tensorflow::Status s = cc_ctx->GetAttr(attr_name, &v); \
+ ::tensorflow::Set_TF_Status_from_Status(status, s); \
+ if (s.ok()) { \
+ *val = static_cast(v); \
+ } \
+ }
+
+DEFINE_TF_GETATTR(Type, TF_DataType, tensorflow::DataType)
+
+TF_DataType TF_ExpectedOutputDataType(TF_OpKernelContext* ctx, int i) {
+ auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx);
+ return static_cast(cc_ctx->expected_output_dtype(i));
+}
+
+int64_t TF_StepId(TF_OpKernelContext* ctx) {
+ return reinterpret_cast<::tensorflow::OpKernelContext*>(ctx)->step_id();
+}
diff --git a/tensorflow/c/kernels.h b/tensorflow/c/kernels.h
index 1a91aa184f11ac8e45b38a1d106c7b445747a7c1..c47bfa8aa3a721d422a0a1536b924f3e53793193 100644
--- a/tensorflow/c/kernels.h
+++ b/tensorflow/c/kernels.h
@@ -35,9 +35,9 @@ extern "C" {
// `TF_RegisterKernelBuilder`, which will allow TF to construct user-provided
// kernels when necessary.
-struct TF_KernelBuilder;
-struct TF_OpKernelConstruction;
-struct TF_OpKernelContext;
+typedef struct TF_KernelBuilder TF_KernelBuilder;
+typedef struct TF_OpKernelConstruction TF_OpKernelConstruction;
+typedef struct TF_OpKernelContext TF_OpKernelContext;
// Allocates a new kernel builder and returns a pointer to it.
//
@@ -111,6 +111,32 @@ TF_CAPI_EXPORT extern void TF_SetOutput(TF_OpKernelContext* ctx, int i,
const TF_Tensor* tensor,
TF_Status* status);
+// Notifies the given OpKernelConstruction that kernel construction has failed.
+TF_CAPI_EXPORT extern void TF_OpKernelConstruction_Failure(
+ TF_OpKernelConstruction* ctx, TF_Status* status);
+
+// Notifies the given OpKernelContext that the kernel's compute function has
+// failed.
+TF_CAPI_EXPORT extern void TF_OpKernelContext_Failure(TF_OpKernelContext* ctx,
+ TF_Status* status);
+
+// Returns the expected output data type of the ith output. If i < 0 or
+// i >= TF_NumOutputs(ctx), the program aborts.
+TF_CAPI_EXPORT extern TF_DataType TF_ExpectedOutputDataType(
+ TF_OpKernelContext* ctx, int i);
+
+// Returns the step ID of the given context.
+TF_CAPI_EXPORT extern int64_t TF_StepId(TF_OpKernelContext* ctx);
+
+// Interprets the named kernel construction attribute as a TF_DataType and
+// places it into *val. *status is set to TF_OK.
+//
+// If the attribute could not be found or could not be interpreted as
+// TF_DataType, *status is populated with an error.
+TF_CAPI_EXPORT extern void TF_OpKernelConstruction_GetAttrType(
+ TF_OpKernelConstruction* ctx, const char* attr_name, TF_DataType* val,
+ TF_Status* status);
+
#ifdef __cplusplus
} /* end extern "C" */
#endif
diff --git a/tensorflow/c/kernels_test.cc b/tensorflow/c/kernels_test.cc
index e659ee3c3d258a626ccf03a782ec031b5a703a48..608887722f7bca44c884a3426d5e378e9387a530 100644
--- a/tensorflow/c/kernels_test.cc
+++ b/tensorflow/c/kernels_test.cc
@@ -16,6 +16,7 @@ limitations under the License.
#include "tensorflow/c/kernels.h"
#include "tensorflow/c/c_api.h"
+#include "tensorflow/core/framework/attr_value.pb.h"
#include "tensorflow/core/framework/kernel_def.pb.h"
#include "tensorflow/core/framework/node_def.pb_text.h"
#include "tensorflow/core/framework/op.h"
@@ -35,12 +36,24 @@ static void* MyCreateFunc(TF_OpKernelConstruction* ctx) {
struct MyCustomKernel* s = new struct MyCustomKernel;
s->created = true;
s->compute_called = false;
+
+ // Exercise attribute reads.
+ TF_DataType type;
+ TF_Status* status = TF_NewStatus();
+ TF_OpKernelConstruction_GetAttrType(ctx, "SomeDataTypeAttr", &type, status);
+ EXPECT_EQ(TF_OK, TF_GetCode(status));
+ EXPECT_EQ(TF_FLOAT, type);
+ TF_DeleteStatus(status);
+
return s;
}
static void MyComputeFunc(void* kernel, TF_OpKernelContext* ctx) {
struct MyCustomKernel* s = static_cast(kernel);
s->compute_called = true;
+ if (ctx != nullptr) {
+ EXPECT_EQ(43, TF_StepId(ctx));
+ }
}
static void MyDeleteFunc(void* kernel) {
@@ -61,6 +74,11 @@ static std::unique_ptr GetFakeKernel(const char* device_name,
def.set_device(device_name);
def.add_input("input1");
def.add_input("input2");
+
+ AttrValue v;
+ v.set_type(DataType::DT_FLOAT);
+ (*def.mutable_attr())["SomeDataTypeAttr"] = v;
+
return CreateOpKernel(DeviceType(device_name), nullptr, nullptr, def, 1,
status);
}
@@ -75,7 +93,8 @@ TEST(TestKernel, TestRegisterKernelBuilder) {
REGISTER_OP(op_name)
.Input("input1: double")
.Input("input2: uint8")
- .Output("output1: uint8");
+ .Output("output1: uint8")
+ .Attr("SomeDataTypeAttr: type");
TF_KernelBuilder* builder = TF_NewKernelBuilder(
op_name, device_name, &MyCreateFunc, &MyComputeFunc, &MyDeleteFunc);
@@ -126,7 +145,8 @@ TEST(TestKernel, TestInputAndOutputCount) {
REGISTER_OP(op_name)
.Input("input1: double")
.Input("input2: uint8")
- .Output("output1: uint8");
+ .Output("output1: uint8")
+ .Attr("SomeDataTypeAttr: type");
static int num_inputs = 0;
static int num_outputs = 0;
@@ -155,6 +175,8 @@ TEST(TestKernel, TestInputAndOutputCount) {
TF_SetOutput(ctx, 24, input, s);
EXPECT_EQ(TF_OUT_OF_RANGE, TF_GetCode(s));
+ EXPECT_EQ(TF_UINT8, TF_ExpectedOutputDataType(ctx, 0));
+
TF_DeleteStatus(s);
if (input != nullptr) {
TF_DeleteTensor(input);
@@ -175,6 +197,7 @@ TEST(TestKernel, TestInputAndOutputCount) {
OpKernelContext::Params p;
DummyDevice dummy_device(nullptr, false);
p.device = &dummy_device;
+ p.step_id = 43;
Tensor t(tensorflow::uint8(123));
@@ -200,4 +223,8 @@ TEST(TestKernel, TestInputAndOutputCount) {
}
}
+TEST(TestKernel, DeleteKernelBuilderIsOkOnNull) {
+ TF_DeleteKernelBuilder(nullptr);
+}
+
} // namespace tensorflow
diff --git a/tensorflow/cc/BUILD b/tensorflow/cc/BUILD
index a09becc49b10d2c58f98fbcc11df5190f794c1d4..4c4d587fce04d101b3cc8faebcc3ba04f2f1d0cf 100644
--- a/tensorflow/cc/BUILD
+++ b/tensorflow/cc/BUILD
@@ -150,6 +150,7 @@ cc_library_with_android_deps(
"//tensorflow/core:framework",
"//tensorflow/core:lib",
"//tensorflow/core:lib_internal",
+ "//tensorflow/core:ops",
"//tensorflow/core:protos_all_cc",
],
)
@@ -586,6 +587,25 @@ tf_gen_op_wrappers_cc(
pkg = "//tensorflow/core",
)
+tf_gen_op_wrappers_cc(
+ name = "tpu_ops",
+ include_internal_ops = 1,
+ op_lib_names = [
+ "tpu_configuration_ops",
+ "tpu_cross_replica_ops",
+ "tpu_embedding_ops",
+ "tpu_functional_ops",
+ "tpu_heartbeat_ops",
+ "tpu_host_compute_ops",
+ "tpu_infeed_ops",
+ "tpu_outfeed_ops",
+ "tpu_ordinal_selector_ops",
+ "tpu_replication_ops",
+ ],
+ pkg = "//tensorflow/core",
+ visibility = ["//tensorflow:internal"],
+)
+
cc_library_with_android_deps(
name = "cc_op_gen_main",
srcs = [
diff --git a/tensorflow/cc/framework/cc_op_gen.cc b/tensorflow/cc/framework/cc_op_gen.cc
index 39593370d1c243e84dc5b6091724d1d404c102b0..43a33cbea6e1e4a50f61cc7d6d8d70cac6a603d2 100644
--- a/tensorflow/cc/framework/cc_op_gen.cc
+++ b/tensorflow/cc/framework/cc_op_gen.cc
@@ -321,6 +321,7 @@ std::pair AttrTypeName(StringPiece attr_type) {
{"tensor", {"TensorProto", true}},
{"list(tensor)", {"gtl::ArraySlice", true}},
{"func", {"NameAttrList", true}},
+ {"list(func)", {"gtl::ArraySlice", true}},
};
auto entry = attr_type_map->find(attr_type);
diff --git a/tensorflow/cc/framework/gradients.cc b/tensorflow/cc/framework/gradients.cc
index affd90b1bcc7cb4a8b3ffed6aeeb4bd480f5e314..a7e645e8b556f14f0c7a51d2eba6ab1e2256b837 100644
--- a/tensorflow/cc/framework/gradients.cc
+++ b/tensorflow/cc/framework/gradients.cc
@@ -96,7 +96,7 @@ class SymbolicGradientBuilder {
// Used to identify nodes at which to stop backprop.
std::unordered_set GetStopBackpropNodes(
const std::vector& reachable_nodes,
- std::unordered_set output_nodes);
+ const std::unordered_set& output_nodes);
const Scope& scope_;
const ops::GradOpRegistry* registry_;
@@ -167,7 +167,6 @@ Status SymbolicGradientBuilder::BackpropAlongEdge(const Output& dst_grad,
std::vector SymbolicGradientBuilder::GetReachableNodes() {
std::vector reachable_nodes(scope_.graph()->num_node_ids(), false);
std::deque queue;
- std::vector visited(scope_.graph()->num_node_ids(), false);
for (const Output& out : outputs_) {
if (!reachable_nodes[out.node()->id()]) {
queue.push_back(out.node());
@@ -180,10 +179,10 @@ std::vector SymbolicGradientBuilder::GetReachableNodes() {
queue.pop_front();
for (const Edge* e : n->in_edges()) {
if (e->IsControlEdge()) continue;
- if (visited[e->src()->id()]) continue;
- queue.push_back(e->src());
- reachable_nodes[e->src()->id()] = true;
- visited[e->src()->id()] = true;
+ if (!reachable_nodes[e->src()->id()]) {
+ queue.push_back(e->src());
+ reachable_nodes[e->src()->id()] = true;
+ }
}
}
return reachable_nodes;
@@ -191,7 +190,7 @@ std::vector SymbolicGradientBuilder::GetReachableNodes() {
std::unordered_set SymbolicGradientBuilder::GetStopBackpropNodes(
const std::vector& reachable_nodes,
- std::unordered_set output_nodes) {
+ const std::unordered_set& output_nodes) {
// Output nodes that get transitively consumed by other `outputs_` are stored
// in `internal_outputs`.
std::unordered_set internal_outputs;
diff --git a/tensorflow/cc/gradients/image_grad.cc b/tensorflow/cc/gradients/image_grad.cc
index 882709e1e2817431a32c453fe0f35f2b2e6c69b0..05c287bdc62cdb8be7208ce3975f280aaa816766 100644
--- a/tensorflow/cc/gradients/image_grad.cc
+++ b/tensorflow/cc/gradients/image_grad.cc
@@ -69,6 +69,23 @@ Status ResizeBicubicGradHelper(const Scope& scope, const Operation& op,
}
REGISTER_GRADIENT_OP("ResizeBicubic", ResizeBicubicGradHelper);
+Status ScaleAndTranslateGradHelper(const Scope& scope, const Operation& op,
+ const std::vector