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/CODEOWNERS b/CODEOWNERS
index 54a61a4d72c40d297d90d53e223f64f813d9167d..cb3fa2312405ce44d5dfc30ea4164740f436e07e 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1,7 +1,7 @@
# Where component owners are known, add them here.
/tenosrflow/core/debug @caisq
-/tensorflow/core/nccl/ @azaks @csigg
+/tensorflow/core/nccl/ @azaks2 @chsigg
/tensorflow/core/platform/windows/ @mrry
/tensorflow/core/platform/s3 @yongtang
/tensorflow/go @asimshankar
@@ -51,13 +51,13 @@
/tensorflow/contrib/pi_examples/ @maciekcc
/tensorflow/contrib/quantization/ @petewarden
/tensorflow/contrib/rnn/ @ebrevdo @scottzhu
-/tensorflow/contrib/saved_model/ @nfiedel @sukritiramesh @allenl
+/tensorflow/contrib/saved_model/ @nfiedel @sukritiramesh @allenlavoie
/tensorflow/contrib/seq2seq/ @ebrevdo @lmthang
/tensorflow/contrib/session_bundle/ @nfiedel @sukritiramesh
/tensorflow/contrib/slim/ @sguada @thenbasilmanran
/tensorflow/contrib/stateless/ @girving @alextp
/tensorflow/contrib/tensor_forest/ @gilberthendry @thomascolthurst @yupbank
-/tensorflow/contrib/tensorrt/ @aaroey
+/tensorflow/contrib/tensorrt/ @aaroey @smit-hinsu @azaks2
# NEED OWNER: /tensorflow/contrib/testing/
/tensorflow/contrib/timeseries/ @allenlavoie
/tensorflow/contrib/tpu/ @frankchn @saeta @jhseu @sourabhbajaj
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..957b8d8528dc9b5e2ea134921b28601aa6fed2d1 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,64 @@ 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//container:container.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 = "4fe4ee824200b48821730f89ff260984332dc3551db587c24691235d1d96a8a7",
+ strip_prefix = "rules_apple-0.10.0",
+ urls = ["https://github.com/bazelbuild/rules_apple/archive/0.10.0.tar.gz"],
+)
+http_archive(
+ name = "build_bazel_rules_swift",
+ sha256 = "6544ff5615febec0342de1127144d2f3e43ea80fb7f9b1ade65e6a184e39e618",
+ strip_prefix = "rules_swift-0.5.0",
+ urls = ["https://github.com/bazelbuild/rules_swift/archive/0.5.0.tar.gz"],
+)
+http_archive(
+ name = "bazel_skylib",
+ sha256 = "eb5c57e4c12e68c0c20bc774bfbc60a568e800d025557bc4ea022c6479acc867",
+ strip_prefix = "bazel-skylib-0.6.0",
+ urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.6.0.tar.gz"],
+)
+http_file(
+ name = "xctestrunner",
+ executable = 1,
+ urls = ["https://github.com/google/xctestrunner/releases/download/0.2.5/ios_test_runner.par"],
+)
+load("@build_bazel_rules_apple//apple:repositories.bzl", "apple_rules_dependencies")
+apple_rules_dependencies(ignore_version_differences = True)
+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")
diff --git a/configure.py b/configure.py
index 57a03bd17fac1a3a9942bdacf4661d021a62bbaa..8dcd31822000820df12c7e96f5c57c68ed605f41 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'
@@ -255,18 +255,6 @@ 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 +476,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!' % min_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
@@ -1491,7 +1482,7 @@ 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 set_system_libs_flag(environ_cp):
@@ -1565,11 +1556,9 @@ def main():
# environment variables.
environ_cp = dict(os.environ)
- check_bazel_version('0.15.0', '0.19.2')
+ check_bazel_version('0.19.0', '0.21.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)
@@ -1705,7 +1694,7 @@ def main():
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..6bc8403d126a58c1eb6499ab7f224e12c6bc5aa4 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
@@ -202,6 +206,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 +277,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 +347,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 +381,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(
@@ -574,13 +601,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 +624,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 +647,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 +661,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 f13623b0d57d3b59bb9455a46a9fab29fee25784..a93799bfe84b0f9c4743e1ad0effd6e69ad7f3f2 100644
--- a/tensorflow/api_template.__init__.py
+++ b/tensorflow/api_template.__init__.py
@@ -18,25 +18,77 @@ 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
+
+# API IMPORTS PLACEHOLDER
# 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'))
-
-# API IMPORTS PLACEHOLDER
+ child_package_str=(
+ 'tensorflow_estimator.python.estimator.api._v2.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._v2.keras'))
# 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__:
+if not hasattr(_current_module, '__path__'):
+ __path__ = [_tf_api_dir]
+elif _tf_api_dir not in __path__:
__path__.append(_tf_api_dir)
-# Calls to enable and disable features.
-enable_eager_execution() # pylint: disable=undefined-variable
+# Enable TF2 behaviors
+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
@@ -58,4 +110,6 @@ try:
del compiler
except NameError:
pass
+
+
# pylint: enable=undefined-variable
diff --git a/tensorflow/api_template_v1.__init__.py b/tensorflow/api_template_v1.__init__.py
index 65bdb6cb1b5e6fb0656a12b932d767aeacfccd29..eeca8f0d566a6401cb64e4fe3f0ee3c5aeb4ece2 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__)) # pylint: disable=undefined-variable
+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 f653e581bf3beda9fdbf8fb7905a4f9fe170e7fb..ef52a28460062b57317b4027ab83479e5e075b5f 100644
--- a/tensorflow/c/BUILD
+++ b/tensorflow/c/BUILD
@@ -83,7 +83,7 @@ tf_cuda_library(
],
"//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",
@@ -123,13 +123,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",
],
)
@@ -175,6 +175,32 @@ tf_cuda_library(
],
)
+tf_cuda_library(
+ name = "env",
+ srcs = [
+ "env.cc",
+ ],
+ hdrs = [
+ "env.h",
+ ],
+ copts = tf_copts(),
+ visibility = ["//visibility:public"],
+ deps = select({
+ "//tensorflow:android": [
+ ":c_api",
+ ":tf_status_helper",
+ "//tensorflow/core:android_tensorflow_lib_lite",
+ "//tensorflow/core:lib",
+ ],
+ "//conditions:default": [
+ ":c_api",
+ ":tf_status_helper",
+ "//tensorflow/core:framework",
+ "//tensorflow/core:lib",
+ ],
+ }) + [":c_api_internal"],
+)
+
tf_cuda_library(
name = "kernels",
srcs = [
@@ -188,10 +214,14 @@ tf_cuda_library(
deps = select({
"//tensorflow:android": [
":c_api",
+ ":c_api_internal",
+ ":tf_status_helper",
"//tensorflow/core:android_tensorflow_lib_lite",
],
"//conditions:default": [
":c_api",
+ ":c_api_internal",
+ ":tf_status_helper",
"//tensorflow/core:framework",
],
}),
@@ -219,6 +249,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",
@@ -247,13 +289,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",
@@ -277,6 +329,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",
@@ -330,6 +383,27 @@ tf_kernel_library(
alwayslink = 1,
)
+tf_cuda_cc_test(
+ name = "env_test",
+ size = "small",
+ srcs = ["env_test.cc"],
+ linkopts = select({
+ "//tensorflow:darwin": ["-headerpad_max_install_names"],
+ "//conditions:default": [],
+ }),
+ tags = ["noasan"],
+ # We must ensure that the dependencies can be dynamically linked since
+ # the shared library must be able to use core:framework.
+ # linkstatic = tf_kernel_tests_linkstatic(),
+ deps = [
+ ":c_api",
+ ":env",
+ "//tensorflow/core:lib",
+ "//tensorflow/core:test",
+ "//tensorflow/core:test_main",
+ ],
+)
+
tf_cuda_cc_test(
name = "kernels_test",
size = "small",
diff --git a/tensorflow/c/c_api.cc b/tensorflow/c/c_api.cc
index f13e8777dff164bcd8eedf46310ae846abd0c804..94d9f4a6fa2f14cb3343bdd51b7e4d61944444d0 100644
--- a/tensorflow/c/c_api.cc
+++ b/tensorflow/c/c_api.cc
@@ -136,16 +136,22 @@ const char* TF_Message(const TF_Status* s) {
namespace {
class TF_ManagedBuffer : public TensorBuffer {
public:
- void* data_;
- size_t len_;
- void (*deallocator_)(void* data, size_t len, void* arg);
- void* deallocator_arg_;
+ TF_ManagedBuffer(void* data, size_t len,
+ void (*deallocator)(void* data, size_t len, void* arg),
+ void* deallocator_arg)
+ : TensorBuffer(data),
+ len_(len),
+ deallocator_(deallocator),
+ deallocator_arg_(deallocator_arg) {}
+
+ const size_t len_;
+ void (*const deallocator_)(void* data, size_t len, void* arg);
+ void* const deallocator_arg_;
~TF_ManagedBuffer() override {
- (*deallocator_)(data_, len_, deallocator_arg_);
+ (*deallocator_)(data(), len_, deallocator_arg_);
}
- void* data() const override { return data_; }
size_t size() const override { return len_; }
TensorBuffer* root_buffer() override { return this; }
void FillAllocationDescription(AllocationDescription* proto) const override {
@@ -199,8 +205,7 @@ TF_Tensor* TF_NewTensor(TF_DataType dtype, const int64_t* dims, int num_dims,
dimvec[i] = static_cast(dims[i]);
}
- TF_ManagedBuffer* buf = new TF_ManagedBuffer;
- buf->len_ = len;
+ TF_ManagedBuffer* buf = nullptr;
if (dtype != TF_STRING && dtype != TF_RESOURCE &&
tensorflow::DataTypeCanUseMemcpy(static_cast(dtype)) &&
reinterpret_cast(data) % std::max(1, EIGEN_MAX_ALIGN_BYTES) !=
@@ -212,17 +217,15 @@ TF_Tensor* TF_NewTensor(TF_DataType dtype, const int64_t* dims, int num_dims,
//
// Other types have the same representation, so copy only if it is safe to
// do so.
- buf->data_ = allocate_tensor("TF_NewTensor", len);
- std::memcpy(buf->data_, data, len);
- buf->deallocator_ = deallocate_buffer;
- buf->deallocator_arg_ = nullptr;
+ buf = new TF_ManagedBuffer(allocate_tensor("TF_NewTensor", len), len,
+ deallocate_buffer, nullptr);
+ std::memcpy(buf->data(), data, len);
// Free the original buffer.
deallocator(data, len, deallocator_arg);
} else {
- buf->data_ = data;
- buf->deallocator_ = deallocator;
- buf->deallocator_arg_ = deallocator_arg;
+ buf = new TF_ManagedBuffer(data, len, deallocator, deallocator_arg);
}
+
TF_Tensor* ret = new TF_Tensor{dtype, TensorShape(dimvec), buf};
size_t elem_size = TF_DataTypeSize(dtype);
if (elem_size > 0 && len < (elem_size * ret->shape.num_elements())) {
@@ -254,6 +257,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) {
@@ -477,14 +548,15 @@ static TF_Tensor* EmptyTensor(TF_DataType dtype, const TensorShape& shape) {
CHECK_EQ(nelems, 0);
static_assert(sizeof(int64_t) == sizeof(tensorflow::int64),
"64-bit int types should match in size");
- return TF_NewTensor(dtype, reinterpret_cast(dims.data()),
- shape.dims(), reinterpret_cast(&empty), 0,
- [](void*, size_t, void*) {}, nullptr);
+ return TF_NewTensor(
+ dtype, reinterpret_cast(dims.data()), shape.dims(),
+ reinterpret_cast(&empty), 0, [](void*, size_t, void*) {}, nullptr);
}
// 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");
@@ -1592,18 +1664,20 @@ TF_AttrMetadata TF_OperationGetAttrMetadata(TF_Operation* oper,
break; \
}
- LIST_CASE(s, TF_ATTR_STRING, metadata.total_size = 0;
- for (int i = 0; i < attr->list().s_size();
- ++i) { metadata.total_size += attr->list().s(i).size(); });
+ LIST_CASE(
+ s, TF_ATTR_STRING, metadata.total_size = 0;
+ for (int i = 0; i < attr->list().s_size();
+ ++i) { metadata.total_size += attr->list().s(i).size(); });
LIST_CASE(i, TF_ATTR_INT);
LIST_CASE(f, TF_ATTR_FLOAT);
LIST_CASE(b, TF_ATTR_BOOL);
LIST_CASE(type, TF_ATTR_TYPE);
- LIST_CASE(shape, TF_ATTR_SHAPE, metadata.total_size = 0;
- for (int i = 0; i < attr->list().shape_size(); ++i) {
- const auto& s = attr->list().shape(i);
- metadata.total_size += s.unknown_rank() ? 0 : s.dim_size();
- });
+ LIST_CASE(
+ shape, TF_ATTR_SHAPE, metadata.total_size = 0;
+ for (int i = 0; i < attr->list().shape_size(); ++i) {
+ const auto& s = attr->list().shape(i);
+ metadata.total_size += s.unknown_rank() ? 0 : s.dim_size();
+ });
LIST_CASE(tensor, TF_ATTR_TENSOR);
LIST_CASE(tensor, TF_ATTR_FUNC);
#undef LIST_CASE
@@ -2875,6 +2949,9 @@ 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
+}
} // end extern "C"
diff --git a/tensorflow/c/c_api.h b/tensorflow/c/c_api.h
index 3d56268110edbe96616201d15a69cc8c84d3115a..8031928dac4de2391f0aec46e69d61a137606e4d 100644
--- a/tensorflow/c/c_api.h
+++ b/tensorflow/c/c_api.h
@@ -91,7 +91,7 @@ extern "C" {
// --------------------------------------------------------------------------
// TF_Version returns a string describing version information of the
// TensorFlow library. TensorFlow using semantic versioning.
-TF_CAPI_EXPORT extern const char* TF_Version();
+TF_CAPI_EXPORT extern const char* TF_Version(void);
// --------------------------------------------------------------------------
// TF_DataType holds the type for a scalar value. E.g., one slot in a tensor.
@@ -157,7 +157,7 @@ typedef enum TF_Code {
typedef struct TF_Status TF_Status;
// Return a new status object.
-TF_CAPI_EXPORT extern TF_Status* TF_NewStatus();
+TF_CAPI_EXPORT extern TF_Status* TF_NewStatus(void);
// Delete a previously created status object.
TF_CAPI_EXPORT extern void TF_DeleteStatus(TF_Status*);
@@ -196,7 +196,7 @@ TF_CAPI_EXPORT extern TF_Buffer* TF_NewBufferFromString(const void* proto,
size_t proto_len);
// Useful for passing *out* a protobuf.
-TF_CAPI_EXPORT extern TF_Buffer* TF_NewBuffer();
+TF_CAPI_EXPORT extern TF_Buffer* TF_NewBuffer(void);
TF_CAPI_EXPORT extern void TF_DeleteBuffer(TF_Buffer*);
@@ -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`
@@ -305,7 +338,7 @@ TF_CAPI_EXPORT extern size_t TF_StringEncodedSize(size_t len);
typedef struct TF_SessionOptions TF_SessionOptions;
// Return a new options object.
-TF_CAPI_EXPORT extern TF_SessionOptions* TF_NewSessionOptions();
+TF_CAPI_EXPORT extern TF_SessionOptions* TF_NewSessionOptions(void);
// Set the target in TF_SessionOptions.options.
// target can be empty, a single entry, or a comma separated list of entries.
@@ -338,7 +371,7 @@ TF_CAPI_EXPORT extern void TF_DeleteSessionOptions(TF_SessionOptions*);
typedef struct TF_Graph TF_Graph;
// Return a new graph object.
-TF_CAPI_EXPORT extern TF_Graph* TF_NewGraph();
+TF_CAPI_EXPORT extern TF_Graph* TF_NewGraph(void);
// Destroy an options object. Graph will be deleted once no more
// TFSession's are referencing it.
@@ -890,7 +923,8 @@ TF_CAPI_EXPORT extern void TF_GraphVersions(TF_Graph* graph,
// TF_GraphImportGraphDef.
typedef struct TF_ImportGraphDefOptions TF_ImportGraphDefOptions;
-TF_CAPI_EXPORT extern TF_ImportGraphDefOptions* TF_NewImportGraphDefOptions();
+TF_CAPI_EXPORT extern TF_ImportGraphDefOptions* TF_NewImportGraphDefOptions(
+ void);
TF_CAPI_EXPORT extern void TF_DeleteImportGraphDefOptions(
TF_ImportGraphDefOptions* opts);
@@ -1611,7 +1645,7 @@ TF_CAPI_EXPORT extern void TF_DeleteLibraryHandle(TF_Library* lib_handle);
//
// The data in the buffer will be the serialized OpList proto for ops registered
// in this address space.
-TF_CAPI_EXPORT extern TF_Buffer* TF_GetAllOpList();
+TF_CAPI_EXPORT extern TF_Buffer* TF_GetAllOpList(void);
// TF_ApiDefMap encapsulates a collection of API definitions for an operation.
//
diff --git a/tensorflow/c/c_api_experimental.cc b/tensorflow/c/c_api_experimental.cc
index 3693cc85996365360253c8a94c29272a16e11e9a..a8325ce494c4f57fcd7e64b2d233ee4e6666bc4e 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.
//
@@ -6530,7 +6542,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/cycle_length"
+ name: "ExperimentalParallelInterleaveDataset/cycle_length"
op: "Const"
attr {
key: "dtype"
@@ -6551,7 +6563,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/block_length"
+ name: "ExperimentalParallelInterleaveDataset/block_length"
op: "Const"
attr {
key: "dtype"
@@ -6572,7 +6584,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/sloppy"
+ name: "ExperimentalParallelInterleaveDataset/sloppy"
op: "Const"
attr {
key: "dtype"
@@ -6593,7 +6605,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/buffer_output_elements"
+ name: "ExperimentalParallelInterleaveDataset/buffer_output_elements"
op: "Const"
attr {
key: "dtype"
@@ -6614,7 +6626,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/prefetch_input_elements"
+ name: "ExperimentalParallelInterleaveDataset/prefetch_input_elements"
op: "Const"
attr {
key: "dtype"
@@ -6635,14 +6647,14 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset"
- op: "ParallelInterleaveDataset"
+ name: "ExperimentalParallelInterleaveDataset"
+ op: "ExperimentalParallelInterleaveDataset"
input: "RepeatDataset:handle:0"
- input: "ParallelInterleaveDataset/cycle_length:output:0"
- input: "ParallelInterleaveDataset/block_length:output:0"
- input: "ParallelInterleaveDataset/sloppy:output:0"
- input: "ParallelInterleaveDataset/buffer_output_elements:output:0"
- input: "ParallelInterleaveDataset/prefetch_input_elements:output:0"
+ input: "ExperimentalParallelInterleaveDataset/cycle_length:output:0"
+ input: "ExperimentalParallelInterleaveDataset/block_length:output:0"
+ input: "ExperimentalParallelInterleaveDataset/sloppy:output:0"
+ input: "ExperimentalParallelInterleaveDataset/buffer_output_elements:output:0"
+ input: "ExperimentalParallelInterleaveDataset/prefetch_input_elements:output:0"
attr {
key: "Targuments"
value {
@@ -6742,7 +6754,7 @@ library {
node_def {
name: "ShuffleDataset_2"
op: "ShuffleDataset"
- input: "ParallelInterleaveDataset:handle:0"
+ input: "ExperimentalParallelInterleaveDataset:handle:0"
input: "ShuffleDataset_2/buffer_size_1:output:0"
input: "ShuffleDataset_2/seed_2:output:0"
input: "ShuffleDataset_2/seed2_2:output:0"
@@ -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,217 @@ 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());
+ for (auto* input : op->operation.Inputs()) {
+ auto symbolic_input = getOrCreateSymbolicTensor(trace_ctx, input, status);
+ if (!status->status.ok()) return nullptr;
+ TF_AddInput(desc, symbolic_input);
+ }
+
+ 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);
+ }
+
+ 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 80c8bfe594c4c89606efd01bec7f50e7a86b5bda..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
@@ -239,13 +251,69 @@ TF_CAPI_EXPORT void TF_InitMain(const char* usage, int* argc, char*** argv);
// Platform-specific implementation to return an unused port. (This should used
// in tests only.)
-TF_CAPI_EXPORT int TF_PickUnusedPortOrDie();
+TF_CAPI_EXPORT int TF_PickUnusedPortOrDie(void);
// Fast path method that makes constructing a single scalar tensor require less
// overhead and copies.
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..354ee5f49f373edbc10e7706aa8776f3cc2a17cd 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,154 @@ 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);
+}
+
} // namespace
} // namespace tensorflow
diff --git a/tensorflow/c/c_api_internal.h b/tensorflow/c/c_api_internal.h
index 5ba26d3c585350aa510f9970cbfc246a9a108543..73283d775639b297857b2a50007dc7c28b1f39a3 100644
--- a/tensorflow/c/c_api_internal.h
+++ b/tensorflow/c/c_api_internal.h
@@ -228,6 +228,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..b86d8eb8e300e02a3871ecd5f424a82c521b18fc
--- /dev/null
+++ b/tensorflow/c/c_test.c
@@ -0,0 +1,88 @@
+/* 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 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_DataType type;
+ TF_OpKernelContext_GetAttrType(ctx, "foobar", &type, s);
+
+ 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", NULL, &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..257be6379c09841d1427813a0aa25b10a205016d 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: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/contrib/tpu/profiler:trace_events_proto_cc",
+ "//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..af13f487af91594fedd4d5f77592682a6f98c34f 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,6 +757,10 @@ 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);
}
@@ -774,7 +797,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 8d6c8d958d5961fce817156a14eb2b2940c1f2f0..044dfb7415b027b707af05a197fdb41fe1f6d2e5 100755
--- a/tensorflow/c/eager/c_api.h
+++ b/tensorflow/c/eager/c_api.h
@@ -48,7 +48,7 @@ extern "C" {
typedef struct TFE_ContextOptions TFE_ContextOptions;
// Return a new options object.
-TF_CAPI_EXPORT extern TFE_ContextOptions* TFE_NewContextOptions();
+TF_CAPI_EXPORT extern TFE_ContextOptions* TFE_NewContextOptions(void);
// Set the config in TF_ContextOptions.options.
// config should be a serialized tensorflow.ConfigProto proto.
@@ -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..dab17505643e791e6294a64247898ae23769a055
--- /dev/null
+++ b/tensorflow/c/eager/c_api_experimental.cc
@@ -0,0 +1,52 @@
+/* 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/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_Context* ctx) {
+ return new TFE_Profiler(ctx);
+}
+
+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);
+ };
+}
+
+void TFE_StartProfilerServer(TFE_Context* ctx, int port) {
+ auto server_thread = tensorflow::StartProfilerServer(&ctx->context, port);
+ ctx->context.AddChildThread(std::move(server_thread));
+}
diff --git a/tensorflow/c/eager/c_api_experimental.h b/tensorflow/c/eager/c_api_experimental.h
new file mode 100644
index 0000000000000000000000000000000000000000..8c85d0e51695fde09cf0e2bb3930f9173e6cfb54
--- /dev/null
+++ b/tensorflow/c/eager/c_api_experimental.h
@@ -0,0 +1,58 @@
+/* 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);
+
+// 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_Context* ctx);
+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);
+
+// Start a profiler grpc server which listens to specified port. It will start
+// the server on its own thread. It can be shutdown by destructing TFE_Context.
+// 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_Context* ctx, int port);
+
+#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..af55fee66e8708e39626da3b10b6dd2f73af92bb
--- /dev/null
+++ b/tensorflow/c/eager/c_api_experimental_test.cc
@@ -0,0 +1,104 @@
+/* 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/contrib/tpu/profiler/trace_events.pb.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"
+
+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_Profiler* profiler = TFE_NewProfiler(ctx);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_DeleteContextOptions(opts);
+
+ 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);
+ tensorflow::tpu::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); }
+
+} // namespace
+} // namespace tensorflow
diff --git a/tensorflow/c/eager/c_api_internal.h b/tensorflow/c/eager/c_api_internal.h
index 67bc1bcd24605f8363d6a7c8d5d6a0836a42fc82..b70c0f1c112c675641a023d6c7bf4fa847ee4610 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,13 @@ struct TFE_Op {
tensorflow::EagerOperation operation;
};
+struct TFE_Profiler {
+ TFE_Profiler(TFE_Context* ctx)
+ : profiler(tensorflow::ProfilerSession::Create(&ctx->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 +121,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
new file mode 100644
index 0000000000000000000000000000000000000000..1c35ff9001d0ee1ab0fbae9e1bcc07116fab1065
--- /dev/null
+++ b/tensorflow/c/env.cc
@@ -0,0 +1,183 @@
+/* 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/env.h"
+
+#include "tensorflow/c/c_api_internal.h"
+#include "tensorflow/c/tf_status_helper.h"
+#include "tensorflow/core/platform/env.h"
+#include "tensorflow/core/platform/types.h"
+
+struct TF_StringStream {
+ std::vector<::tensorflow::string>* list;
+ size_t position;
+};
+
+void TF_CreateDir(const char* dirname, TF_Status* status) {
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(
+ status, ::tensorflow::Env::Default()->CreateDir(dirname));
+}
+
+void TF_DeleteDir(const char* dirname, TF_Status* status) {
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(
+ status, ::tensorflow::Env::Default()->DeleteDir(dirname));
+}
+
+void TF_DeleteRecursively(const char* dirname, int64_t* undeleted_file_count,
+ int64_t* undeleted_dir_count, TF_Status* status) {
+ ::tensorflow::int64 f, d;
+
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(
+ status, ::tensorflow::Env::Default()->DeleteRecursively(dirname, &f, &d));
+ *undeleted_file_count = f;
+ *undeleted_dir_count = d;
+}
+
+void TF_FileStat(const char* filename, TF_FileStatistics* stats,
+ TF_Status* status) {
+ ::tensorflow::FileStatistics cc_stats;
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Status s =
+ ::tensorflow::Env::Default()->Stat(filename, &cc_stats);
+ ::tensorflow::Set_TF_Status_from_Status(status, s);
+ if (s.ok()) {
+ stats->length = cc_stats.length;
+ stats->mtime_nsec = cc_stats.mtime_nsec;
+ stats->is_directory = cc_stats.is_directory;
+ }
+}
+
+void TF_NewWritableFile(const char* filename, TF_WritableFileHandle** handle,
+ TF_Status* status) {
+ std::unique_ptr<::tensorflow::WritableFile> f;
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Status s =
+ ::tensorflow::Env::Default()->NewWritableFile(filename, &f);
+ ::tensorflow::Set_TF_Status_from_Status(status, s);
+
+ if (s.ok()) {
+ *handle = reinterpret_cast(f.release());
+ }
+}
+
+void TF_CloseWritableFile(TF_WritableFileHandle* handle, TF_Status* status) {
+ auto* cc_file = reinterpret_cast<::tensorflow::WritableFile*>(handle);
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(status, cc_file->Close());
+ delete cc_file;
+}
+
+void TF_SyncWritableFile(TF_WritableFileHandle* handle, TF_Status* status) {
+ auto* cc_file = reinterpret_cast<::tensorflow::WritableFile*>(handle);
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(status, cc_file->Sync());
+}
+
+void TF_FlushWritableFile(TF_WritableFileHandle* handle, TF_Status* status) {
+ auto* cc_file = reinterpret_cast<::tensorflow::WritableFile*>(handle);
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(status, cc_file->Flush());
+}
+
+void TF_AppendWritableFile(TF_WritableFileHandle* handle, const char* data,
+ size_t length, TF_Status* status) {
+ auto* cc_file = reinterpret_cast<::tensorflow::WritableFile*>(handle);
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(
+ status, cc_file->Append(::tensorflow::StringPiece{data, length}));
+}
+
+void TF_DeleteFile(const char* filename, TF_Status* status) {
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(
+ status, ::tensorflow::Env::Default()->DeleteFile(filename));
+}
+
+bool TF_StringStreamNext(TF_StringStream* list, const char** result) {
+ if (list->position >= list->list->size()) {
+ *result = nullptr;
+ return false;
+ }
+
+ *result = list->list->at(list->position++).c_str();
+ return true;
+}
+
+void TF_StringStreamDone(TF_StringStream* list) {
+ delete list->list;
+ delete list;
+}
+TF_StringStream* TF_GetChildren(const char* dirname, TF_Status* status) {
+ auto* children = new std::vector<::tensorflow::string>;
+
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(
+ status, ::tensorflow::Env::Default()->GetChildren(dirname, children));
+
+ auto* list = new TF_StringStream;
+ list->list = children;
+ list->position = 0;
+ return list;
+}
+
+TF_StringStream* TF_GetLocalTempDirectories() {
+ auto* tmpdirs = new std::vector<::tensorflow::string>;
+
+ ::tensorflow::Env::Default()->GetLocalTempDirectories(tmpdirs);
+
+ auto* list = new TF_StringStream;
+ list->list = tmpdirs;
+ list->position = 0;
+ return list;
+}
+
+TF_CAPI_EXPORT extern uint64_t TF_NowNanos(void) {
+ return ::tensorflow::Env::Default()->NowNanos();
+}
+
+// Returns the number of microseconds since the Unix epoch.
+TF_CAPI_EXPORT extern uint64_t TF_NowMicros(void) {
+ return ::tensorflow::Env::Default()->NowMicros();
+}
+
+// Returns the number of seconds since the Unix epoch.
+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
new file mode 100644
index 0000000000000000000000000000000000000000..73078fcbbc5ae4c042f4a992655072a838e42915
--- /dev/null
+++ b/tensorflow/c/env.h
@@ -0,0 +1,195 @@
+/* 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
+
+#ifndef TENSORFLOW_C_ENV_H_
+#define TENSORFLOW_C_ENV_H_
+
+#include "tensorflow/c/c_api.h"
+
+// --------------------------------------------------------------------------
+// C API for tensorflow::Env.
+
+#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;
+ // The last modified time in nanoseconds.
+ int64_t mtime_nsec;
+ // Whether the name refers to a directory.
+ 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
+// * TF_PERMISSION_DENIED - dirname is not writable
+TF_CAPI_EXPORT extern void TF_CreateDir(const char* dirname, TF_Status* status);
+
+// Deletes the specified directory. Typical status codes are:
+// * TF_OK - successfully deleted the directory
+// * TF_FAILED_PRECONDITION - the directory is not empty
+TF_CAPI_EXPORT extern void TF_DeleteDir(const char* dirname, TF_Status* status);
+
+// Deletes the specified directory and all subdirectories and files underneath
+// it. This is accomplished by traversing the directory tree rooted at dirname
+// and deleting entries as they are encountered.
+//
+// If dirname itself is not readable or does not exist, *undeleted_dir_count is
+// set to 1, *undeleted_file_count is set to 0 and an appropriate status (e.g.
+// TF_NOT_FOUND) is returned.
+//
+// If dirname and all its descendants were successfully deleted, TF_OK is
+// returned and both error counters are set to zero.
+//
+// Otherwise, while traversing the tree, undeleted_file_count and
+// undeleted_dir_count are updated if an entry of the corresponding type could
+// not be deleted. The returned error status represents the reason that any one
+// of these entries could not be deleted.
+//
+// Typical status codes:
+// * TF_OK - dirname exists and we were able to delete everything underneath
+// * TF_NOT_FOUND - dirname doesn't exist
+// * TF_PERMISSION_DENIED - dirname or some descendant is not writable
+// * TF_UNIMPLEMENTED - some underlying functions (like Delete) are not
+// implemented
+TF_CAPI_EXPORT extern void TF_DeleteRecursively(const char* dirname,
+ int64_t* undeleted_file_count,
+ int64_t* undeleted_dir_count,
+ TF_Status* status);
+
+// Obtains statistics for the given path. If status is TF_OK, *stats is
+// updated, otherwise it is not touched.
+TF_CAPI_EXPORT extern void TF_FileStat(const char* filename,
+ TF_FileStatistics* stats,
+ TF_Status* status);
+
+// Creates or truncates the given filename and returns a handle to be used for
+// appending data to the file. If status is TF_OK, *handle is updated and the
+// caller is responsible for freeing it (see TF_CloseWritableFile).
+TF_CAPI_EXPORT extern void TF_NewWritableFile(const char* filename,
+ TF_WritableFileHandle** handle,
+ TF_Status* status);
+
+// Closes the given handle and frees its memory. If there was a problem closing
+// the file, it is indicated by status. Memory is freed in any case.
+TF_CAPI_EXPORT extern void TF_CloseWritableFile(TF_WritableFileHandle* handle,
+ TF_Status* status);
+
+// Syncs content of the handle to the filesystem. Blocks waiting for the
+// filesystem to indicate that the content has been persisted.
+TF_CAPI_EXPORT extern void TF_SyncWritableFile(TF_WritableFileHandle* handle,
+ TF_Status* status);
+
+// Flush local buffers to the filesystem. If the process terminates after a
+// successful flush, the contents may still be persisted, since the underlying
+// filesystem may eventually flush the contents. If the OS or machine crashes
+// after a successful flush, the contents may or may not be persisted, depending
+// on the implementation.
+TF_CAPI_EXPORT extern void TF_FlushWritableFile(TF_WritableFileHandle* handle,
+ TF_Status* status);
+
+// Appends the given bytes to the file. Any failure to do so is indicated in
+// status.
+TF_CAPI_EXPORT extern void TF_AppendWritableFile(TF_WritableFileHandle* handle,
+ const char* data,
+ size_t length,
+ TF_Status* status);
+
+// Deletes the named file and indicates whether successful in *status.
+TF_CAPI_EXPORT extern void TF_DeleteFile(const char* filename,
+ TF_Status* status);
+
+// Retrieves the next item from the given TF_StringStream and places a pointer
+// to it in *result. If no more items are in the list, *result is set to NULL
+// and false is returned.
+//
+// Ownership of the items retrieved with this function remains with the library.
+// Item points are invalidated after a call to TF_StringStreamDone.
+TF_CAPI_EXPORT extern bool TF_StringStreamNext(TF_StringStream* list,
+ const char** result);
+
+// Frees the resources associated with given string list. All pointers returned
+// by TF_StringStreamNext are invalid after this call.
+TF_CAPI_EXPORT extern void TF_StringStreamDone(TF_StringStream* list);
+
+// Retrieves the list of children of the given directory. You can iterate
+// through the list with TF_StringStreamNext. The caller is responsible for
+// freeing the list (see TF_StringStreamDone).
+TF_CAPI_EXPORT extern TF_StringStream* TF_GetChildren(const char* filename,
+ TF_Status* status);
+
+// Retrieves a list of directory names on the local machine that may be used for
+// temporary storage. You can iterate through the list with TF_StringStreamNext.
+// The caller is responsible for freeing the list (see TF_StringStreamDone).
+TF_CAPI_EXPORT extern TF_StringStream* TF_GetLocalTempDirectories(void);
+
+// Returns the number of nanoseconds since the Unix epoch.
+TF_CAPI_EXPORT extern uint64_t TF_NowNanos(void);
+
+// Returns the number of microseconds since the Unix epoch.
+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
+
+#endif // TENSORFLOW_C_ENV_H_
diff --git a/tensorflow/c/env_test.cc b/tensorflow/c/env_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..687ad024137352662759ec1f43df87e89faca353
--- /dev/null
+++ b/tensorflow/c/env_test.cc
@@ -0,0 +1,127 @@
+/* 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/env.h"
+
+#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"
+
+#define ASSERT_TF_OK(x) ASSERT_EQ(TF_OK, TF_GetCode(x))
+
+TEST(TestEnv, TestDirHandling) {
+ TF_StringStream* tempdirs = TF_GetLocalTempDirectories();
+ const char* tempdir;
+ bool found = false;
+ while (TF_StringStreamNext(tempdirs, &tempdir)) {
+ found = true;
+
+ TF_Status* s = TF_NewStatus();
+
+ ::tensorflow::string dirpath =
+ ::tensorflow::io::JoinPath(tempdir, "somedir");
+ TF_CreateDir(dirpath.c_str(), s);
+ ASSERT_TF_OK(s) << "TF_CreateDir failed for " << dirpath << ": "
+ << TF_Message(s);
+
+ ::tensorflow::string filepath =
+ ::tensorflow::io::JoinPath(dirpath, "somefile.txt");
+ TF_WritableFileHandle* handle;
+ TF_NewWritableFile(filepath.c_str(), &handle, s);
+ ASSERT_TF_OK(s) << "NewWritableFile failed for " << filepath << ": "
+ << TF_Message(s);
+
+ const char* data = "Hello, world!\n";
+ TF_AppendWritableFile(handle, data, strlen(data), s);
+ ASSERT_TF_OK(s) << "TF_AppendWritableFile failed to append data to file at "
+ << filepath << ": " << TF_Message(s);
+
+ TF_CloseWritableFile(handle, s);
+ ASSERT_TF_OK(s) << "TF_CloseWritableFile failed to close handle to "
+ << filepath << ": " << TF_Message(s);
+
+ TF_StringStream* children = TF_GetChildren(dirpath.c_str(), s);
+ ASSERT_TF_OK(s) << "TF_GetChildren failed for " << dirpath;
+ const char* childpath;
+ ASSERT_TRUE(TF_StringStreamNext(children, &childpath));
+ ASSERT_EQ(::tensorflow::string(childpath), "somefile.txt");
+ // There should only be one file in this directory.
+ ASSERT_FALSE(TF_StringStreamNext(children, &childpath));
+ ASSERT_EQ(childpath, nullptr);
+ TF_StringStreamDone(children);
+
+ TF_FileStatistics stats;
+ TF_FileStat(filepath.c_str(), &stats, s);
+ ASSERT_EQ(stats.length, strlen(data));
+ ASSERT_FALSE(stats.is_directory);
+ ASSERT_GT(stats.mtime_nsec, 0);
+
+ // Trying to delete a non-empty directory should fail.
+ TF_DeleteDir(dirpath.c_str(), s);
+ ASSERT_NE(TF_OK, TF_GetCode(s))
+ << "TF_DeleteDir unexpectedly succeeded with a non-empty directory "
+ << dirpath;
+
+ TF_DeleteFile(filepath.c_str(), s);
+ ASSERT_TF_OK(s) << "TF_DeleteFile failed for " << filepath << ": "
+ << TF_Message(s);
+
+ // Now deleting the directory should work.
+ TF_DeleteDir(dirpath.c_str(), s);
+ ASSERT_TF_OK(s) << "TF_DeleteDir failed for " << dirpath << ": "
+ << TF_Message(s);
+
+ TF_DeleteStatus(s);
+ break;
+ }
+
+ ASSERT_TRUE(found) << "expected at least one temp dir";
+
+ TF_StringStreamDone(tempdirs);
+}
+
+TEST(TestEnv, TestTimeFunctions) {
+ ASSERT_GE(TF_NowSeconds(), 946684800); // Midnight Jan 1, 2000
+ 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 ca69345264607ac689fb556b4f5c9bc08ea5eb88..9505bf9dda32b9a338b574f1d31ec555a5628c6a 100644
--- a/tensorflow/c/kernels.cc
+++ b/tensorflow/c/kernels.cc
@@ -15,7 +15,9 @@ limitations under the License.
#include
+#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/kernels.h"
+#include "tensorflow/c/tf_status_helper.h"
#include "tensorflow/core/framework/kernel_def_builder.h"
#include "tensorflow/core/framework/op_kernel.h"
@@ -46,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 {
@@ -116,3 +119,84 @@ void TF_RegisterKernelBuilder(const char* name, TF_KernelBuilder* builder,
TF_SetStatus(status, TF_OK, "");
}
+
+int TF_NumInputs(TF_OpKernelContext* ctx) {
+ auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx);
+ return cc_ctx->num_inputs();
+}
+
+int TF_NumOutputs(TF_OpKernelContext* ctx) {
+ auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx);
+ return cc_ctx->num_outputs();
+}
+
+void TF_GetInput(TF_OpKernelContext* ctx, int i, TF_Tensor** tensor,
+ TF_Status* status) {
+ auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx);
+ if (i < 0 || i >= cc_ctx->num_inputs()) {
+ TF_SetStatus(status, TF_OUT_OF_RANGE, "input index out of range");
+ return;
+ }
+ const ::tensorflow::Tensor& cc_tensor(cc_ctx->input(i));
+ TF_Tensor* result = ::tensorflow::TF_TensorFromTensor(cc_tensor, status);
+ if (TF_GetCode(status) == TF_OK) {
+ *tensor = result;
+ }
+}
+
+void TF_SetOutput(TF_OpKernelContext* ctx, int i, const TF_Tensor* tensor,
+ TF_Status* status) {
+ auto* cc_ctx = reinterpret_cast<::tensorflow::OpKernelContext*>(ctx);
+ if (i < 0 || i >= cc_ctx->num_inputs()) {
+ TF_SetStatus(status, TF_OUT_OF_RANGE, "input index out of range");
+ return;
+ }
+ ::tensorflow::Tensor cc_tensor;
+ ::tensorflow::Status s = ::tensorflow::TF_TensorToTensor(tensor, &cc_tensor);
+ TF_SetStatus(status, TF_OK, "");
+ ::tensorflow::Set_TF_Status_from_Status(status, s);
+ if (s.ok()) {
+ 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_(struct_name, func, c_type, cc_type) \
+ void struct_name##_GetAttr##func(struct_name* 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 DEFINE_TF_GETATTR(func, c_type, cc_type) \
+ DEFINE_TF_GETATTR_(TF_OpKernelConstruction, func, c_type, cc_type) \
+ DEFINE_TF_GETATTR_(TF_OpKernelContext, func, c_type, cc_type)
+
+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 2518789a3c141755d0b3373d53642c487331f68b..b015d0103969355e8566242bfcc007f697c6ae18 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.
//
@@ -85,6 +85,67 @@ TF_CAPI_EXPORT extern void TF_RegisterKernelBuilder(const char* kernel_name,
// builder is not registered with TensorFlow via TF_RegisterKernelBuilder.
TF_CAPI_EXPORT extern void TF_DeleteKernelBuilder(TF_KernelBuilder* builder);
+// --------------------------------------------------------------------------
+// OpKernelContext routines
+
+// TF_NumInputs returns the number of inputs available in ctx.
+TF_CAPI_EXPORT extern int TF_NumInputs(TF_OpKernelContext* ctx);
+
+// TF_NumOutputs returns the number of outputs to be placed in *ctx by the
+// kernel.
+TF_CAPI_EXPORT extern int TF_NumOutputs(TF_OpKernelContext* ctx);
+
+// Retrieves the ith input from ctx. If TF_GetCode(status) is TF_OK, *tensor is
+// populated and its ownership is passed to the caller. In any other case,
+// *tensor is not modified.
+//
+// If i < 0 or i >= TF_NumInputs(ctx), *status is set to TF_OUT_OF_RANGE.
+TF_CAPI_EXPORT extern void TF_GetInput(TF_OpKernelContext* ctx, int i,
+ TF_Tensor** tensor, TF_Status* status);
+
+// Sets the ith output of ctx to tensor. If TF_GetCode(status) is anything but
+// TF_OK, ctx is left unmodified.
+//
+// If i < 0 or i >= TF_NumOutputs(ctx), *status is set to TF_OUT_OF_RANGE.
+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);
+
+// Interprets the named kernel context 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_OpKernelContext_GetAttrType(
+ TF_OpKernelContext* 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 e706c7c1d96ee1781d8efc0f28c5e0cbcbc80861..0d2954717e7a83c102a35815809a554e3a917e07 100644
--- a/tensorflow/c/kernels_test.cc
+++ b/tensorflow/c/kernels_test.cc
@@ -15,6 +15,8 @@ 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"
@@ -31,7 +33,6 @@ struct MyCustomKernel {
static bool delete_called = false;
static void* MyCreateFunc(TF_OpKernelConstruction* ctx) {
- LOG(INFO) << "Wow, actually got into creation";
struct MyCustomKernel* s = new struct MyCustomKernel;
s->created = true;
s->compute_called = false;
@@ -41,6 +42,19 @@ static void* MyCreateFunc(TF_OpKernelConstruction* ctx) {
static void MyComputeFunc(void* kernel, TF_OpKernelContext* ctx) {
struct MyCustomKernel* s = static_cast(kernel);
s->compute_called = true;
+ if (ctx != nullptr) {
+ TF_Status* status = TF_NewStatus();
+
+ EXPECT_EQ(43, TF_StepId(ctx));
+
+ // Exercise attribute reads.
+ TF_DataType type;
+ TF_OpKernelContext_GetAttrType(ctx, "SomeDataTypeAttr", &type, status);
+ EXPECT_EQ(TF_OK, TF_GetCode(status));
+ EXPECT_EQ(TF_FLOAT, type);
+
+ TF_DeleteStatus(status);
+ }
}
static void MyDeleteFunc(void* kernel) {
@@ -51,12 +65,37 @@ static void MyDeleteFunc(void* kernel) {
delete s;
}
+namespace tensorflow {
+
+static std::unique_ptr GetFakeKernel(const char* device_name,
+ const char* op_name,
+ Status* status) {
+ NodeDef def;
+ def.set_op(op_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);
+}
+
// Tests registration of a single C kernel and checks that calls through the
// C/C++ boundary are being made.
TEST(TestKernel, TestRegisterKernelBuilder) {
const char* kernel_name = "SomeKernelName";
const char* op_name = "FooOp";
- const char* device_name = "barDev";
+ const char* device_name = "FakeDeviceName1";
+
+ REGISTER_OP(op_name)
+ .Input("input1: double")
+ .Input("input2: uint8")
+ .Output("output1: uint8")
+ .Attr("SomeDataTypeAttr: type");
TF_KernelBuilder* builder = TF_NewKernelBuilder(
op_name, device_name, &MyCreateFunc, &MyComputeFunc, &MyDeleteFunc);
@@ -65,35 +104,128 @@ TEST(TestKernel, TestRegisterKernelBuilder) {
TF_Status* status = TF_NewStatus();
TF_RegisterKernelBuilder(kernel_name, builder, status);
EXPECT_EQ(TF_OK, TF_GetCode(status));
- TF_Buffer* buf = TF_GetRegisteredKernelsForOp("FooOp", status);
+ TF_Buffer* buf = TF_GetRegisteredKernelsForOp(op_name, status);
EXPECT_EQ(TF_OK, TF_GetCode(status));
- ::tensorflow::KernelList list;
+ KernelList list;
list.ParseFromArray(buf->data, buf->length);
ASSERT_EQ(1, list.kernel_size());
- ASSERT_EQ("barDev", list.kernel(0).device_type());
+ ASSERT_EQ(device_name, list.kernel(0).device_type());
TF_DeleteBuffer(buf);
TF_DeleteStatus(status);
}
- REGISTER_OP("FooOp")
+ {
+ Status status;
+ std::unique_ptr kernel =
+ GetFakeKernel(device_name, op_name, &status);
+ TF_EXPECT_OK(status);
+ ASSERT_NE(nullptr, kernel.get());
+ kernel->Compute(nullptr);
+ }
+
+ ASSERT_TRUE(delete_called);
+}
+
+class DummyDevice : public DeviceBase {
+ public:
+ DummyDevice(Env* env, bool save) : DeviceBase(env), save_(save) {}
+ bool RequiresRecordingAccessedTensors() const override { return save_; }
+ Allocator* GetAllocator(AllocatorAttributes /*attr*/) override {
+ return cpu_allocator();
+ }
+
+ private:
+ bool save_;
+};
+
+TEST(TestKernel, TestInputAndOutputCount) {
+ const char* kernel_name = "InputOutputCounterKernel";
+ const char* op_name = "BarOp";
+ const char* device_name = "FakeDeviceName2";
+
+ 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;
+
+ // A kernel whose Compute function has a side-effect of updating num_inputs
+ // and num_outputs. Various functions on TF_OpKernelContext are also
+ // exercised.
+ auto my_compute_func = [](void* kernel, TF_OpKernelContext* ctx) {
+ num_inputs = TF_NumInputs(ctx);
+ num_outputs = TF_NumOutputs(ctx);
+
+ TF_Tensor* input = nullptr;
+ TF_Status* s = TF_NewStatus();
+ TF_GetInput(ctx, 0, &input, s);
+ EXPECT_EQ(TF_OK, TF_GetCode(s)) << "Failed to get input: " << TF_Message(s);
+ EXPECT_EQ(123, *static_cast(TF_TensorData(input)));
+ TF_GetInput(ctx, -1, &input, s);
+ EXPECT_EQ(TF_OUT_OF_RANGE, TF_GetCode(s));
+ TF_GetInput(ctx, 3, &input, s);
+ EXPECT_EQ(TF_OUT_OF_RANGE, TF_GetCode(s));
+
+ // Copy the input tensor to output.
+ TF_SetOutput(ctx, 0, input, s);
+ EXPECT_EQ(TF_OK, TF_GetCode(s));
+
+ 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);
+ }
+ };
+
+ TF_KernelBuilder* builder = TF_NewKernelBuilder(op_name, device_name, nullptr,
+ my_compute_func, nullptr);
{
- ::tensorflow::NodeDef def;
- def.set_op("FooOp");
- def.set_device("bar");
- def.add_input("input1");
- def.add_input("input2");
- ::tensorflow::Status status;
- std::unique_ptr<::tensorflow::OpKernel> kernel =
- ::tensorflow::CreateOpKernel(::tensorflow::DeviceType("barDev"),
- nullptr, nullptr, def, 1, &status);
+ TF_Status* status = TF_NewStatus();
+ TF_RegisterKernelBuilder(kernel_name, builder, status);
+ EXPECT_EQ(TF_OK, TF_GetCode(status));
+ TF_DeleteStatus(status);
+ }
+
+ {
+ OpKernelContext::Params p;
+ DummyDevice dummy_device(nullptr, false);
+ p.device = &dummy_device;
+ p.step_id = 43;
+
+ Tensor t(tensorflow::uint8(123));
+
+ gtl::InlinedVector inputs;
+ // Simulate 2 inputs
+ inputs.emplace_back(&t);
+ inputs.emplace_back();
+ p.inputs = &inputs;
+
+ Status status;
+ std::unique_ptr kernel =
+ GetFakeKernel(device_name, op_name, &status);
TF_EXPECT_OK(status);
ASSERT_NE(nullptr, kernel.get());
- kernel->Compute(nullptr);
+
+ p.op_kernel = kernel.get();
+ OpKernelContext ctx(&p);
+ kernel->Compute(&ctx);
+
+ ASSERT_EQ(2, num_inputs);
+ ASSERT_EQ(1, num_outputs);
+ ASSERT_EQ(123, ctx.mutable_output(0)->scalar()());
}
+}
- ASSERT_TRUE(delete_called);
+TEST(TestKernel, DeleteKernelBuilderIsOkOnNull) {
+ TF_DeleteKernelBuilder(nullptr);
}
+
+} // namespace tensorflow
diff --git a/tensorflow/c/python_api.cc b/tensorflow/c/python_api.cc
index 247236b760dd8c07bbb08426100b6a4d34296d2e..98d8393332269ae349cf8aa5c0b612c6f17172e6 100644
--- a/tensorflow/c/python_api.cc
+++ b/tensorflow/c/python_api.cc
@@ -160,4 +160,17 @@ void SetHandleShapeAndType(TF_Graph* graph, TF_Output output, const void* proto,
ic->set_output_handle_shapes_and_types(output.index, shapes_and_types);
}
+void AddWhileInputHack(TF_Graph* graph, TF_Output new_src, TF_Operation* dst,
+ TF_Status* status) {
+ mutex_lock l(graph->mu);
+ status->status = graph->graph.AddWhileInputHack(&new_src.oper->node,
+ new_src.index, &dst->node);
+ if (status->status.ok()) {
+ // This modification only updates the destination node for
+ // the purposes of running this graph in a session. Thus, we don't
+ // record the source node as being modified.
+ RecordMutation(graph, *dst, "adding input tensor");
+ }
+}
+
} // namespace tensorflow
diff --git a/tensorflow/c/python_api.h b/tensorflow/c/python_api.h
index 5cce84020bc68d912d259f51512341eb5f464a2c..44779ca656165dd65590cb5e9ea3ccf71165ed63 100644
--- a/tensorflow/c/python_api.h
+++ b/tensorflow/c/python_api.h
@@ -34,6 +34,7 @@ void SetAttr(TF_Graph* graph, TF_Operation* op, const char* attr_name,
void SetRequestedDevice(TF_Graph* graph, TF_Operation* op, const char* device);
+// Updates 'dst' to consume 'new_src'.
void UpdateEdge(TF_Graph* graph, TF_Output new_src, TF_Input dst,
TF_Status* status);
@@ -65,6 +66,13 @@ std::string GetHandleShapeAndType(TF_Graph* graph, TF_Output output);
// because I couldn't get SWIG to work otherwise.
void SetHandleShapeAndType(TF_Graph* graph, TF_Output output, const void* proto,
size_t proto_len, TF_Status* status);
+
+// This method is used to add a new input edge to 'dst', which must be a While
+// op. The While op's "T" attribute must have already been updated to include
+// the new edge. This is used to construct tf.while_loop gradients.
+void AddWhileInputHack(TF_Graph* graph, TF_Output new_src, TF_Operation* dst,
+ TF_Status* status);
+
} // namespace tensorflow
#endif // TENSORFLOW_C_PYTHON_API_H_
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