diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000000000000000000000000000000000000..c70c57136102b483a4332ca22f775d7a2c5b849e
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,104 @@
+# Android configs. Bazel needs to have --cpu and --fat_apk_cpu both set to the
+# target CPU to build transient dependencies correctly. See
+# https://docs.bazel.build/versions/master/user-manual.html#flag--fat_apk_cpu
+build:android --crosstool_top=//external:android/crosstool
+build:android --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
+build:android_arm --config=android
+build:android_arm --cpu=armeabi-v7a
+build:android_arm --fat_apk_cpu=armeabi-v7a
+build:android_arm64 --config=android
+build:android_arm64 --cpu=arm64-v8a
+build:android_arm64 --fat_apk_cpu=arm64-v8a
+
+# Config to use a mostly-static build and disable modular op registration
+# support (this will revert to loading TensorFlow with RTLD_GLOBAL in Python).
+# By default, TensorFlow will build with a dependence on
+# //tensorflow:libtensorflow_framework.so.
+build:monolithic --define framework_shared_object=false
+
+# For projects which use TensorFlow as part of a Bazel build process, putting
+# nothing in a bazelrc will default to a monolithic build. The following line
+# opts in to modular op registration support by default.
+build --define framework_shared_object=true
+
+# Please note that MKL on MacOS or windows is still not supported.
+# 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
+# Instruct clang to use LLD for linking.
+# This only works with GPU builds currently, since Bazel sets -B/usr/bin in
+# auto-generated CPU crosstool, forcing /usr/bin/ld.lld to be preferred over
+# the downloaded one.
+build:download_clang_use_lld --linkopt='-fuse-ld=lld'
+
+build:cuda --crosstool_top=@local_config_cuda//crosstool:toolchain
+build:cuda --define=using_cuda=true --define=using_cuda_nvcc=true
+
+build:rocm --crosstool_top=@local_config_rocm//crosstool:toolchain
+build:rocm --define=using_rocm=true --define=using_rocm_hipcc=true
+
+build:cuda_clang --crosstool_top=@local_config_cuda//crosstool:toolchain
+build:cuda_clang --define=using_cuda=true --define=using_cuda_clang=true --define=using_clang=true
+
+build:sycl --crosstool_top=@local_config_sycl//crosstool:toolchain
+build:sycl --define=using_sycl=true --define=using_trisycl=false
+
+build:sycl_nodouble --crosstool_top=@local_config_sycl//crosstool:toolchain
+build:sycl_nodouble --define=using_sycl=true --cxxopt -DTENSORFLOW_SYCL_NO_DOUBLE
+
+build:sycl_asan --crosstool_top=@local_config_sycl//crosstool:toolchain
+build:sycl_asan --define=using_sycl=true --define=using_trisycl=false --copt -fno-omit-frame-pointer --copt -fsanitize-coverage=3 --copt -DGPR_NO_DIRECT_SYSCALLS --linkopt -fPIC --linkopt -fsanitize=address
+
+build:sycl_trisycl --crosstool_top=@local_config_sycl//crosstool:toolchain
+build:sycl_trisycl --define=using_sycl=true --define=using_trisycl=true
+
+# Options extracted from configure script
+build:gdr --define=with_gdr_support=true
+build:ngraph --define=with_ngraph_support=true
+build:verbs --define=with_verbs_support=true
+
+# Options to disable default on features
+build:noaws --define=no_aws_support=true
+build:nogcp --define=no_gcp_support=true
+build:nohdfs --define=no_hdfs_support=true
+build:nokafka --define=no_kafka_support=true
+build:noignite --define=no_ignite_support=true
+build:nonccl --define=no_nccl_support=true
+
+build --define=use_fast_cpp_protos=true
+build --define=allow_oversize_protos=true
+
+build --spawn_strategy=standalone
+build --strategy=Genrule=standalone
+build -c opt
+
+# Other build flags.
+build --define=grpc_no_ares=true
+
+# Modular TF build options
+build:dynamic_kernels --define=dynamic_loaded_kernels=true
+build:dynamic_kernels --copt=-DAUTOLOAD_DYNAMIC_KERNELS
+
+# 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/.github/ISSUE_TEMPLATE/bug-performance-issue.md b/.github/ISSUE_TEMPLATE/00-bug-performance-issue.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE/bug-performance-issue.md
rename to .github/ISSUE_TEMPLATE/00-bug-performance-issue.md
diff --git a/.github/ISSUE_TEMPLATE/build-installation-issue.md b/.github/ISSUE_TEMPLATE/10-build-installation-issue.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE/build-installation-issue.md
rename to .github/ISSUE_TEMPLATE/10-build-installation-issue.md
diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.md b/.github/ISSUE_TEMPLATE/20-documentation-issue.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE/documentation-issue.md
rename to .github/ISSUE_TEMPLATE/20-documentation-issue.md
diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/30-feature-request.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE/feature-request.md
rename to .github/ISSUE_TEMPLATE/30-feature-request.md
diff --git a/.github/ISSUE_TEMPLATE/40-tflite-op-request.md b/.github/ISSUE_TEMPLATE/40-tflite-op-request.md
new file mode 100644
index 0000000000000000000000000000000000000000..7b391279e479ade4ed5327728f19be8752e11507
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/40-tflite-op-request.md
@@ -0,0 +1,24 @@
+---
+name: TensorFlow Lite Op Request
+about: Use this template for reporting ops you are using or missing.
+
+---
+
+
+**System information**
+- OS Platform and Distribution (e.g., Linux Ubuntu 16.04):
+- TensorFlow installed from (source or binary):
+- TensorFlow version (or github SHA if from source):
+
+
+**Provide the text output from tflite_convert**
+
+```
+# Copy and paste here
+```
+
+Also, please include a link to a GraphDef or the model if possible.
+
+**Any other info / logs**
+
+Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached.
diff --git a/.github/ISSUE_TEMPLATE/other-issues.md b/.github/ISSUE_TEMPLATE/50-other-issues.md
similarity index 100%
rename from .github/ISSUE_TEMPLATE/other-issues.md
rename to .github/ISSUE_TEMPLATE/50-other-issues.md
diff --git a/.gitignore b/.gitignore
index 1ef4c297ee4f369775c13b32a46a55887de719e7..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
@@ -24,10 +24,10 @@ Pods
Podfile.lock
*.pbxproj
*.xcworkspacedata
-/tensorflow/contrib/lite/downloads/**
-/tensorflow/contrib/lite/gen/**
-/tensorflow/contrib/lite/examples/ios/simple/data/*.txt
-/tensorflow/contrib/lite/examples/ios/simple/data/*.tflite
+/tensorflow/lite/tools/make/downloads/**
+/tensorflow/lite/gen/**
+/tensorflow/lite/examples/ios/simple/data/*.txt
+/tensorflow/lite/examples/ios/simple/data/*.tflite
xcuserdata/**
/api_init_files_list.txt
/estimator_api_init_files_list.txt
diff --git a/BUILD b/BUILD
index 4bf647e47aa56cff0b3fd5af7d5df99d8b70549b..1200cf5f7103cad12ab9693c339c372f4f3bc0fb 100644
--- a/BUILD
+++ b/BUILD
@@ -2,5 +2,7 @@ exports_files(
[
"LICENSE",
"ACKNOWLEDGEMENTS",
+ "configure",
+ "configure.py",
],
)
diff --git a/CODEOWNERS b/CODEOWNERS
index 94cc865479cd6ab5cdb589490d3a2d650f06b160..cb3fa2312405ce44d5dfc30ea4164740f436e07e 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1,6 +1,7 @@
# Where component owners are known, add them here.
/tenosrflow/core/debug @caisq
+/tensorflow/core/nccl/ @azaks2 @chsigg
/tensorflow/core/platform/windows/ @mrry
/tensorflow/core/platform/s3 @yongtang
/tensorflow/go @asimshankar
@@ -46,18 +47,17 @@
/tensorflow/contrib/losses/ @alextp @ispirmustafa
/tensorflow/contrib/makefile/ @petewarden @satok16 @wolffg
/tensorflow/contrib/metrics/ @alextp @honkentuber @ispirmustafa
-/tensorflow/contrib/nccl/ @cwhipkey @zheng-xq
/tensorflow/contrib/opt/ @strategist333 @alextp
/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/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 20601eaf611d98f78382a7d260629e72e24a07c0..a4647020ff76830badd75f3d3f76a41a637159bb 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -51,10 +51,12 @@ However, for the vast majority of issues, we aim to empower individuals to first
If you are experiencing or witnessing conflict, we ask you to use the following escalation strategy to address the conflict:
-1. Address the perceived conflict directly with those involved, preferably in a real-time medium.
-2. If this fails, get a third party (e.g. a mutual friend, and/or someone with background on the issue, but not involved in conflict) to intercede.
-3. If you are still unable to resolve the conflict, and you believe it rises to harassment or another code of conduct violation, report it.
-
+1. Address the perceived conflict directly with those involved, preferably in a
+ real-time medium.
+2. If this fails, get a third party (e.g. a mutual friend, and/or someone with
+ background on the issue, but not involved in the conflict) to intercede.
+3. If you are still unable to resolve the conflict, and you believe it rises to
+ harassment or another code of conduct violation, report it.
## Reporting Violations
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 8af5370befbb090966a8b3af54d80c84a969aaa5..4e37b239b16e6eeefc587aeb242a03e1f88eddbd 100644
--- a/README.md
+++ b/README.md
@@ -9,12 +9,14 @@
|-----------------|
| [](https://www.tensorflow.org/api_docs/) |
-**TensorFlow** is an open source software library for numerical computation using
-data flow graphs. The graph nodes represent mathematical operations, while
+**TensorFlow** is an open source software library for numerical computation
+using data flow graphs. The graph nodes represent mathematical operations, while
the graph edges represent the multidimensional data arrays (tensors) that flow
-between them. This flexible architecture enables you to deploy computation to one
-or more CPUs or GPUs in a desktop, server, or mobile device without rewriting
-code. TensorFlow also includes [TensorBoard](https://www.tensorflow.org/guide/summaries_and_tensorboard), a data visualization toolkit.
+between them. This flexible architecture enables you to deploy computation to
+one or more CPUs or GPUs in a desktop, server, or mobile device without
+rewriting code. TensorFlow also includes
+[TensorBoard](https://github.com/tensorflow/tensorboard), a data visualization
+toolkit.
TensorFlow was originally developed by researchers and engineers
working on the Google Brain team within Google's Machine Intelligence Research
@@ -55,21 +57,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
@@ -111,22 +116,25 @@ 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_Ubuntu_16.04_CPU/) | 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
-* [TensorFlow Website](https://www.tensorflow.org)
-* [TensorFlow Tutorials](https://www.tensorflow.org/tutorials/)
-* [TensorFlow Model Zoo](https://github.com/tensorflow/models)
-* [TensorFlow Twitter](https://twitter.com/tensorflow)
-* [TensorFlow Blog](https://medium.com/tensorflow)
-* [TensorFlow Course at Stanford](https://web.stanford.edu/class/cs20si)
-* [TensorFlow Roadmap](https://www.tensorflow.org/community/roadmap)
-* [TensorFlow White Papers](https://www.tensorflow.org/about/bib)
-* [TensorFlow YouTube Channel](https://www.youtube.com/channel/UC0rqucBdTuFTjJiefW5t-IQ)
+
+* [TensorFlow Website](https://www.tensorflow.org)
+* [TensorFlow Tutorials](https://www.tensorflow.org/tutorials/)
+* [TensorFlow Model Zoo](https://github.com/tensorflow/models)
+* [TensorFlow Twitter](https://twitter.com/tensorflow)
+* [TensorFlow Blog](https://medium.com/tensorflow)
+* [TensorFlow Course at Stanford](https://web.stanford.edu/class/cs20si)
+* [TensorFlow Roadmap](https://www.tensorflow.org/community/roadmap)
+* [TensorFlow White Papers](https://www.tensorflow.org/about/bib)
+* [TensorFlow YouTube Channel](https://www.youtube.com/channel/UC0rqucBdTuFTjJiefW5t-IQ)
+* [TensorFlow Visualization Toolkit](https://github.com/tensorflow/tensorboard)
Learn more about the TensorFlow community at the [community page of tensorflow.org](https://www.tensorflow.org/community) for a few ways to participate.
diff --git a/RELEASE.md b/RELEASE.md
index 2b00d06580d925a4afed5753afb8f51f0ebac99f..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
@@ -258,8 +260,8 @@ Ag Ramesh, Alex Wiltschko, Alexander Pantyukhin, Amogh Mannekote, An Jiaoyang, A
* Update `tf.keras` to the Keras 2.1.6 API.
* Added [`tf.keras.layers.CuDNNGRU`](https://www.tensorflow.org/versions/r1.9/api_docs/python/tf/keras/layers/CuDNNGRU) and [`tf.keras.layers.CuDNNLSTM`](https://www.tensorflow.org/versions/r1.9/api_docs/python/tf/keras/layers/CuDNNLSTM) layers. [Try it](https://colab.sandbox.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/contrib/eager/python/examples/nmt_with_attention/nmt_with_attention.ipynb?linkId=53292082).
* Adding support of core [feature columns](https://www.tensorflow.org/get_started/feature_columns) and [losses](https://www.tensorflow.org/api_docs/python/tf/losses) to [gradient boosted trees estimators](https://github.com/tensorflow/models/tree/master/official/boosted_trees).
-* The [python interface](https://www.tensorflow.org/versions/r1.9/api_docs/python/tf/contrib/lite)
- for the [TFLite Optimizing Converter](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/toco/README.md)
+* The [python interface](https://www.tensorflow.org/versions/r1.9/api_docs/python/tf/lite)
+ for the [TFLite Optimizing Converter](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/toco/README.md)
has been expanded, and the command line interface (AKA: `toco`, `tflite_convert`) is once again
included in the standard `pip` installation.
* Improved data-loading and text processing with:
@@ -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
@@ -562,7 +590,7 @@ Yoni Tsafir, yordun, Yuan (Terry) Tang, Yuxin Wu, zhengdi, Zhengsheng Wei, 田
## Major Features And Improvements
* [Eager execution](https://github.com/tensorflow/tensorflow/tree/r1.5/tensorflow/contrib/eager)
preview version is now available.
-* [TensorFlow Lite](https://github.com/tensorflow/tensorflow/tree/r1.5/tensorflow/contrib/lite)
+* [TensorFlow Lite](https://github.com/tensorflow/tensorflow/tree/r1.5/tensorflow/lite)
dev preview is now available.
* CUDA 9.0 and cuDNN 7 support.
* Accelerated Linear Algebra (XLA):
@@ -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.
@@ -909,7 +937,7 @@ See also [TensorBoard 0.1.4](https://github.com/tensorflow/tensorboard/releases/
* Adds tf.contrib.nn.rank_sampled_softmax_loss, a sampled-softmax variant that can improve rank loss.
* `tf.contrib.metrics`.{streaming_covariance,streaming_pearson_correlation} modified to return nan when they have seen less or equal to 1 unit of weight.
* Adds time series models to contrib. See contrib/timeseries/README.md for details.
-* Adds FULLY_CONNECTED Op to tensorflow/contrib/lite/schema.fbs
+* Adds FULLY_CONNECTED Op to tensorflow/lite/schema.fbs
## Known Issues
* Tensorflow_gpu compilation fails with Bazel 0.5.3.
diff --git a/WORKSPACE b/WORKSPACE
index 17961829a605c2d1f2d2ba86a7c30c47618c139b..957b8d8528dc9b5e2ea134921b28601aa6fed2d1 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,12 +1,14 @@
workspace(name = "org_tensorflow")
+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
],
)
@@ -14,11 +16,64 @@ load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories")
closure_repositories()
+load("//third_party/toolchains/preconfig/generate:archives.bzl",
+ "bazel_toolchains_archive")
+
+bazel_toolchains_archive()
+
+load(
+ "@bazel_toolchains//repositories:repositories.bzl",
+ bazel_toolchains_repositories = "repositories",
+)
+
+bazel_toolchains_repositories()
+
+load(
+ "@io_bazel_rules_docker//container:container.bzl",
+ container_repositories = "repositories",
+)
+
+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")
@@ -30,9 +85,9 @@ android_workspace()
# Please add all new TensorFlow dependencies in workspace.bzl.
tf_workspace()
-new_http_archive(
+http_archive(
name = "inception_v1",
- build_file = "models.BUILD",
+ build_file = "//:models.BUILD",
sha256 = "7efe12a8363f09bc24d7b7a450304a15655a57a7751929b2c1593a71183bb105",
urls = [
"http://storage.googleapis.com/download.tensorflow.org/models/inception_v1.zip",
@@ -40,9 +95,9 @@ new_http_archive(
],
)
-new_http_archive(
+http_archive(
name = "mobile_ssd",
- build_file = "models.BUILD",
+ build_file = "//:models.BUILD",
sha256 = "bddd81ea5c80a97adfac1c9f770e6f55cbafd7cce4d3bbe15fbeb041e6b8f3e8",
urls = [
"http://storage.googleapis.com/download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_android_export.zip",
@@ -50,9 +105,9 @@ new_http_archive(
],
)
-new_http_archive(
+http_archive(
name = "mobile_multibox",
- build_file = "models.BUILD",
+ build_file = "//:models.BUILD",
sha256 = "859edcddf84dddb974c36c36cfc1f74555148e9c9213dedacf1d6b613ad52b96",
urls = [
"http://storage.googleapis.com/download.tensorflow.org/models/mobile_multibox_v1a.zip",
@@ -60,9 +115,9 @@ new_http_archive(
],
)
-new_http_archive(
+http_archive(
name = "stylize",
- build_file = "models.BUILD",
+ build_file = "//:models.BUILD",
sha256 = "3d374a730aef330424a356a8d4f04d8a54277c425e274ecb7d9c83aa912c6bfa",
urls = [
"http://storage.googleapis.com/download.tensorflow.org/models/stylize_v1.zip",
@@ -70,12 +125,13 @@ new_http_archive(
],
)
-new_http_archive(
+http_archive(
name = "speech_commands",
- build_file = "models.BUILD",
+ build_file = "//:models.BUILD",
sha256 = "c3ec4fea3158eb111f1d932336351edfe8bd515bb6e87aad4f25dbad0a600d0c",
urls = [
"http://storage.googleapis.com/download.tensorflow.org/models/speech_commands_v0.01.zip",
"http://download.tensorflow.org/models/speech_commands_v0.01.zip",
],
)
+
diff --git a/configure.py b/configure.py
index ceaae6399343b6d3690b37ae9624776b7276c93e..adc9ef9caca8c0128c63896fdebbbadf7f86da81 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'
@@ -43,7 +43,7 @@ _DEFAULT_CUDA_PATH_WIN = ('C:/Program Files/NVIDIA GPU Computing '
_TF_OPENCL_VERSION = '1.2'
_DEFAULT_COMPUTECPP_TOOLKIT_PATH = '/usr/local/computecpp'
_DEFAULT_TRISYCL_INCLUDE_DIR = '/usr/local/triSYCL/include'
-_SUPPORTED_ANDROID_NDK_VERSIONS = [10, 11, 12, 13, 14, 15, 16]
+_SUPPORTED_ANDROID_NDK_VERSIONS = [10, 11, 12, 13, 14, 15, 16, 17, 18]
_DEFAULT_PROMPT_ASK_ATTEMPTS = 10
@@ -238,6 +238,13 @@ def setup_python(environ_cp):
write_to_bazelrc('build --python_path=\"%s"' % python_bin_path)
environ_cp['PYTHON_BIN_PATH'] = python_bin_path
+ # If choosen python_lib_path is from a path specified in the PYTHONPATH
+ # variable, need to tell bazel to include PYTHONPATH
+ if environ_cp.get('PYTHONPATH'):
+ python_paths = environ_cp.get('PYTHONPATH').split(':')
+ if python_lib_path in python_paths:
+ write_action_env_to_bazelrc('PYTHONPATH', environ_cp.get('PYTHONPATH'))
+
# Write tools/python_bin_path.sh
with open(
os.path.join(_TF_WORKSPACE_ROOT, 'tools', 'python_bin_path.sh'),
@@ -248,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.
@@ -445,11 +440,12 @@ def convert_version_to_int(version):
return int(version_str)
-def check_bazel_version(min_version):
- """Check installed bazel version is at least min_version.
+def check_bazel_version(min_version, max_version):
+ """Check installed bazel version is between min_version and max_version.
Args:
min_version: string for minimum bazel version.
+ max_version: string for maximum bazel version.
Returns:
The bazel version detected.
@@ -467,6 +463,7 @@ def check_bazel_version(min_version):
min_version_int = convert_version_to_int(min_version)
curr_version_int = convert_version_to_int(curr_version)
+ max_version_int = convert_version_to_int(max_version)
# Check if current bazel version can be detected properly.
if not curr_version_int:
@@ -479,7 +476,14 @@ def check_bazel_version(min_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)
+ 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! 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
@@ -859,7 +863,7 @@ def set_tf_cuda_version(environ_cp):
cuda_toolkit_paths_full = [
os.path.join(cuda_toolkit_path, x) for x in cuda_rt_lib_paths
]
- if any([os.path.exists(x) for x in cuda_toolkit_paths_full]):
+ if any(os.path.exists(x) for x in cuda_toolkit_paths_full):
break
# Reset and retry
@@ -1182,6 +1186,7 @@ def set_tf_nccl_install_path(environ_cp):
if is_windows() or is_cygwin():
nccl_install_path = cygpath(nccl_install_path)
+ nccl_lib_path = ''
if is_windows():
nccl_lib_path = 'lib/x64/nccl.lib'
elif is_linux():
@@ -1417,11 +1422,16 @@ def set_mpi_home(environ_cp):
def valid_mpi_path(mpi_home):
exists = (
os.path.exists(os.path.join(mpi_home, 'include')) and
- os.path.exists(os.path.join(mpi_home, 'lib')))
+ (os.path.exists(os.path.join(mpi_home, 'lib')) or
+ os.path.exists(os.path.join(mpi_home, 'lib64')) or
+ os.path.exists(os.path.join(mpi_home, 'lib32'))))
if not exists:
- print('Invalid path to the MPI Toolkit. %s or %s cannot be found' %
- (os.path.join(mpi_home, 'include'),
- os.path.exists(os.path.join(mpi_home, 'lib'))))
+ print(
+ 'Invalid path to the MPI Toolkit. %s or %s or %s or %s cannot be found'
+ % (os.path.join(mpi_home, 'include'),
+ os.path.exists(os.path.join(mpi_home, 'lib')),
+ os.path.exists(os.path.join(mpi_home, 'lib64')),
+ os.path.exists(os.path.join(mpi_home, 'lib32'))))
return exists
_ = prompt_loop_or_load_from_env(
@@ -1462,8 +1472,17 @@ def set_other_mpi_vars(environ_cp):
if os.path.exists(os.path.join(mpi_home, 'lib/libmpi.so')):
symlink_force(
os.path.join(mpi_home, 'lib/libmpi.so'), 'third_party/mpi/libmpi.so')
+ elif os.path.exists(os.path.join(mpi_home, 'lib64/libmpi.so')):
+ symlink_force(
+ os.path.join(mpi_home, 'lib64/libmpi.so'), 'third_party/mpi/libmpi.so')
+ elif os.path.exists(os.path.join(mpi_home, 'lib32/libmpi.so')):
+ symlink_force(
+ os.path.join(mpi_home, 'lib32/libmpi.so'), 'third_party/mpi/libmpi.so')
+
else:
- raise ValueError('Cannot find the MPI library file in %s/lib' % mpi_home)
+ raise ValueError(
+ 'Cannot find the MPI library file in %s/lib or %s/lib64 or %s/lib32' %
+ mpi_home, mpi_home, mpi_home)
def set_system_libs_flag(environ_cp):
@@ -1537,9 +1556,10 @@ def main():
# environment variables.
environ_cp = dict(os.environ)
- check_bazel_version('0.15.0')
+ check_bazel_version('0.19.0', '0.21.0')
reset_tf_configure_bazelrc()
+
cleanup_makefile()
setup_python(environ_cp)
@@ -1674,10 +1694,10 @@ 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.')
if __name__ == '__main__':
main()
-
diff --git a/tensorflow/BUILD b/tensorflow/BUILD
index 82526cead476bdc2eb9a5c5d53922d3a3d3ba5ae..0be7920d1193f2709a4d09d2d5e51daba35a5ff4 100644
--- a/tensorflow/BUILD
+++ b/tensorflow/BUILD
@@ -43,6 +43,11 @@ TENSORFLOW_API_INIT_FILES_V2 = (
TENSORFLOW_API_INIT_FILES + 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)
+)
+
# Config setting used when building for products
# which requires restricted licenses to be avoided.
config_setting(
@@ -197,6 +202,12 @@ config_setting(
visibility = ["//visibility:public"],
)
+config_setting(
+ name = "arm",
+ values = {"cpu": "arm"},
+ visibility = ["//visibility:public"],
+)
+
config_setting(
name = "freebsd",
values = {"cpu": "freebsd"},
@@ -213,31 +224,37 @@ config_setting(
#
config_setting(
name = "no_aws_support",
- define_values = {"no_aws_support": "false"},
+ define_values = {"no_aws_support": "true"},
visibility = ["//visibility:public"],
)
config_setting(
name = "no_gcp_support",
- define_values = {"no_gcp_support": "false"},
+ define_values = {"no_gcp_support": "true"},
visibility = ["//visibility:public"],
)
config_setting(
name = "no_hdfs_support",
- define_values = {"no_hdfs_support": "false"},
+ define_values = {"no_hdfs_support": "true"},
visibility = ["//visibility:public"],
)
config_setting(
name = "no_ignite_support",
- define_values = {"no_ignite_support": "false"},
+ define_values = {"no_ignite_support": "true"},
visibility = ["//visibility:public"],
)
config_setting(
name = "no_kafka_support",
- define_values = {"no_kafka_support": "false"},
+ define_values = {"no_kafka_support": "true"},
+ visibility = ["//visibility:public"],
+)
+
+config_setting(
+ name = "no_nccl_support",
+ define_values = {"no_nccl_support": "true"},
visibility = ["//visibility:public"],
)
@@ -256,6 +273,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"},
@@ -344,14 +370,26 @@ 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/deepmind/...",
"//learning/meta_rank/...",
+ "//platforms/performance/autograppler/...",
"//tensorflow/...",
- "//tensorflow_estimator/...",
+ "//tensorflow_estimator/contrib/...",
"//tensorflow_fold/llgtm/...",
+ "//tensorflow_text/...",
"//third_party/py/tensor2tensor/...",
],
)
@@ -553,29 +591,40 @@ genrule(
}),
outs = ["__init__.py"],
cmd = select({
- "api_version_2": "cp $(@D)/_api/v2/__init__.py $(OUTS)",
- "//conditions:default": "cp $(@D)/_api/v1/__init__.py $(OUTS)",
+ "api_version_2": "cp $(@D)/_api/v2/v2.py $(OUTS)",
+ "//conditions:default": "cp $(@D)/_api/v1/v1.py $(OUTS)",
}),
)
gen_api_init_files(
name = "tf_python_api_gen_v1",
- srcs = ["api_template.__init__.py"],
+ srcs = [
+ "api_template_v1.__init__.py",
+ "compat_template_v1.__init__.py",
+ ],
api_version = 1,
+ compat_api_versions = [1],
+ compat_init_templates = ["compat_template_v1.__init__.py"],
output_dir = "_api/v1/",
- output_files = TENSORFLOW_API_INIT_FILES_V1,
+ output_files = TENSORFLOW_API_INIT_FILES_V1_WITH_COMPAT,
output_package = "tensorflow._api.v1",
- root_init_template = "api_template.__init__.py",
+ root_file_name = "v1.py",
+ root_init_template = "api_template_v1.__init__.py",
)
gen_api_init_files(
name = "tf_python_api_gen_v2",
- srcs = ["api_template.__init__.py"],
+ srcs = [
+ "api_template.__init__.py",
+ "compat_template_v1.__init__.py",
+ ],
api_version = 2,
compat_api_versions = [1],
+ compat_init_templates = ["compat_template_v1.__init__.py"],
output_dir = "_api/v2/",
output_files = TENSORFLOW_API_INIT_FILES_V2,
output_package = "tensorflow._api.v2",
+ root_file_name = "v2.py",
root_init_template = "api_template.__init__.py",
)
@@ -583,9 +632,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",
],
)
@@ -595,7 +646,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 65172fd74a1660adc021ae97f769b05483bc0ba0..a93799bfe84b0f9c4743e1ad0effd6e69ad7f3f2 100644
--- a/tensorflow/api_template.__init__.py
+++ b/tensorflow/api_template.__init__.py
@@ -18,36 +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 import pywrap_tensorflow # pylint: disable=unused-import
+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._v2.estimator'))
-from tensorflow.python.tools import component_api_helper
-component_api_helper.package_hook(
+_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_estimator.python.estimator.api.estimator'))
-del component_api_helper
+ 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 not hasattr(_current_module, '__path__'):
+ __path__ = [_tf_api_dir]
+elif _tf_api_dir not in __path__:
+ __path__.append(_tf_api_dir)
-# API IMPORTS PLACEHOLDER
+# Enable TF2 behaviors
+from tensorflow.python.compat import v2_compat as _compat # pylint: disable=g-import-not-at-top
+_compat.enable_v2_behavior()
-from tensorflow.python.util.lazy_loader import LazyLoader # pylint: disable=g-import-not-at-top
-contrib = LazyLoader('contrib', globals(), 'tensorflow.contrib')
-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
-# "contrib".
-if '__all__' in vars():
- vars()['__all__'].append('contrib')
-from tensorflow.python.platform import flags # pylint: disable=g-import-not-at-top
-app.flags = flags # pylint: disable=undefined-variable
+# 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
-# 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__:
- __path__.append(_tf_api_dir)
+# 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
@@ -59,7 +100,16 @@ try:
del core
except NameError:
# Don't fail if these modules are not available.
- # For e.g. we are using this file for compat.v1 module as well and
- # 'python', 'core' directories are not under compat/v1.
+ # For e.g. this file will be originally placed under tensorflow/_api/v1 which
+ # does not have 'python', 'core' directories. Then, it will be copied
+ # to tensorflow/ which does have these two directories.
pass
+# Similarly for compiler. Do it separately to make sure we do this even if the
+# others don't exist.
+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
new file mode 100644
index 0000000000000000000000000000000000000000..eeca8f0d566a6401cb64e4fe3f0ee3c5aeb4ece2
--- /dev/null
+++ b/tensorflow/api_template_v1.__init__.py
@@ -0,0 +1,133 @@
+# Copyright 2015 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.
+# ==============================================================================
+"""Bring in all of the public TensorFlow interface into this module."""
+
+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._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_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
+# "contrib".
+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(_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
+# must come from this module. So python adds these symbols for the
+# resolution to succeed.
+# pylint: disable=undefined-variable
+try:
+ del python
+ del core
+except NameError:
+ # Don't fail if these modules are not available.
+ # For e.g. this file will be originally placed under tensorflow/_api/v1 which
+ # does not have 'python', 'core' directories. Then, it will be copied
+ # to tensorflow/ which does have these two directories.
+ pass
+# Similarly for compiler. Do it separately to make sure we do this even if the
+# others don't exist.
+try:
+ del compiler
+except NameError:
+ pass
+# pylint: enable=undefined-variable
diff --git a/tensorflow/c/BUILD b/tensorflow/c/BUILD
index 56f5e6767ac68b1008c786e3b5a47b9b173ab9cb..6e50a09bfc5ed3a8f2f7e05e6a6a151525e8dfce 100644
--- a/tensorflow/c/BUILD
+++ b/tensorflow/c/BUILD
@@ -60,6 +60,7 @@ tf_cuda_library(
"//tensorflow/core:framework",
"//tensorflow/core:lib",
"//tensorflow/core:op_gen_lib",
+ "//tensorflow/core/distributed_runtime:server_lib",
],
}),
)
@@ -82,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",
@@ -95,6 +96,7 @@ tf_cuda_library(
"//tensorflow/core:protos_all_cc",
"//tensorflow/core:lib",
"//tensorflow/core:lib_internal",
+ "//tensorflow/core/distributed_runtime:server_lib",
],
}) + select({
"//tensorflow:with_xla_support": [
@@ -119,13 +121,15 @@ tf_cuda_library(
":c_api",
":c_api_internal",
"//tensorflow/c/eager:c_api",
- "//tensorflow/compiler/jit/legacy_flags:mark_for_compilation_pass_flags",
- "//tensorflow/contrib/tpu:all_ops",
+ "//tensorflow/c/eager:c_api_internal",
+ "//tensorflow/compiler/jit:flags",
"//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",
],
)
@@ -171,6 +175,58 @@ 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 = [
+ "kernels.cc",
+ ],
+ hdrs = [
+ "kernels.h",
+ ],
+ copts = tf_copts(),
+ visibility = ["//visibility:public"],
+ 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",
+ ],
+ }),
+)
+
# -----------------------------------------------------------------------------
# Tests
@@ -193,11 +249,24 @@ 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",
srcs = ["c_api_test.cc"],
data = [
+ ":test_op1.so",
"//tensorflow/cc/saved_model:saved_model_half_plus_two",
],
kernels = [":test_op_kernel"],
@@ -205,7 +274,10 @@ tf_cuda_cc_test(
"//tensorflow:darwin": ["-headerpad_max_install_names"],
"//conditions:default": [],
}),
- tags = ["noasan"],
+ tags = [
+ "no_oss", # http://b/119522529
+ "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(),
@@ -216,13 +288,21 @@ tf_cuda_cc_test(
"//tensorflow/cc:grad_ops",
"//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:proto_text",
"//tensorflow/core:protos_all_cc",
+ "//tensorflow/core:spectral_ops_op_lib",
"//tensorflow/core:test",
"//tensorflow/core:test_main",
"//tensorflow/core/kernels:array",
@@ -233,7 +313,7 @@ tf_cuda_cc_test(
tf_cc_test(
name = "c_api_experimental_test",
- size = "small",
+ size = "medium",
srcs = ["c_api_experimental_test.cc"],
data = ["testdata/tf_record"],
linkopts = select({
@@ -244,8 +324,12 @@ tf_cc_test(
# the shared library must be able to use core:framework.
# linkstatic = tf_kernel_tests_linkstatic(),
deps = [
+ ":c_api",
":c_api_experimental",
+ ":c_api_internal",
":c_test_util",
+ "//tensorflow/c/eager:c_api",
+ "//tensorflow/c/eager:c_api_test_util",
"//tensorflow/core:lib",
"//tensorflow/core:protos_all_cc",
"//tensorflow/core:test",
@@ -282,8 +366,8 @@ tf_cc_test(
)
tf_custom_op_library(
- name = "test_op.so",
- srcs = ["test_op.cc"],
+ name = "test_op1.so",
+ srcs = ["test_op1.cc"],
)
tf_kernel_library(
@@ -296,6 +380,51 @@ 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",
+ srcs = ["kernels_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",
+ ":kernels",
+ "//tensorflow/core:framework",
+ "//tensorflow/core:lib",
+ "//tensorflow/core:proto_text",
+ "//tensorflow/core:protos_all_cc",
+ "//tensorflow/core:test",
+ "//tensorflow/core:test_main",
+ ],
+)
+
# -----------------------------------------------------------------------------
# Python API target
diff --git a/tensorflow/c/README.md b/tensorflow/c/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b386998ceaf3e91daba04125fe83e2f3bdd508e5
--- /dev/null
+++ b/tensorflow/c/README.md
@@ -0,0 +1,7 @@
+# TensorFlow C API
+
+- See [www.tensorflow.org/install/lang_c](https://www.tensorflow.org/install/lang_c)
+- Nightly builds:
+ - [Linux CPU-only](https://storage.googleapis.com/tensorflow-nightly/github/tensorflow/lib_package/libtensorflow-cpu-linux-x86_64.tar.gz)
+ - [Linux GPU](https://storage.googleapis.com/tensorflow-nightly/github/tensorflow/lib_package/libtensorflow-gpu-linux-x86_64.tar.gz)
+ - [MacOS CPU-only](https://storage.googleapis.com/tensorflow-nightly/github/tensorflow/lib_package/libtensorflow-cpu-darwin-x86_64.tar.gz)
diff --git a/tensorflow/c/c_api.cc b/tensorflow/c/c_api.cc
index 1726db12fa62c5a3665de9fc306da38c1b7f0f9c..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
@@ -1942,6 +2016,10 @@ void TF_ImportGraphDefOptionsSetPrefix(TF_ImportGraphDefOptions* opts,
const char* prefix) {
opts->opts.prefix = prefix;
}
+void TF_ImportGraphDefOptionsSetDefaultDevice(TF_ImportGraphDefOptions* opts,
+ const char* device) {
+ opts->opts.default_device = device;
+}
void TF_ImportGraphDefOptionsSetUniquifyNames(TF_ImportGraphDefOptions* opts,
unsigned char uniquify_names) {
@@ -2806,4 +2884,74 @@ TF_Buffer* TF_GetRegisteredKernelsForOp(const char* name, TF_Status* status) {
}
return ret;
}
+
+// TF_Server functions ----------------------------------------------
+
+#ifndef __ANDROID__
+TF_Server::TF_Server(std::unique_ptr server)
+ : target(server->target()), server(std::move(server)) {}
+#endif // __ANDROID__
+
+TF_Server* TF_NewServer(const void* proto, size_t proto_len,
+ TF_Status* status) {
+#ifdef __ANDROID__
+ status->status = tensorflow::errors::Unimplemented(
+ "Server functionality is not supported in Android");
+ return nullptr;
+#else
+ tensorflow::ServerDef server_def;
+ if (!server_def.ParseFromArray(proto, static_cast(proto_len))) {
+ status->status = InvalidArgument(
+ "Could not parse provided bytes into a ServerDef protocol buffer");
+ return nullptr;
+ }
+
+ std::unique_ptr out_server;
+ status->status = tensorflow::NewServer(server_def, &out_server);
+ if (!status->status.ok()) return nullptr;
+
+ return new TF_Server(std::move(out_server));
+#endif
+}
+
+void TF_ServerStart(TF_Server* server, TF_Status* status) {
+#ifdef __ANDROID__
+ status->status = tensorflow::errors::Unimplemented(
+ "Server functionality is not supported in Android");
+#else
+ status->status = server->server->Start();
+#endif
+}
+
+void TF_ServerStop(TF_Server* server, TF_Status* status) {
+#ifdef __ANDROID__
+ status->status = tensorflow::errors::Unimplemented(
+ "Server functionality is not supported in Android");
+#else
+ status->status = server->server->Stop();
+#endif
+}
+
+void TF_ServerJoin(TF_Server* server, TF_Status* status) {
+#ifdef __ANDROID__
+ status->status = tensorflow::errors::Unimplemented(
+ "Server functionality is not supported in Android");
+#else
+ status->status = server->server->Join();
+#endif
+}
+
+const char* TF_ServerTarget(TF_Server* server) {
+#ifdef __ANDROID__
+ return nullptr;
+#else
+ return server->target.c_str();
+#endif
+}
+
+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 850f6ecd637d768bca99720e0add07680829e17a..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);
@@ -900,6 +934,12 @@ TF_CAPI_EXPORT extern void TF_DeleteImportGraphDefOptions(
TF_CAPI_EXPORT extern void TF_ImportGraphDefOptionsSetPrefix(
TF_ImportGraphDefOptions* opts, const char* prefix);
+// Set the execution device for nodes in `graph_def`.
+// Only applies to nodes where a device was not already explicitly specified.
+// `device` is copied and has no lifetime requirements.
+TF_CAPI_EXPORT extern void TF_ImportGraphDefOptionsSetDefaultDevice(
+ TF_ImportGraphDefOptions* opts, const char* device);
+
// Set whether to uniquify imported operation names. If true, imported operation
// names will be modified if their name already exists in the graph. If false,
// conflicting names will be treated as an error. Note that this option has no
@@ -1605,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.
//
@@ -1662,6 +1702,47 @@ TF_CAPI_EXPORT extern TF_Buffer* TF_GetAllRegisteredKernels(TF_Status* status);
TF_CAPI_EXPORT extern TF_Buffer* TF_GetRegisteredKernelsForOp(
const char* name, TF_Status* status);
+// --------------------------------------------------------------------------
+// In-process TensorFlow server functionality, for use in distributed training.
+// A Server instance encapsulates a set of devices and a Session target that
+// can participate in distributed training. A server belongs to a cluster
+// (specified by a ClusterSpec), and corresponds to a particular task in a
+// named job. The server can communicate with any other server in the same
+// cluster.
+
+// In-process TensorFlow server.
+typedef struct TF_Server TF_Server;
+
+// Creates a new in-process TensorFlow server configured using a serialized
+// ServerDef protocol buffer provided via `proto` and `proto_len`.
+//
+// The server will not serve any requests until TF_ServerStart is invoked.
+// The server will stop serving requests once TF_ServerStop or
+// TF_DeleteServer is invoked.
+TF_CAPI_EXPORT extern TF_Server* TF_NewServer(const void* proto,
+ size_t proto_len,
+ TF_Status* status);
+
+// Starts an in-process TensorFlow server.
+TF_CAPI_EXPORT extern void TF_ServerStart(TF_Server* server, TF_Status* status);
+
+// Stops an in-process TensorFlow server.
+TF_CAPI_EXPORT extern void TF_ServerStop(TF_Server* server, TF_Status* status);
+
+// Blocks until the server has been successfully stopped (via TF_ServerStop or
+// TF_ServerClose).
+TF_CAPI_EXPORT extern void TF_ServerJoin(TF_Server* server, TF_Status* status);
+
+// Returns the target string that can be provided to TF_SetTarget() to connect
+// a TF_Session to `server`.
+//
+// The returned string is valid only until TF_DeleteServer is invoked.
+TF_CAPI_EXPORT extern const char* TF_ServerTarget(TF_Server* server);
+
+// Destroy an in-process TensorFlow server, frees memory. If server is running
+// it will be stopped and joined.
+TF_CAPI_EXPORT extern void TF_DeleteServer(TF_Server* server);
+
#ifdef __cplusplus
} /* end extern "C" */
#endif
diff --git a/tensorflow/c/c_api_experimental.cc b/tensorflow/c/c_api_experimental.cc
index d4b78138e93624a7e41e917f8210281b500661bc..6cc74cfb3246e9526e862f363590ce43e390ffaa 100644
--- a/tensorflow/c/c_api_experimental.cc
+++ b/tensorflow/c/c_api_experimental.cc
@@ -15,12 +15,19 @@ 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/compiler/jit/legacy_flags/mark_for_compilation_pass_flags.h"
+#include "tensorflow/c/eager/c_api.h"
+#include "tensorflow/c/eager/c_api_internal.h"
+#include "tensorflow/compiler/jit/flags.h"
+#include "tensorflow/core/common_runtime/eager/attr_builder.h"
#include "tensorflow/core/framework/tensor.pb.h"
#include "tensorflow/core/graph/graph.h"
#include "tensorflow/core/graph/node_builder.h"
#include "tensorflow/core/lib/strings/strcat.h"
+#include "tensorflow/core/platform/init_main.h"
+#include "tensorflow/core/platform/net.h"
#include "tensorflow/core/platform/platform.h"
#include "tensorflow/core/protobuf/config.pb.h"
#include "tensorflow/core/protobuf/tensorflow_server.pb.h"
@@ -50,8 +57,8 @@ void TF_EnableXLACompilation(TF_SessionOptions* options, unsigned char enable) {
// These XLA flags are needed to trigger XLA properly from C (more generally
// non-Python) clients. If this API is called again with `enable` set to
// false, it is safe to keep these flag values as is.
- tensorflow::legacy_flags::MarkForCompilationPassFlags* flags =
- tensorflow::legacy_flags::GetMarkForCompilationPassFlags();
+ tensorflow::MarkForCompilationPassFlags* flags =
+ tensorflow::GetMarkForCompilationPassFlags();
flags->tf_xla_cpu_global_jit = true;
flags->tf_xla_min_cluster_size = 1;
} else {
@@ -60,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();
@@ -70,8 +78,8 @@ TF_Buffer* TF_CreateConfig(unsigned char enable_xla_compilation,
// These XLA flags are needed to trigger XLA properly from C (more generally
// non-Python) clients. If this API is called again with `enable` set to
// false, it is safe to keep these flag values as is.
- tensorflow::legacy_flags::MarkForCompilationPassFlags* flags =
- tensorflow::legacy_flags::GetMarkForCompilationPassFlags();
+ tensorflow::MarkForCompilationPassFlags* flags =
+ tensorflow::GetMarkForCompilationPassFlags();
flags->tf_xla_cpu_global_jit = true;
flags->tf_xla_min_cluster_size = 1;
} else {
@@ -81,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
@@ -119,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.
//
@@ -6524,7 +6542,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/cycle_length"
+ name: "ExperimentalParallelInterleaveDataset/cycle_length"
op: "Const"
attr {
key: "dtype"
@@ -6545,7 +6563,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/block_length"
+ name: "ExperimentalParallelInterleaveDataset/block_length"
op: "Const"
attr {
key: "dtype"
@@ -6566,7 +6584,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/sloppy"
+ name: "ExperimentalParallelInterleaveDataset/sloppy"
op: "Const"
attr {
key: "dtype"
@@ -6587,7 +6605,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/buffer_output_elements"
+ name: "ExperimentalParallelInterleaveDataset/buffer_output_elements"
op: "Const"
attr {
key: "dtype"
@@ -6608,7 +6626,7 @@ library {
}
}
node_def {
- name: "ParallelInterleaveDataset/prefetch_input_elements"
+ name: "ExperimentalParallelInterleaveDataset/prefetch_input_elements"
op: "Const"
attr {
key: "dtype"
@@ -6629,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 {
@@ -6736,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"
@@ -8529,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);
@@ -8727,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);
@@ -8738,7 +8763,359 @@ void TFE_TensorHandlePrintDebugString(TFE_TensorHandle* handle) {
TF_DeleteStatus(status);
}
-TF_CAPI_EXPORT extern void TF_MakeInternalErrorStatus(TF_Status* status,
- const char* errMsg) {
+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;
+ std::unique_ptr thread;
+ std::unique_ptr status;
+};
+
+TFE_ExecuteOpNotification* TFE_ExecuteOpInNewThread(TFE_Op* op,
+ TFE_TensorHandle** retvals,
+ int* num_retvals,
+ TF_Status* status) {
+ TFE_ExecuteOpNotification* n = new TFE_ExecuteOpNotification;
+
+ n->thread.reset(op->operation.EagerContext()->TFEnv()->StartThread(
+ tensorflow::ThreadOptions(), "ExecuteOpThread",
+ [op, retvals, num_retvals, n]() {
+ TFE_Execute(op, retvals, num_retvals, n->status.get());
+ n->n.Notify();
+ }));
+
+ return n;
+}
+
+void TFE_ExecuteOpNotificationWaitAndDelete(
+ TFE_ExecuteOpNotification* notification, TF_Status* status) {
+ if (notification == nullptr) {
+ status->status = tensorflow::errors::InvalidArgument(
+ "Passed in notification is a nullptr.");
+
+ return;
+ }
+ if (notification->thread == nullptr) {
+ status->status = tensorflow::errors::InvalidArgument(
+ "Passed in notification didn't start a thread correctly. Cleaning up "
+ "this notification. Please re-execute the operation to get a new "
+ "notification.");
+
+ delete notification;
+ return;
+ }
+
+ notification->n.WaitForNotification();
+
+ status->status = notification->status->status;
+
+ delete notification;
+}
+
+void TF_MakeInternalErrorStatus(TF_Status* status, const char* errMsg) {
status->status = tensorflow::errors::Internal(errMsg);
}
+
+// This builder is used in the eager API to build a NodeDef.
+struct TF_AttrBuilder : public tensorflow::AttrBuilder {
+ using tensorflow::AttrBuilder::AttrBuilder;
+ // The string buffers to make sure that any `attr_name` we pass into
+ // `builder->Set()` will outlive the subsequent
+ // `TF_AttrBuilderCheckCanRunOnDevice()` call(s) on the same `builder`.
+ std::set attr_names;
+};
+
+TF_AttrBuilder* TF_NewAttrBuilder(const char* op_name) {
+ return new TF_AttrBuilder(op_name);
+}
+
+void TF_DeleteAttrBuilder(TF_AttrBuilder* builder) { delete builder; }
+
+void TF_AttrBuilderSetType(TF_AttrBuilder* builder, const char* attr_name,
+ TF_DataType value) {
+ auto iter = builder->attr_names.insert(attr_name).first;
+ builder->Set((*iter).c_str(), static_cast(value));
+}
+
+void TF_AttrBuilderSetTypeList(TF_AttrBuilder* builder, const char* attr_name,
+ const TF_DataType* values, int num_values) {
+ auto iter = builder->attr_names.insert(attr_name).first;
+ builder->Set(
+ (*iter).c_str(),
+ tensorflow::gtl::ArraySlice(
+ reinterpret_cast(values), num_values));
+}
+
+void TF_AttrBuilderCheckCanRunOnDevice(TF_AttrBuilder* builder,
+ const char* device_type,
+ TF_Status* status) {
+ status->status = tensorflow::FindKernelDef(
+ tensorflow::DeviceType(device_type), builder->BuildNodeDef(),
+ /* def = */ nullptr, /* kernel_class_name = */ nullptr);
+}
+
+const char* TF_GetNumberAttrForOpListInput(const char* op_name, int input_index,
+ TF_Status* status) {
+ const tensorflow::OpDef* op_def = nullptr;
+ status->status =
+ tensorflow::OpRegistry::Global()->LookUpOpDef(op_name, &op_def);
+ if (!status->status.ok()) return nullptr;
+
+ if (input_index >= op_def->input_arg_size() || input_index < 0) {
+ status->status = tensorflow::errors::InvalidArgument(
+ input_index, " out of range for ", op_name);
+ return nullptr;
+ }
+
+ const tensorflow::OpDef_ArgDef& input_arg = op_def->input_arg()[input_index];
+
+ if (input_arg.number_attr().empty()) {
+ status->status = tensorflow::errors::NotFound(
+ op_name, " does not have number_attr() defined.");
+ return nullptr;
+ }
+
+ // The returned string is owned by OpRegistry, so liveness is not a concern.
+ return input_arg.number_attr().c_str();
+}
+
+int TF_OpIsStateful(const char* op_type, TF_Status* status) {
+ const tensorflow::OpRegistrationData* op_reg_data;
+ status->status =
+ tensorflow::OpRegistry::Global()->LookUp(op_type, &op_reg_data);
+ if (!status->status.ok()) {
+ return 0;
+ }
+ return op_reg_data->op_def.is_stateful();
+}
+
+void TF_InitMain(const char* usage, int* argc, char*** argv) {
+ tensorflow::port::InitMain(usage, argc, argv);
+}
+
+int TF_PickUnusedPortOrDie() {
+ return tensorflow::internal::PickUnusedPortOrDie();
+}
+
+TFE_TensorHandle* TFE_NewTensorHandleFromScalar(TF_DataType dtype_arg,
+ void* data, size_t len) {
+ auto dtype = static_cast(dtype_arg);
+ DCHECK(tensorflow::DataTypeCanUseMemcpy(dtype));
+
+ tensorflow::Tensor tensor(dtype, tensorflow::TensorShape({}));
+ 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;
+}
+
+void 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;
+ TF_AddInput(desc, symbolic_input);
+ }
+
+ VLOG(1) << "Adding attrs.";
+ // TODO(hongm): add attrs
+
+ auto* graph_op = TF_FinishOperation(desc, status);
+ if (!status->status.ok()) return;
+
+ 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);
+ }
+}
+
+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 d98d532e32e891e21f5b7ba360c74c3256fb1947..48ea0ec1ed78a071b7bf7c858881d943a3ff3acd 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,9 +190,131 @@ 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
+// notification that can be waited upon. This always executes the kernel in a
+// new thread.
+// 1. `retvals` and `num_retvals` can only be consumed after
+// `TFE_ExecuteOp` returns successfully. They shouldn't be used
+// if the return is unsuccessful
+// 2. These new APIs cannot be used together with the TFE context level async
+// support.
+TF_CAPI_EXPORT extern TFE_ExecuteOpNotification* TFE_ExecuteOpInNewThread(
+ TFE_Op* op, TFE_TensorHandle** retvals, int* num_retvals,
+ TF_Status* status);
+
+// Waits to complete the op execution, and cleans up the notification.
+// Errors reported by op execution are set in `status`.
+TF_CAPI_EXPORT extern void TFE_ExecuteOpNotificationWaitAndDelete(
+ TFE_ExecuteOpNotification* notification, TF_Status* status);
+
TF_CAPI_EXPORT extern void TF_MakeInternalErrorStatus(TF_Status* status,
const char* errMsg);
+// TF_NewAttrBuilder() returns an object that you can set attributes on as
+// though it were an op. This allows querying properties of that op for
+// type-checking purposes like if the op will run on a particular device type.
+typedef struct TF_AttrBuilder TF_AttrBuilder;
+TF_CAPI_EXPORT extern TF_AttrBuilder* TF_NewAttrBuilder(const char* op_name);
+TF_CAPI_EXPORT extern void TF_DeleteAttrBuilder(TF_AttrBuilder* builder);
+TF_CAPI_EXPORT extern void TF_AttrBuilderSetType(TF_AttrBuilder* builder,
+ const char* attr_name,
+ TF_DataType value);
+TF_CAPI_EXPORT extern void TF_AttrBuilderSetTypeList(TF_AttrBuilder* builder,
+ const char* attr_name,
+ const TF_DataType* values,
+ int num_values);
+
+// Checks the tensorflow::NodeDef built via the methods above to see if it can
+// run on device_type.
+TF_CAPI_EXPORT extern void TF_AttrBuilderCheckCanRunOnDevice(
+ TF_AttrBuilder* builder, const char* device_type, TF_Status* status);
+
+// For argument number input_index, fetch the corresponding number_attr that
+// needs to be updated with the argument length of the input list.
+// Returns nullptr if there is any problem like op_name is not found, or the
+// argument does not support this attribute type.
+TF_CAPI_EXPORT extern const char* TF_GetNumberAttrForOpListInput(
+ const char* op_name, int input_index, TF_Status* status);
+
+// Returns 1 if the op is stateful, 0 otherwise. The return value is undefined
+// if the status is not ok.
+TF_CAPI_EXPORT extern int TF_OpIsStateful(const char* op_type,
+ TF_Status* status);
+
+// Platform specific initialization routine. Very few platforms actually require
+// this to be called.
+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(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`.
+TF_CAPI_EXPORT extern void 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 c6effd39697e0397278770b53e98508074f99862..4cfcf2ef3b2ccd9d8aedaf8efa4a31ac12d91c1b 100644
--- a/tensorflow/c/c_api_experimental_test.cc
+++ b/tensorflow/c/c_api_experimental_test.cc
@@ -14,7 +14,10 @@ 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"
#include "tensorflow/core/lib/io/path.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/logging.h"
@@ -162,5 +165,205 @@ protocol: "grpc"
TF_DeleteStatus(status);
}
+TEST(CAPI_EXPERIMENTAL, IsStateful) {
+ std::unique_ptr status(
+ TF_NewStatus(), TF_DeleteStatus);
+ int assign = TF_OpIsStateful("AssignAddVariableOp", status.get());
+ ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
+ EXPECT_EQ(assign, 1);
+ int id = TF_OpIsStateful("Identity", status.get());
+ ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
+ EXPECT_EQ(id, 0);
+}
+
+TEST(CAPI_EXPERIMENTAL, TFE_ExecuteOpInNewThreadTest_Simple) {
+ TF_Status* status = TF_NewStatus();
+ TFE_ContextOptions* opts = TFE_NewContextOptions();
+ TFE_Context* ctx = TFE_NewContext(opts, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_DeleteContextOptions(opts);
+
+ TFE_TensorHandle* m = TestMatrixTensorHandle();
+
+ TFE_Op* matmul_op = MatMulOp(ctx, m, m);
+
+ TFE_TensorHandle* retvals[1] = {nullptr};
+ int num_retvals = 1;
+
+ auto* r =
+ TFE_ExecuteOpInNewThread(matmul_op, &retvals[0], &num_retvals, status);
+
+ TFE_ExecuteOpNotificationWaitAndDelete(r, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+
+ TF_Tensor* t = TFE_TensorHandleResolve(retvals[0], status);
+ 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]);
+
+ TFE_DeleteOp(matmul_op);
+ TFE_DeleteTensorHandle(m);
+
+ TFE_DeleteTensorHandle(retvals[0]);
+ TFE_DeleteContext(ctx);
+ TF_DeleteStatus(status);
+}
+
+// Perform a send/recv test. Recv blocks, so they need to be executed
+// asynchronously.
+TEST(CAPI_EXPERIMENTAL, TFE_ExecuteOpInNewThreadTest_Blocking) {
+ TF_Status* status = TF_NewStatus();
+ TFE_ContextOptions* opts = TFE_NewContextOptions();
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_Context* ctx = TFE_NewContext(opts, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_DeleteContextOptions(opts);
+
+ // Returns a 2x2 float32 Tensor on the CPU, with data 1., 2., 3., 4.
+ TFE_TensorHandle* m = TestMatrixTensorHandle();
+
+ // Build a send op.
+ TFE_Op* send_op = TFE_NewOp(ctx, "_Send", status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_OpAddInput(send_op, m, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+
+ string tensor_name = "Tensor";
+ TFE_OpSetAttrType(send_op, "T", TF_FLOAT);
+ TFE_OpSetAttrString(send_op, "tensor_name", tensor_name.c_str(),
+ tensor_name.size());
+ string send_device = "/job:localhost/replica:0/task:0/device:CPU:0";
+ TFE_OpSetAttrString(send_op, "send_device", send_device.c_str(),
+ send_device.size());
+ TFE_OpSetAttrInt(send_op, "send_device_incarnation", 1234);
+ string recv_device = "/job:localhost/replica:0/task:0/device:CPU:0";
+ TFE_OpSetAttrString(send_op, "recv_device", recv_device.c_str(),
+ recv_device.size());
+ TFE_OpSetAttrBool(send_op, "client_terminated", true);
+
+ // Build a recv op.
+ TFE_Op* recv_op = TFE_NewOp(ctx, "_Recv", status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+
+ TFE_OpSetAttrType(recv_op, "tensor_type", TF_FLOAT);
+ TFE_OpSetAttrString(recv_op, "tensor_name", tensor_name.c_str(),
+ tensor_name.size());
+ TFE_OpSetAttrString(recv_op, "send_device", send_device.c_str(),
+ send_device.size());
+ TFE_OpSetAttrInt(recv_op, "send_device_incarnation", 1234);
+ TFE_OpSetAttrString(recv_op, "recv_device", recv_device.c_str(),
+ recv_device.size());
+ TFE_OpSetAttrBool(recv_op, "client_terminated", true);
+
+ TFE_TensorHandle* send_retvals;
+ int send_num_retvals = 0;
+ auto* send_result = TFE_ExecuteOpInNewThread(send_op, &send_retvals,
+ &send_num_retvals, status);
+
+ TFE_TensorHandle* recv_retvals[1] = {nullptr};
+ int recv_num_retvals = 1;
+ auto* recv_result = TFE_ExecuteOpInNewThread(recv_op, &recv_retvals[0],
+ &recv_num_retvals, status);
+
+ TFE_ExecuteOpNotificationWaitAndDelete(send_result, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_ExecuteOpNotificationWaitAndDelete(recv_result, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+
+ TF_Tensor* t = TFE_TensorHandleResolve(recv_retvals[0], status);
+ 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(1, product[0]);
+ EXPECT_EQ(2, product[1]);
+ EXPECT_EQ(3, product[2]);
+ EXPECT_EQ(4, product[3]);
+
+ TFE_DeleteOp(send_op);
+ TFE_DeleteOp(recv_op);
+ TFE_DeleteTensorHandle(m);
+
+ TFE_DeleteTensorHandle(recv_retvals[0]);
+ TFE_DeleteContext(ctx);
+ 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);
+}
+
+TEST(CAPI_EXPERIMENTAL, DebugPrintAndSymbolicExecution) {
+ TF_Status* status = TF_NewStatus();
+ TFE_ContextOptions* opts = TFE_NewContextOptions();
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_Context* ctx = TFE_NewContext(opts, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_DeleteContextOptions(opts);
+
+ TFE_TensorHandle* m = TestMatrixTensorHandle();
+ TFE_Op* op = MatMulOp(ctx, m, m);
+
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_OpPrintDebugString(op);
+
+ auto* graph = TF_NewGraph();
+ auto* trace_ctx = TFE_NewTraceContext(graph);
+ 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_DeleteTraceContext(trace_ctx);
+ TF_DeleteGraph(graph);
+
+ TFE_DeleteTensorHandle(m);
+ TFE_DeleteOp(op);
+ TFE_DeleteContext(ctx);
+ TF_DeleteStatus(status);
+}
+
} // namespace
} // namespace tensorflow
diff --git a/tensorflow/c/c_api_function.cc b/tensorflow/c/c_api_function.cc
index f68f8a3e90a971b5e4a024feaf26ba498afc48da..28b9f8df9c873ee394eb6a241dd9ac06ba6c8796 100644
--- a/tensorflow/c/c_api_function.cc
+++ b/tensorflow/c/c_api_function.cc
@@ -392,26 +392,26 @@ Status ProcessInputs(
EXCLUSIVE_LOCKS_REQUIRED(fn_body->mu) {
input_tensors->reserve(ninputs);
for (int i = 0; i < ninputs; ++i) {
- const Node& node = inputs[i].oper->node;
+ Node* node = &inputs[i].oper->node;
int idx = inputs[i].index;
TF_RETURN_WITH_CONTEXT_IF_ERROR(
- fn_body->graph.IsValidOutputTensor(&node, idx),
+ fn_body->graph.IsValidOutputTensor(node, idx),
"Encountered while processing input ", i, " into function '", fn_name,
"'");
- TF_RETURN_WITH_CONTEXT_IF_ERROR(ValidateNonRefOutput(&node, idx),
+ TF_RETURN_WITH_CONTEXT_IF_ERROR(ValidateNonRefOutput(node, idx),
"Encountered while processing input ", i,
" into function '", fn_name, "'");
- input_tensors->emplace_back(&node, idx);
+ input_tensors->emplace_back(node, idx);
- const auto& iter = input_nodes->find(&node);
+ const auto& iter = input_nodes->find(node);
if (iter == input_nodes->end()) {
- input_nodes->insert({&node, {idx}});
+ input_nodes->insert({node, {idx}});
} else {
auto& indices = iter->second;
if (std::find(indices.begin(), indices.end(), idx) != indices.end()) {
- return InvalidArgument("TF_Output ", node.name(), ":", idx,
+ return InvalidArgument("TF_Output ", node->name(), ":", idx,
" appears more than once in the input list");
}
indices.push_back(idx);
@@ -428,16 +428,16 @@ Status ProcessOutputs(const TF_Graph* fn_body, const char* fn_name,
EXCLUSIVE_LOCKS_REQUIRED(fn_body->mu) {
output_tensors->reserve(noutputs);
for (int i = 0; i < noutputs; ++i) {
- const Node& node = outputs[i].oper->node;
+ Node* node = &outputs[i].oper->node;
int idx = outputs[i].index;
TF_RETURN_WITH_CONTEXT_IF_ERROR(
- fn_body->graph.IsValidOutputTensor(&node, idx),
+ fn_body->graph.IsValidOutputTensor(node, idx),
"Encountered while processing output ", i, " from function '", fn_name,
"'");
- TF_RETURN_WITH_CONTEXT_IF_ERROR(ValidateNonRefOutput(&node, idx),
+ TF_RETURN_WITH_CONTEXT_IF_ERROR(ValidateNonRefOutput(node, idx),
"Encountered while creating function '",
fn_name, "'");
- output_tensors->emplace_back(&node, idx);
+ output_tensors->emplace_back(node, idx);
}
return Status::OK();
}
diff --git a/tensorflow/c/c_api_internal.h b/tensorflow/c/c_api_internal.h
index 95652a11378d6276b5ba6540a07baa15aa77cc1c..73283d775639b297857b2a50007dc7c28b1f39a3 100644
--- a/tensorflow/c/c_api_internal.h
+++ b/tensorflow/c/c_api_internal.h
@@ -25,6 +25,7 @@ limitations under the License.
#include
#ifndef __ANDROID__
+#include "tensorflow/core/distributed_runtime/server_lib.h"
#include "tensorflow/core/framework/op_gen_lib.h"
#endif
#include "tensorflow/core/common_runtime/shape_refiner.h"
@@ -179,6 +180,15 @@ struct TF_ApiDefMap {
tensorflow::mutex lock;
};
+#ifndef __ANDROID__
+struct TF_Server {
+ TF_Server(std::unique_ptr server);
+
+ const tensorflow::string target;
+ std::unique_ptr server;
+};
+#endif
+
namespace tensorflow {
class TensorCApi {
@@ -218,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 c4746b4990bc3bf80b749428f803056e552421c3..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);
}
@@ -187,15 +188,26 @@ TEST(CAPI, LibraryLoadFunctions) {
// tf_cuda_cc_test() bazel rule and remove the next line.
if (!GPUDeviceName().empty()) return;
- // Load the library.
- TF_Status* status = TF_NewStatus();
- TF_Library* lib =
- TF_LoadLibrary("tensorflow/c/test_op.so", status);
- TF_Code code = TF_GetCode(status);
- string status_msg(TF_Message(status));
- TF_DeleteStatus(status);
- ASSERT_EQ(TF_OK, code) << status_msg;
+#if !defined(TENSORFLOW_NO_SHARED_OBJECTS)
+ {
+ // Load the library.
+ TF_Status* status = TF_NewStatus();
+ TF_Library* lib =
+ TF_LoadLibrary("tensorflow/c/test_op1.so", status);
+ TF_Code code = TF_GetCode(status);
+ string status_msg(TF_Message(status));
+ TF_DeleteStatus(status);
+ ASSERT_EQ(TF_OK, code) << status_msg;
+ // Test op list.
+ TF_Buffer op_list_buf = TF_GetOpList(lib);
+ tensorflow::OpList op_list;
+ EXPECT_TRUE(op_list.ParseFromArray(op_list_buf.data, op_list_buf.length));
+ ASSERT_EQ(op_list.op_size(), 1);
+ EXPECT_EQ("TestCApi1", op_list.op(0).name());
+ TF_DeleteLibraryHandle(lib);
+ }
+#endif // !defined(TENSORFLOW_NO_SHARED_OBJECTS)
{
TF_Buffer* op_list_buffer = TF_GetAllOpList();
tensorflow::OpList op_list;
@@ -210,19 +222,6 @@ TEST(CAPI, LibraryLoadFunctions) {
EXPECT_TRUE(found);
TF_DeleteBuffer(op_list_buffer);
}
-
-#if !defined(TENSORFLOW_NO_SHARED_OBJECTS)
- {
- // Test op list.
- TF_Buffer op_list_buf = TF_GetOpList(lib);
- tensorflow::OpList op_list;
- EXPECT_TRUE(op_list.ParseFromArray(op_list_buf.data, op_list_buf.length));
- ASSERT_EQ(op_list.op_size(), 1);
- EXPECT_EQ("TestCApi", op_list.op(0).name());
- }
-#endif // !defined(TENSORFLOW_NO_SHARED_OBJECTS)
-
- TF_DeleteLibraryHandle(lib);
}
void TestEncodeDecode(int line, const std::vector& data) {
@@ -1469,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")
@@ -2349,14 +2383,8 @@ TEST(TestApiDef, TestCreateApiDef) {
// tf_cuda_cc_test() bazel rule and remove the next line.
if (!GPUDeviceName().empty()) return;
- TF_Status* status = TF_NewStatus();
- TF_Library* lib =
- TF_LoadLibrary("tensorflow/c/test_op.so", status);
- EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
- TF_DeleteStatus(status);
-
TF_Buffer* op_list_buf = TF_GetAllOpList();
- status = TF_NewStatus();
+ TF_Status* status = TF_NewStatus();
auto* api_def_map = TF_NewApiDefMap(op_list_buf, status);
EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
TF_DeleteStatus(status);
@@ -2376,7 +2404,6 @@ TEST(TestApiDef, TestCreateApiDef) {
TF_DeleteBuffer(api_def_buf);
TF_DeleteApiDefMap(api_def_map);
TF_DeleteBuffer(op_list_buf);
- TF_DeleteLibraryHandle(lib);
}
TEST(TestApiDef, TestCreateApiDefWithOverwrites) {
@@ -2384,14 +2411,8 @@ TEST(TestApiDef, TestCreateApiDefWithOverwrites) {
// tf_cuda_cc_test() bazel rule and remove the next line.
if (!GPUDeviceName().empty()) return;
- TF_Status* status = TF_NewStatus();
- TF_Library* lib =
- TF_LoadLibrary("tensorflow/c/test_op.so", status);
- EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
- TF_DeleteStatus(status);
-
TF_Buffer* op_list_buf = TF_GetAllOpList();
- status = TF_NewStatus();
+ TF_Status* status = TF_NewStatus();
auto* api_def_map = TF_NewApiDefMap(op_list_buf, status);
EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
TF_DeleteStatus(status);
@@ -2422,7 +2443,6 @@ TEST(TestApiDef, TestCreateApiDefWithOverwrites) {
TF_DeleteBuffer(api_def_buf);
TF_DeleteApiDefMap(api_def_map);
TF_DeleteBuffer(op_list_buf);
- TF_DeleteLibraryHandle(lib);
}
class DummyKernel : public tensorflow::OpKernel {
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 3ee31a6a7ac641bbd3fc4c05568b61e433a1d523..04dfefa6da28429b73856d392d94fa402ecda580 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(
@@ -50,6 +58,7 @@ tf_cuda_library(
],
"//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",
@@ -61,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:eager_profiler",
"//tensorflow/core:gpu_runtime",
],
)
@@ -69,7 +79,7 @@ tf_cuda_library(
name = "c_api_internal",
hdrs = ["c_api_internal.h"],
visibility = [
- "//learning/deepmind/courier:__pkg__",
+ "//learning/deepmind/courier:__subpackages__",
"//tensorflow:internal",
],
deps = [
@@ -100,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:eager_profiler",
],
)
@@ -143,6 +154,89 @@ tf_cuda_cc_test(
"//tensorflow/core:test",
"//tensorflow/core:test_main",
"//tensorflow/core/distributed_runtime/rpc:grpc_server_lib",
+ "@com_google_absl//absl/strings",
+ ],
+)
+
+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",
],
)
diff --git a/tensorflow/c/eager/c_api.cc b/tensorflow/c/eager/c_api.cc
index 3554ec0bf3202b54bfc38d67e51b89df19832302..af13f487af91594fedd4d5f77592682a6f98c34f 100755
--- a/tensorflow/c/eager/c_api.cc
+++ b/tensorflow/c/eager/c_api.cc
@@ -21,9 +21,11 @@ limitations under the License.
#include
#include
+#include "absl/memory/memory.h"
#include "tensorflow/c/c_api.h"
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/eager/c_api_internal.h"
+#include "tensorflow/core/platform/host_info.h"
#ifdef TENSORFLOW_EAGER_USE_XLA
#include "tensorflow/compiler/tf2xla/xla_op_registry.h"
#endif // TENSORFLOW_EAGER_USE_XLA
@@ -79,7 +81,7 @@ tensorflow::Status GetAllRemoteDevices(
const std::vector& remote_workers,
tensorflow::WorkerCacheInterface* worker_cache,
std::unique_ptr* device_mgr) {
- std::vector remote_devices;
+ std::vector> remote_devices;
tensorflow::Status status;
// TODO(nareshmodi) do this in parallel instead of serially.
for (const string& remote_worker : remote_workers) {
@@ -92,7 +94,7 @@ tensorflow::Status GetAllRemoteDevices(
status = s;
if (s.ok()) {
for (tensorflow::Device* d : *devices) {
- remote_devices.push_back(d);
+ remote_devices.emplace_back(d);
}
}
n.Notify();
@@ -100,7 +102,7 @@ tensorflow::Status GetAllRemoteDevices(
n.WaitForNotification();
}
std::unique_ptr remote_device_mgr(
- new tensorflow::DeviceMgr(remote_devices));
+ new tensorflow::DeviceMgr(std::move(remote_devices)));
TF_RETURN_IF_ERROR(status);
@@ -261,13 +263,13 @@ TF_CAPI_EXPORT extern void TFE_ContextSetAsyncForThread(TFE_Context* ctx,
void TFE_DeleteContextOptions(TFE_ContextOptions* options) { delete options; }
TFE_Context* TFE_NewContext(const TFE_ContextOptions* opts, TF_Status* status) {
- std::vector devices;
+ std::vector> devices;
status->status = tensorflow::DeviceFactory::AddDevices(
opts->session_options.options, "/job:localhost/replica:0/task:0",
&devices);
if (!status->status.ok()) return nullptr;
std::unique_ptr device_mgr(
- new tensorflow::DeviceMgr(devices));
+ new tensorflow::DeviceMgr(std::move(devices)));
tensorflow::Rendezvous* r =
new tensorflow::IntraProcessRendezvous(device_mgr.get());
@@ -354,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();
}
@@ -404,8 +408,19 @@ const char* TFE_TensorHandleDeviceName(TFE_TensorHandle* h, TF_Status* status) {
"The passed in handle is a nullptr");
return nullptr;
}
- tensorflow::Device* d = nullptr;
- status->status = h->handle->OpDevice(&d);
+ tensorflow::Device* d = h->handle->op_device();
+ return (d == nullptr) ? "/job:localhost/replica:0/task:0/device:CPU:0"
+ : d->name().c_str();
+}
+
+const char* TFE_TensorHandleBackingDeviceName(TFE_TensorHandle* h,
+ TF_Status* status) {
+ if (h == nullptr || h->handle == nullptr) {
+ status->status = tensorflow::errors::InvalidArgument(
+ "The passed in handle is a nullptr");
+ return nullptr;
+ }
+ tensorflow::Device* d = h->handle->device();
return (d == nullptr) ? "/job:localhost/replica:0/task:0/device:CPU:0"
: d->name().c_str();
}
@@ -430,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;
}
@@ -447,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) {
@@ -459,13 +490,20 @@ TFE_Op* TFE_NewOp(TFE_Context* ctx, const char* op_or_function_name,
TF_Status* status) {
const char* name = op_or_function_name; // Shorthand
const tensorflow::AttrTypeMap* types;
- status->status = tensorflow::AttrTypeMapForOp(name, &types);
- if (status->status.ok()) return new TFE_Op(ctx, name, types);
- if (TF_GetCode(status) == TF_NOT_FOUND) {
- if (ctx->context.FindFunctionByName(name)) {
- status->status = tensorflow::Status::OK();
- return new TFE_Op(ctx, name, nullptr);
+ bool is_function = false;
+ status->status = tensorflow::AttrTypeMapForOp(name, &types, &is_function);
+ if (status->status.ok()) {
+ if (is_function && !ctx->context.FindFunctionByName(name)) {
+ status->status = tensorflow::errors::NotFound(
+ "'", name,
+ "' is neither a type of a primitive operation nor a name "
+ "of a function registered in binary running on ",
+ tensorflow::port::Hostname(),
+ ". Make sure the operation or function is "
+ "registered in the binary running in this process.");
+ return nullptr;
}
+ return new TFE_Op(ctx, name, is_function, types);
}
return nullptr;
}
@@ -498,12 +536,6 @@ void TFE_OpAddInput(TFE_Op* op, TFE_TensorHandle* h, TF_Status* status) {
TF_AttrType TFE_OpGetAttrType(TFE_Op* op, const char* attr_name,
unsigned char* is_list, TF_Status* status) {
TF_AttrType ret;
- if (op->operation.is_function()) {
- status->status = tensorflow::errors::Unimplemented(
- "TODO(apassos): Support for attributes for TensorFlow functions is not "
- "ready yet.");
- return TF_ATTR_INT; // The compiler requires that we return something.
- }
status->status = tensorflow::AttrTypeByName(*op->operation.AttrTypes(),
attr_name, &ret, is_list);
return ret;
@@ -682,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 =
@@ -724,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);
}
@@ -760,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 b2454d872207e26feb3764671474a5d87c01f84d..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.
@@ -169,10 +169,21 @@ TF_CAPI_EXPORT extern int64_t TFE_TensorHandleNumElements(TFE_TensorHandle* h,
TF_CAPI_EXPORT extern int64_t TFE_TensorHandleDim(TFE_TensorHandle* h,
int dim_index,
TF_Status* status);
-// 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);
+// Returns the name of the device in whose memory `h` resides.
+//
+// This function will block till the operation that produces `h` has completed.
+TF_CAPI_EXPORT extern const char* TFE_TensorHandleBackingDeviceName(
+ TFE_TensorHandle* h, TF_Status* status);
+
// Return a pointer to a new TFE_TensorHandle that shares the underlying tensor
// with `h`. On success, `status` is set to OK. On failure, `status` reflects
// the error and a nullptr is returned.
@@ -382,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 5006b76f1981d068e99a2c081115ebb3a66d8c7f..ffcd5ace0b98597363abe63201bf6c328a03212f 100644
--- a/tensorflow/c/eager/c_api_debug.cc
+++ b/tensorflow/c/eager/c_api_debug.cc
@@ -57,13 +57,9 @@ TF_CAPI_EXPORT extern TFE_TensorDebugInfo* TFE_TensorHandleTensorDebugInfo(
return nullptr;
}
- tensorflow::Device* device;
- status->status = handle->handle->Device(&device);
- if (!status->status.ok()) {
- return nullptr;
- }
-
#ifdef TENSORFLOW_EAGER_USE_XLA
+ tensorflow::Device* device = handle->handle->device();
+
// If tensor resides on an XLA device, use XLA device's PaddedShapeFn.
tensorflow::XlaDevice* xla_device =
dynamic_cast(device);
@@ -87,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
@@ -103,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 104d52430cf7aa14d4d2a335a1b96e667f21ce87..3b9e681194b7cebc61d9028525d200c692bbd529 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/eager_profiler.h"
#include "tensorflow/core/public/version.h"
struct TFE_ContextOptions {
@@ -79,13 +80,15 @@ struct TFE_TensorHandle {
tensorflow::Device* op_device)
: handle(new tensorflow::TensorHandle(t, d, op_device, nullptr)) {}
- TFE_TensorHandle(tensorflow::uint64 node_id, tensorflow::DataType dtype,
- tensorflow::EagerContext* ctx)
- : handle(new tensorflow::TensorHandle(node_id, dtype, ctx)) {}
-
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 {
@@ -97,14 +100,20 @@ struct TFE_TensorDebugInfo {
};
struct TFE_Op {
- // t is NULL iff the TFE_Op corresponds to a TensorFlow function instead of a
- // primitive operation.
- TFE_Op(TFE_Context* ctx, const char* op, const tensorflow::AttrTypeMap* t)
- : operation(&ctx->context, op, t) {}
+ TFE_Op(TFE_Context* ctx, const char* op, bool is_function,
+ const tensorflow::AttrTypeMap* t)
+ : operation(&ctx->context, op, is_function, t) {}
tensorflow::EagerOperation operation;
};
+struct TFE_Profiler {
+ TFE_Profiler(TFE_Context* ctx)
+ : profiler(tensorflow::EagerProfiler::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,
@@ -112,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 55331022b9dbd0696928fa44430f340f371432ac..3d1ca4fb4b561a03ea9d879b1876fb1fd08a3139 100644
--- a/tensorflow/c/eager/c_api_test.cc
+++ b/tensorflow/c/eager/c_api_test.cc
@@ -16,6 +16,7 @@ limitations under the License.
#include "tensorflow/c/eager/c_api.h"
#include
+#include "absl/strings/match.h"
#include "tensorflow/c/eager/c_api_test_util.h"
#include "tensorflow/core/distributed_runtime/rpc/grpc_server_lib.h"
#include "tensorflow/core/framework/function.pb.h"
@@ -174,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));
@@ -589,9 +585,22 @@ void TensorHandleCopyBetweenTwoGPUDevices(bool async) {
TF_DeviceList* devices = TFE_ContextListDevices(ctx, status.get());
ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
const int num_devices = TF_DeviceListCount(devices);
+ bool has_gpu0 = false;
+ bool has_gpu1 = false;
+ for (int i = 0; i < num_devices; ++i) {
+ const char* dev = TF_DeviceListName(devices, i, status.get());
+ ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
+ string device_name(dev);
+ if (device_name.find("GPU:0") != string::npos) {
+ has_gpu0 = true;
+ }
+ if (device_name.find("GPU:1") != string::npos) {
+ has_gpu1 = true;
+ }
+ }
const char* kCPUDevice = "CPU:0";
- if (num_devices < 3) {
+ if (!has_gpu0 || !has_gpu1) {
TF_DeleteDeviceList(devices);
TF_DeleteTensor(t);
TFE_DeleteTensorHandle(hcpu);
@@ -781,6 +790,14 @@ TEST(CAPI, TensorHandleNullptr) {
TF_SetStatus(status.get(), TF_OK, "");
+ device_name = TFE_TensorHandleBackingDeviceName(h, status.get());
+ ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(status.get()));
+ ASSERT_EQ(device_name, nullptr);
+ ASSERT_EQ("The passed in handle is a nullptr",
+ string(TF_Message(status.get())));
+
+ TF_SetStatus(status.get(), TF_OK, "");
+
int num_dims = TFE_TensorHandleNumDims(h, status.get());
ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(status.get()));
ASSERT_EQ(num_dims, -1);
@@ -796,6 +813,62 @@ TEST(CAPI, TensorHandleNullptr) {
string(TF_Message(status.get())));
}
+TEST(CAPI, TensorHandleDevices) {
+ std::unique_ptr status(
+ TF_NewStatus(), TF_DeleteStatus);
+ TFE_ContextOptions* opts = TFE_NewContextOptions();
+ TFE_Context* ctx = TFE_NewContext(opts, status.get());
+ TFE_DeleteContextOptions(opts);
+ ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
+
+ TFE_TensorHandle* hcpu = TestMatrixTensorHandle();
+ const char* device_name = TFE_TensorHandleDeviceName(hcpu, status.get());
+ ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
+ ASSERT_TRUE(absl::StrContains(device_name, "CPU:0")) << device_name;
+ const char* backing_device_name =
+ TFE_TensorHandleBackingDeviceName(hcpu, status.get());
+ ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
+ ASSERT_TRUE(absl::StrContains(backing_device_name, "CPU:0"))
+ << backing_device_name;
+
+ // Disable the test if no GPU is present.
+ string gpu_device_name;
+ if (GetDeviceName(ctx, &gpu_device_name, "GPU")) {
+ TFE_TensorHandle* hgpu = TFE_TensorHandleCopyToDevice(
+ hcpu, ctx, gpu_device_name.c_str(), status.get());
+ ASSERT_TRUE(TF_GetCode(status.get()) == TF_OK) << TF_Message(status.get());
+
+ TFE_Op* shape_op = ShapeOp(ctx, hgpu);
+ TFE_OpSetDevice(shape_op, gpu_device_name.c_str(), status.get());
+ ASSERT_TRUE(TF_GetCode(status.get()) == TF_OK) << TF_Message(status.get());
+ TFE_TensorHandle* retvals[1];
+ int num_retvals = 1;
+ TFE_Execute(shape_op, &retvals[0], &num_retvals, status.get());
+ ASSERT_TRUE(TF_GetCode(status.get()) == TF_OK) << TF_Message(status.get());
+
+ // .device of shape is GPU since the op is executed on GPU
+ device_name = TFE_TensorHandleDeviceName(retvals[0], status.get());
+ ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
+ ASSERT_TRUE(absl::StrContains(device_name, "GPU:0")) << device_name;
+
+ // .backing_device of shape is CPU since the tensor is backed by CPU
+ backing_device_name =
+ TFE_TensorHandleBackingDeviceName(retvals[0], status.get());
+ ASSERT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
+ ASSERT_TRUE(absl::StrContains(backing_device_name, "CPU:0"))
+ << backing_device_name;
+
+ TFE_DeleteOp(shape_op);
+ TFE_DeleteTensorHandle(retvals[0]);
+ TFE_DeleteTensorHandle(hgpu);
+ }
+
+ TFE_DeleteTensorHandle(hcpu);
+ TFE_ContextAsyncWait(ctx, status.get());
+ EXPECT_EQ(TF_OK, TF_GetCode(status.get())) << TF_Message(status.get());
+ TFE_DeleteContext(ctx);
+}
+
void Execute_MatMul_CPU(bool async) {
TF_Status* status = TF_NewStatus();
TFE_ContextOptions* opts = TFE_NewContextOptions();
diff --git a/tensorflow/c/eager/c_api_test_util.cc b/tensorflow/c/eager/c_api_test_util.cc
index 008f088c2dcdd7d9114103516a4702e47a55c6de..bd38127d50c171af801dd1b937acefdba491b4a6 100644
--- a/tensorflow/c/eager/c_api_test_util.cc
+++ b/tensorflow/c/eager/c_api_test_util.cc
@@ -104,6 +104,19 @@ TFE_Op* MatMulOp(TFE_Context* ctx, TFE_TensorHandle* a, TFE_TensorHandle* b) {
return op;
}
+TFE_Op* ShapeOp(TFE_Context* ctx, TFE_TensorHandle* a) {
+ TF_Status* status = TF_NewStatus();
+
+ TFE_Op* op = TFE_NewOp(ctx, "Shape", status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TFE_OpAddInput(op, a, status);
+ CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
+ TF_DeleteStatus(status);
+ TFE_OpSetAttrType(op, "T", TFE_TensorHandleDataType(a));
+
+ return op;
+}
+
TFE_TensorHandle* TestAxisTensorHandle() {
int64_t dims[] = {1};
int data[] = {1};
diff --git a/tensorflow/c/eager/c_api_test_util.h b/tensorflow/c/eager/c_api_test_util.h
index 474cae67c89249af3a62707f0db00ba458ca8f31..75ef9459e93b4f2ed471c423a34565594efc1714 100644
--- a/tensorflow/c/eager/c_api_test_util.h
+++ b/tensorflow/c/eager/c_api_test_util.h
@@ -37,6 +37,9 @@ TFE_TensorHandle* TestMatrixTensorHandle3X2();
// Return a matmul op multiplying `a` by `b`.
TFE_Op* MatMulOp(TFE_Context* ctx, TFE_TensorHandle* a, TFE_TensorHandle* b);
+// Return a shape op fetching the shape of `a`.
+TFE_Op* ShapeOp(TFE_Context* ctx, TFE_TensorHandle* a);
+
// Return an 1-D INT32 tensor containing a single value 1.
TFE_TensorHandle* TestAxisTensorHandle();
diff --git a/tensorflow/c/eager/tape.h b/tensorflow/c/eager/tape.h
index 5ba55a203ff70cc64c07e96b5a869a1f11c9334e..5c11f51e8749de84547ae873f5f55ebd42bc4b3d 100644
--- a/tensorflow/c/eager/tape.h
+++ b/tensorflow/c/eager/tape.h
@@ -141,8 +141,9 @@ class GradientTape {
// null. The result is populated with one tensor per target element.
Status ComputeGradient(
const VSpace& vspace,
- gtl::ArraySlice target_tensor_ids,
- gtl::ArraySlice source_tensor_id,
+ const gtl::ArraySlice target_tensor_ids,
+ const gtl::ArraySlice source_tensor_ids,
+ const gtl::FlatMap sources_that_are_targets,
gtl::ArraySlice output_gradients,
std::vector* result);
@@ -396,6 +397,7 @@ template
Status InitialGradients(
const VSpace& vspace,
gtl::ArraySlice target_tensor_ids,
+ gtl::FlatMap sources_that_are_targets,
gtl::ArraySlice output_gradients, const TensorTape& tensor_tape,
const OpTape& op_tape,
gtl::FlatMap>* result) {
@@ -425,8 +427,13 @@ Status InitialGradients(
"none of operations outputs match expected tensor");
}
} else {
- // No record of the target tensor found on the tape, so no gradient
- // needs to be computed from it. Do nothing.
+ // This target tensor was not generated by any operation recorded on
+ // the tape, so no gradient needs to be computed from it unless this
+ // target is also a source.
+ auto source_tensor = sources_that_are_targets.find(id);
+ if (source_tensor != sources_that_are_targets.end()) {
+ (*result)[id].push_back(vspace.Ones(source_tensor->second));
+ }
}
} else {
(*result)[id].push_back(output_gradients[i]);
@@ -467,8 +474,9 @@ constexpr int kMinAggregateBytes = 128 * 1024 * 1024;
template
Status GradientTape::ComputeGradient(
const VSpace& vspace,
- gtl::ArraySlice target_tensor_ids,
- gtl::ArraySlice source_tensor_ids,
+ const gtl::ArraySlice target_tensor_ids,
+ const gtl::ArraySlice source_tensor_ids,
+ const gtl::FlatMap sources_that_are_targets,
gtl::ArraySlice output_gradients,
std::vector* result) {
gtl::FlatSet sources_set(source_tensor_ids.begin(),
@@ -478,7 +486,8 @@ Status GradientTape::ComputeGradient(
std::vector op_stack =
InitialStack(state.op_tape, state.op_missing_tensor);
gtl::FlatMap> gradients;
- Status s = InitialGradients(vspace, target_tensor_ids, output_gradients,
+ Status s = InitialGradients(vspace, target_tensor_ids,
+ sources_that_are_targets, output_gradients,
tensor_tape_, state.op_tape, &gradients);
auto cleanup = [this, &state]() {
if (!persistent_) {
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
new file mode 100644
index 0000000000000000000000000000000000000000..9505bf9dda32b9a338b574f1d31ec555a5628c6a
--- /dev/null
+++ b/tensorflow/c/kernels.cc
@@ -0,0 +1,202 @@
+/* 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
+
+#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"
+
+// This file forms the basis of a stable ABI for third-party kernel
+// implementations. It is crucial that changes to this file are made cautiously
+// and with a focus on maintaining both source and binary compatibility.
+
+struct TF_KernelBuilder {
+ ::tensorflow::KernelDefBuilder* cc_builder;
+
+ void* (*create_function)(TF_OpKernelConstruction*);
+ void (*compute_function)(void*, TF_OpKernelContext*);
+ void (*delete_function)(void*);
+};
+
+TF_KernelBuilder* TF_NewKernelBuilder(
+ const char* op_name, const char* device_name,
+ void* (*create_func)(TF_OpKernelConstruction*),
+ void (*compute_func)(void*, TF_OpKernelContext*),
+ void (*delete_func)(void*)) {
+ TF_KernelBuilder* result = new TF_KernelBuilder;
+ result->cc_builder = new ::tensorflow::KernelDefBuilder(op_name);
+ result->cc_builder->Device(device_name);
+ result->create_function = create_func;
+ result->compute_function = compute_func;
+ result->delete_function = delete_func;
+ return result;
+}
+
+void TF_DeleteKernelBuilder(TF_KernelBuilder* builder) {
+ if (builder != nullptr) {
+ delete builder->cc_builder;
+ delete builder;
+ }
+}
+
+namespace tensorflow {
+namespace {
+
+// An OpKernel whose methods delegate to C function pointers.
+class COpKernel : public OpKernel {
+ public:
+ explicit COpKernel(OpKernelConstruction* ctx,
+ void* (*create_func)(TF_OpKernelConstruction*),
+ void (*compute_func)(void*, TF_OpKernelContext*),
+ void (*delete_func)(void*))
+ : OpKernel(ctx), compute_func_(compute_func), delete_func_(delete_func) {
+ if (create_func != nullptr) {
+ c_kernel_ =
+ (*create_func)(reinterpret_cast(ctx));
+ } else {
+ c_kernel_ = nullptr;
+ }
+ }
+
+ void Compute(OpKernelContext* ctx) override {
+ (*compute_func_)(c_kernel_, reinterpret_cast(ctx));
+ }
+
+ ~COpKernel() override {
+ if (delete_func_ != nullptr) {
+ (*delete_func_)(c_kernel_);
+ }
+ }
+
+ private:
+ void (*compute_func_)(void*, TF_OpKernelContext* context);
+ void (*delete_func_)(void*);
+ void* c_kernel_;
+};
+
+// A KernelFactory that returns COpKernel instances.
+class KernelBuilderFactory
+ : public ::tensorflow::kernel_factory::OpKernelFactory {
+ public:
+ explicit KernelBuilderFactory(TF_KernelBuilder* builder)
+ : builder_(builder) {}
+ ::tensorflow::OpKernel* Create(
+ ::tensorflow::OpKernelConstruction* context) override {
+ return new ::tensorflow::COpKernel(context, builder_->create_function,
+ builder_->compute_function,
+ builder_->delete_function);
+ }
+ ~KernelBuilderFactory() override { TF_DeleteKernelBuilder(builder_); }
+
+ private:
+ TF_KernelBuilder* builder_;
+};
+} // namespace
+} // namespace tensorflow
+
+void TF_RegisterKernelBuilder(const char* name, TF_KernelBuilder* builder,
+ TF_Status* status) {
+ using tensorflow::register_kernel::Name;
+
+ tensorflow::kernel_factory::OpKernelRegistrar(
+ builder->cc_builder->Build(), name,
+ absl::make_unique(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
new file mode 100644
index 0000000000000000000000000000000000000000..b015d0103969355e8566242bfcc007f697c6ae18
--- /dev/null
+++ b/tensorflow/c/kernels.h
@@ -0,0 +1,153 @@
+/* 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_KERNELS_H_
+#define TENSORFLOW_C_KERNELS_H_
+
+#include "tensorflow/c/c_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// --------------------------------------------------------------------------
+// C API for TensorFlow Kernels.
+//
+// This API allows developers to register custom kernel implementations for
+// TensorFlow.
+//
+// See c_api.h header comments for a discussion about API conventions.
+//
+// Users wishing to extend TensorFlow with new kernels will call
+// `TF_NewKernelBuilder`. The resulting kernel builder can be registered with
+// `TF_RegisterKernelBuilder`, which will allow TF to construct user-provided
+// kernels when necessary.
+
+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.
+//
+// If non-null, TensorFlow will call create_func when it needs to instantiate
+// the kernel. The pointer returned by create_func will be passed to
+// compute_func and delete_func, thereby functioning as a "this" pointer for
+// referring to kernel instances.
+//
+// The TF_OpKernelConstruction pointer passed to create_func is owned by
+// TensorFlow and will be deleted once create_func returns. It must not be used
+// after this.
+//
+// When TensorFlow needs to perform a computation with this kernel, it will
+// call compute_func. This function will receive the pointer returned by
+// create_func (or null if no create_func was provided), along with the inputs
+// to the computation.
+//
+// The TF_OpKernelContext pointer received by compute_func is owned by
+// TensorFlow and will be deleted once compute_func returns. It must not be used
+// after this.
+//
+// Finally, when TensorFlow no longer needs the kernel, it will call
+// delete_func if one is provided. This function will receive the pointer
+// returned in `create_func` or nullptr if no `create_func` was provided.
+//
+// The caller should pass the result of this function to
+// TF_RegisterKernelBuilder, which will take ownership of the pointer. If, for
+// some reason, the kernel builder will not be registered, the caller should
+// delete it with TF_DeleteKernelBuilder.
+TF_CAPI_EXPORT extern TF_KernelBuilder* TF_NewKernelBuilder(
+ const char* op_name, const char* device_name,
+ void* (*create_func)(TF_OpKernelConstruction*),
+ void (*compute_func)(void*, TF_OpKernelContext*),
+ void (*delete_func)(void*));
+
+// Register the given kernel builder with the TensorFlow runtime. If
+// registration fails, the given status will be populated.
+//
+// This call takes ownership of the `builder` pointer.
+TF_CAPI_EXPORT extern void TF_RegisterKernelBuilder(const char* kernel_name,
+ TF_KernelBuilder* builder,
+ TF_Status* status);
+
+// Deletes the given TF_KernelBuilder. This should be called only if the kernel
+// 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
+
+#endif // TENSORFLOW_C_KERNELS_H_
diff --git a/tensorflow/c/kernels_test.cc b/tensorflow/c/kernels_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0d2954717e7a83c102a35815809a554e3a917e07
--- /dev/null
+++ b/tensorflow/c/kernels_test.cc
@@ -0,0 +1,231 @@
+/* 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/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"
+#include "tensorflow/core/framework/op_kernel.h"
+#include "tensorflow/core/framework/types.h"
+#include "tensorflow/core/lib/core/status_test_util.h"
+#include "tensorflow/core/platform/test.h"
+
+struct MyCustomKernel {
+ bool created;
+ bool compute_called;
+};
+
+static bool delete_called = false;
+
+static void* MyCreateFunc(TF_OpKernelConstruction* ctx) {
+ struct MyCustomKernel* s = new struct MyCustomKernel;
+ s->created = true;
+ s->compute_called = false;
+ return s;
+}
+
+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) {
+ struct MyCustomKernel* s = static_cast(kernel);
+ EXPECT_TRUE(s->created);
+ EXPECT_TRUE(s->compute_called);
+ delete_called = true;
+ 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 = "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);
+
+ {
+ TF_Status* status = TF_NewStatus();
+ TF_RegisterKernelBuilder(kernel_name, builder, status);
+ EXPECT_EQ(TF_OK, TF_GetCode(status));
+ TF_Buffer* buf = TF_GetRegisteredKernelsForOp(op_name, status);
+ EXPECT_EQ(TF_OK, TF_GetCode(status));
+ KernelList list;
+ list.ParseFromArray(buf->data, buf->length);
+ ASSERT_EQ(1, list.kernel_size());
+ ASSERT_EQ(device_name, list.kernel(0).device_type());
+ TF_DeleteBuffer(buf);
+ TF_DeleteStatus(status);
+ }
+
+ {
+ 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")
+ .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);
+
+ {
+ 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());
+
+ 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()());
+ }
+}
+
+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/c/test_op1.cc b/tensorflow/c/test_op1.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b22cc9aef2b344282f45340ff12ee849935a26f9
--- /dev/null
+++ b/tensorflow/c/test_op1.cc
@@ -0,0 +1,23 @@
+/* Copyright 2016 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/core/framework/op.h"
+#include "tensorflow/core/framework/op_kernel.h"
+
+namespace tensorflow {
+
+REGISTER_OP("TestCApi1").Doc(R"doc(Used to test C API)doc");
+
+} // namespace tensorflow
diff --git a/tensorflow/cc/BUILD b/tensorflow/cc/BUILD
index c18b07603ae3841d3581741ab5a43f2e8b628356..a09becc49b10d2c58f98fbcc11df5190f794c1d4 100644
--- a/tensorflow/cc/BUILD
+++ b/tensorflow/cc/BUILD
@@ -170,6 +170,7 @@ cc_library_with_android_deps(
"//tensorflow/core:framework",
"//tensorflow/core:lib",
"//tensorflow/core:protos_all_cc",
+ "@com_google_absl//absl/strings",
],
)
@@ -488,6 +489,7 @@ tf_gen_op_wrappers_cc(
"image_ops",
"io_ops",
"linalg_ops",
+ "list_ops",
"logging_ops",
"lookup_ops",
"manip_ops",
@@ -516,6 +518,8 @@ tf_gen_op_wrappers_cc(
":array_ops",
":const_op",
":math_ops",
+ "//tensorflow/cc:ops",
+ "//tensorflow/cc:scope",
],
)
diff --git a/tensorflow/cc/framework/scope.cc b/tensorflow/cc/framework/scope.cc
index 6abc9e268e3ac97379954a34017ddffa010db67f..81785b2d89b3d36b46992b7ae376b5175a806027 100644
--- a/tensorflow/cc/framework/scope.cc
+++ b/tensorflow/cc/framework/scope.cc
@@ -95,6 +95,7 @@ Scope::Impl::Impl(const Scope& other, Tags::ScopeName, const string& name,
kernel_label_(other.impl()->kernel_label_),
device_(other.impl()->device_),
assigned_device_(other.impl()->assigned_device_),
+ xla_cluster_(other.impl()->xla_cluster_),
colocation_constraints_(other.impl()->colocation_constraints_),
disable_shape_inference_(other.impl()->disable_shape_inference_) {}
@@ -112,6 +113,7 @@ Scope::Impl::Impl(const Scope& other, Tags::OpName, const string& name,
kernel_label_(other.impl()->kernel_label_),
device_(other.impl()->device_),
assigned_device_(other.impl()->assigned_device_),
+ xla_cluster_(other.impl()->xla_cluster_),
colocation_constraints_(other.impl()->colocation_constraints_),
disable_shape_inference_(other.impl()->disable_shape_inference_) {}
@@ -135,6 +137,7 @@ Scope::Impl::Impl(const Scope& other, Tags::ControlDeps,
kernel_label_(other.impl()->kernel_label_),
device_(other.impl()->device_),
assigned_device_(other.impl()->assigned_device_),
+ xla_cluster_(other.impl()->xla_cluster_),
colocation_constraints_(other.impl()->colocation_constraints_),
disable_shape_inference_(other.impl()->disable_shape_inference_) {}
@@ -167,6 +170,7 @@ Scope::Impl::Impl(const Scope& other, Tags::SingleUseScope,
kernel_label_(other.impl()->kernel_label_),
device_(other.impl()->device_),
assigned_device_(other.impl()->assigned_device_),
+ xla_cluster_(other.impl()->xla_cluster_),
colocation_constraints_(other.impl()->colocation_constraints_),
disable_shape_inference_(other.impl()->disable_shape_inference_) {}
@@ -183,6 +187,7 @@ Scope::Impl::Impl(const Scope& other, Tags::ExitOnError)
kernel_label_(other.impl()->kernel_label_),
device_(other.impl()->device_),
assigned_device_(other.impl()->assigned_device_),
+ xla_cluster_(other.impl()->xla_cluster_),
colocation_constraints_(other.impl()->colocation_constraints_),
disable_shape_inference_(other.impl()->disable_shape_inference_) {}
@@ -200,6 +205,7 @@ Scope::Impl::Impl(const Scope& other, Tags::KernelLabel,
kernel_label_(kernel_label),
device_(other.impl()->device_),
assigned_device_(other.impl()->assigned_device_),
+ xla_cluster_(other.impl()->xla_cluster_),
colocation_constraints_(other.impl()->colocation_constraints_),
disable_shape_inference_(other.impl()->disable_shape_inference_) {}
@@ -217,6 +223,7 @@ Scope::Impl::Impl(const Scope& other, Tags::Colocate,
kernel_label_(other.impl()->kernel_label_),
device_(other.impl()->device_),
assigned_device_(other.impl()->assigned_device_),
+ xla_cluster_(other.impl()->xla_cluster_),
colocation_constraints_(
clear_colocations
? std::unordered_set()
@@ -237,6 +244,25 @@ Scope::Impl::Impl(const Scope& other, Tags::AssignedDevice,
kernel_label_(other.impl()->kernel_label_),
device_(other.impl()->device_),
assigned_device_(assigned_device),
+ xla_cluster_(other.impl()->xla_cluster_),
+ colocation_constraints_(other.impl()->colocation_constraints_),
+ disable_shape_inference_(other.impl()->disable_shape_inference_) {}
+
+Scope::Impl::Impl(const Scope& other, Tags::XlaCluster,
+ const string& xla_cluster)
+ : graph_(other.impl()->graph_),
+ status_(other.impl()->status_),
+ name_map_(other.impl()->name_map_),
+ refiner_(other.impl()->refiner_),
+ scope_used_(other.impl()->scope_used_),
+ control_deps_(other.impl()->control_deps_),
+ name_(other.impl()->name_),
+ op_name_(other.impl()->op_name_),
+ exit_on_error_(other.impl()->exit_on_error_),
+ kernel_label_(other.impl()->kernel_label_),
+ device_(other.impl()->device_),
+ assigned_device_(other.impl()->assigned_device_),
+ xla_cluster_(xla_cluster),
colocation_constraints_(other.impl()->colocation_constraints_),
disable_shape_inference_(other.impl()->disable_shape_inference_) {}
@@ -326,6 +352,9 @@ void Scope::UpdateBuilder(NodeBuilder* builder) const {
if (!impl()->assigned_device_.empty()) {
builder->AssignedDevice(impl()->assigned_device_);
}
+ if (!impl()->xla_cluster_.empty()) {
+ builder->XlaCluster(impl()->xla_cluster_);
+ }
}
string Scope::Impl::GetUniqueName(const string& prefix,
@@ -388,7 +417,7 @@ Scope Scope::NewSubScope(const string& child_scope_name) const {
false /* copy_names */));
}
-Scope Scope::WithOpName(const string& op_name) const {
+Scope Scope::WithOpNameImpl(const string& op_name) const {
if (impl()->single_use_scope()) {
UpdateStatus(errors::InvalidArgument("Cannot set op name ", op_name,
" on this scope"));
@@ -425,6 +454,10 @@ Scope Scope::WithAssignedDevice(const string& assigned_device) const {
return Scope(new Impl(*this, Impl::Tags::AssignedDevice(), assigned_device));
}
+Scope Scope::WithXlaCluster(const string& xla_cluster) const {
+ return Scope(new Impl(*this, Impl::Tags::XlaCluster(), xla_cluster));
+}
+
Scope Scope::ColocateWith(const Operation& op) const {
return Scope(new Impl(*this, Impl::Tags::Colocate(), op,
/* clear_colocations */ false));
diff --git a/tensorflow/cc/framework/scope.h b/tensorflow/cc/framework/scope.h
index e307d8989b6647dfac8d2691ed2171c86b7f3a7c..0a75f23725c143e6b22ee6dffae1428ed8209fe8 100644
--- a/tensorflow/cc/framework/scope.h
+++ b/tensorflow/cc/framework/scope.h
@@ -22,6 +22,7 @@ limitations under the License.
#include
#include
+#include "absl/strings/str_cat.h"
#include "tensorflow/cc/framework/ops.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/lib/gtl/array_slice.h"
@@ -69,8 +70,9 @@ struct CompositeOpScopes;
/// // W will be named "linear/W"
/// auto W = Variable(linear.WithOpName("W"),
/// {2, 2}, DT_FLOAT);
-/// // b will be named "linear/b"
-/// auto b = Variable(linear.WithOpName("b"),
+/// // b will be named "linear/b_3"
+/// int idx = 3;
+/// auto b = Variable(linear.WithOpName("b_", idx),
/// {2}, DT_FLOAT);
/// auto x = Const(linear, {...}); // name: "linear/Const"
/// auto m = MatMul(linear, x, W); // name: "linear/MatMul"
@@ -113,8 +115,11 @@ class Scope {
Scope NewSubScope(const string& child_scope_name) const;
/// Return a new scope. All ops created within the returned scope will have
- /// names of the form `name/op_name[_suffix]`.
- Scope WithOpName(const string& op_name) const;
+ /// names of the form `name/StrCat(fragments...)[_suffix]`
+ template
+ Scope WithOpName(Ty... fragments) const {
+ return WithOpNameImpl(absl::StrCat(fragments...));
+ }
/// Return a new scope. All ops created within the returned scope will have as
/// control dependencies the union of operations in the control_deps vector
@@ -137,6 +142,10 @@ class Scope {
/// their assigned device set to `assigned_device`.
Scope WithAssignedDevice(const string& assigned_device) const;
+ /// Returns a new scope. All ops created within the returned scope will have
+ /// their _XlaCluster attribute set to `xla_cluster`.
+ Scope WithXlaCluster(const string& xla_cluster) const;
+
/// Return a new scope. All ops created within the returned scope will be
/// co-located on the device where op is placed.
/// NOTE: This function is intended to be use internal libraries only for
@@ -227,6 +236,8 @@ class Scope {
// END_SKIP_DOXYGEN
private:
+ Scope WithOpNameImpl(const string& op_name) const;
+
friend class InternalScope;
std::unique_ptr impl_;
explicit Scope(Impl*);
diff --git a/tensorflow/cc/framework/scope_internal.h b/tensorflow/cc/framework/scope_internal.h
index 514e02e84146b6d95147d83182e5d9a07509cfa1..5db7eab2b819c2c5d8fc358953d4607848f1cba5 100644
--- a/tensorflow/cc/framework/scope_internal.h
+++ b/tensorflow/cc/framework/scope_internal.h
@@ -61,6 +61,7 @@ class Scope::Impl {
enum class KernelLabel;
enum class Colocate;
enum class AssignedDevice;
+ enum class XlaCluster;
};
Impl(Graph* graph, Status* status, NameMap* name_map, ShapeRefiner* refiner,
@@ -78,6 +79,7 @@ class Scope::Impl {
Impl(const Scope& other, Tags::Colocate, const Operation& colocate_with_op,
bool clear_colocations);
Impl(const Scope& other, Tags::AssignedDevice, const string& assigned_device);
+ Impl(const Scope& other, Tags::XlaCluster, const string& xla_cluster);
std::unordered_set GetColocationConstraints(
const Operation& colocate_with_op) const;
@@ -112,6 +114,7 @@ class Scope::Impl {
const string kernel_label_ = "";
const string device_ = "";
const string assigned_device_ = "";
+ const string xla_cluster_ = "";
const std::unordered_set colocation_constraints_;
// If true, Scope::DoShapeInference() always returns Status:OK().
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