Java/C API: Make them Android friendly.

Make the C library and JNI shared library targets Android friendly by linking
with the smaller android runtime when building with --config=android

Relatedly, strip all but the JNI symbols from libtensorflow_jni.so
(regardless of build configuration) to trim its size down (by roughly
50%).

Link in the Java libraries in the Android examples. The longer term intention
is to encourage use of the TensorFlow Java API in Android and do away with the
TensorFlowInferenceInterface class (and related JNI code) currently used in
Android. This will provide a single, more thoroughly tested API for use in all
Java settings - Android or not. An update to the Android example to switch
to this will come in a follow up change.

NOTES:
- For expediency of this change, the C API call: TF_LoadSessionFromSavedModel
  is not available when building for Android. I will look into fixing that
  separately.

- Linking in the JNI library required by the TensorFlow Java API results in
  a small increase (0.7%) in binary size of libtensorflow_demo.so

An unrelatedly, rename libtensorflow-jni.so to libtensorflow_jni.so
to be consistent with other shared libraries created in tensorflow.
Change: 144320074
This commit is contained in:
Asim Shankar 2017-01-12 06:53:31 -08:00 committed by TensorFlower Gardener
parent c4c927cb30
commit e8f2aad0c0
12 changed files with 110 additions and 35 deletions

View File

@ -6,6 +6,7 @@ licenses(["notice"]) # Apache 2.0
load(
"//tensorflow:tensorflow.bzl",
"tf_cc_test",
"tf_copts",
"tf_cuda_library",
"tf_custom_op_library",
)
@ -23,13 +24,19 @@ tf_cuda_library(
name = "c_api",
srcs = ["c_api.cc"],
hdrs = ["c_api.h"],
copts = tf_copts(),
visibility = ["//visibility:public"],
deps = [
deps = select({
"//tensorflow:android": [
"//tensorflow/core:android_tensorflow_lib_lite",
],
"//conditions:default": [
"//tensorflow/cc/saved_model:loader",
"//tensorflow/core:core_cpu",
"//tensorflow/core:framework",
"//tensorflow/core:lib",
],
}),
)
tf_cuda_library(

View File

@ -20,7 +20,9 @@ limitations under the License.
#include <memory>
#include <vector>
#ifndef __ANDROID__
#include "tensorflow/cc/saved_model/loader.h"
#endif
#include "tensorflow/core/common_runtime/shape_refiner.h"
#include "tensorflow/core/framework/log_memory.h"
#include "tensorflow/core/framework/node_def_util.h"
@ -1709,6 +1711,7 @@ TF_Session* TF_NewSession(TF_Graph* graph, const TF_SessionOptions* opt,
}
}
#ifndef __ANDROID__
TF_Session* TF_LoadSessionFromSavedModel(
const TF_SessionOptions* session_options, const TF_Buffer* run_options,
const char* export_dir, const char* const* tags, int tags_len,
@ -1762,6 +1765,7 @@ TF_Session* TF_LoadSessionFromSavedModel(
session->last_num_graph_nodes = graph->graph.num_node_ids();
return session;
}
#endif // __ANDROID__
void TF_CloseSession(TF_Session* s, TF_Status* status) {
status->status = s->session->Close();

View File

@ -835,6 +835,10 @@ typedef struct TF_Session TF_Session;
extern TF_Session* TF_NewSession(TF_Graph* graph, const TF_SessionOptions* opts,
TF_Status* status);
#ifndef __ANDROID__
// TODO(ashankar): Remove the __ANDROID__ guard. This will require ensuring that
// the tensorflow/cc/saved_model:loader build target is Android friendly.
// This function creates a new TF_Session (which is created on success) using
// `session_options`, and then initializes state (restoring tensors and other
// assets) using `run_options`.
@ -853,6 +857,7 @@ TF_Session* TF_LoadSessionFromSavedModel(
const TF_SessionOptions* session_options, const TF_Buffer* run_options,
const char* export_dir, const char* const* tags, int tags_len,
TF_Graph* graph, TF_Buffer* meta_graph_def, TF_Status* status);
#endif // __ANDROID__
// Close a session.
//

View File

@ -33,6 +33,7 @@ cc_library(
visibility = ["//visibility:public"],
deps = [
"//tensorflow/core:android_tensorflow_lib_lite",
"//tensorflow/java/src/main/native",
],
alwayslink = 1,
)

View File

@ -8,7 +8,7 @@ licenses(["notice"]) # Apache 2.0
java_library(
name = "tensorflow",
srcs = glob(["src/main/java/org/tensorflow/*.java"]),
data = [":libtensorflow-jni"],
data = [":libtensorflow_jni"],
visibility = ["//visibility:public"],
)
@ -78,18 +78,42 @@ java_test(
)
filegroup(
name = "libtensorflow-jni",
name = "libtensorflow_jni",
srcs = select({
"//tensorflow:darwin": [":libtensorflow-jni.dylib"],
"//conditions:default": [":libtensorflow-jni.so"],
"//tensorflow:darwin": [":libtensorflow_jni.dylib"],
"//conditions:default": [":libtensorflow_jni.so"],
}),
)
LINKER_VERSION_SCRIPT = ":config/version_script.lds"
LINKER_EXPORTED_SYMBOLS = ":config/exported_symbols.lds"
cc_binary(
name = "libtensorflow-jni.so",
name = "libtensorflow_jni.so",
# Set linker options to strip out anything except the JNI
# symbols from the library. This reduces the size of the library
# considerably (~50% as of January 2017).
linkopts = select({
"//tensorflow:darwin": [
"-Wl,-exported_symbols_list", # This line must be directly followed by LINKER_EXPORTED_SYMBOLS
LINKER_EXPORTED_SYMBOLS,
],
"//tensorflow:windows": [],
"//conditions:default": [
"-z defs",
"-s",
"-Wl,--version-script", # This line must be directly followed by LINKER_VERSION_SCRIPT
LINKER_VERSION_SCRIPT,
],
}),
linkshared = 1,
linkstatic = 1,
deps = ["//tensorflow/java/src/main/native"],
deps = [
"//tensorflow/java/src/main/native",
LINKER_VERSION_SCRIPT,
LINKER_EXPORTED_SYMBOLS,
],
)
genrule(
@ -111,8 +135,8 @@ cc_binary(
# is resolved, perhaps this workaround rule can be removed.
genrule(
name = "darwin-compat",
srcs = [":libtensorflow-jni.so"],
outs = ["libtensorflow-jni.dylib"],
srcs = [":libtensorflow_jni.so"],
outs = ["libtensorflow_jni.dylib"],
cmd = "cp $< $@",
output_to_bindir = 1,
)

View File

@ -37,7 +37,7 @@ Build the Java Archive (JAR) and native library:
```sh
bazel build -c opt \
//tensorflow/java:tensorflow \
//tensorflow/java:libtensorflow-jni
//tensorflow/java:libtensorflow_jni
```
### Maven
@ -86,8 +86,8 @@ bazel run -c opt //tensorflow/java/src/main/java/org/tensorflow/examples:label_i
./src/main/java/org/tensorflow/examples/LabelImage.java
```
- Make `libtensorflow.jar` and `libtensorflow-jni.so`
(`libtensorflow-jni.dylib` on OS X) available during execution. For example:
- Make `libtensorflow.jar` and `libtensorflow_jni.so`
(`libtensorflow_jni.dylib` on OS X) available during execution. For example:
```sh
java \

View File

@ -0,0 +1,3 @@
*Java_org_tensorflow_*
*JNI_OnLoad
*JNI_OnUnload

View File

@ -0,0 +1,11 @@
VERS_1.0 {
# Export JNI symbols.
global:
Java_*;
JNI_OnLoad;
JNI_OnUnload;
# Hide everything else.
local:
*;
};

View File

@ -24,7 +24,7 @@ public final class TensorFlow {
/** Load the TensorFlow runtime C library. */
static void init() {
System.loadLibrary("tensorflow-jni");
System.loadLibrary("tensorflow_jni");
}
static {

View File

@ -154,7 +154,7 @@ public class LabelImage {
}
// In the fullness of time, equivalents of the methods of this class should be auto-generated from
// the OpDefs linked into libtensorflow-jni.so. That would match what is done in other languages
// the OpDefs linked into libtensorflow_jni.so. That would match what is done in other languages
// like Python, C++ and Go.
static class GraphBuilder {
GraphBuilder(Graph g) {

View File

@ -2,26 +2,49 @@
# Java Native Interface (JNI) library intended for implementing the
# TensorFlow Java API using the TensorFlow C library.
package(default_visibility = ["//tensorflow/java:__pkg__"])
package(default_visibility = [
"//tensorflow/java:__pkg__",
# TODO(ashankar): Temporary hack for the Java API and
# //third_party/tensorflow/contrib/android:android_tensorflow_inference_jni
# to co-exist in a single shared library. However, the hope is that
# //third_party/tensorflow/contrib/android:android_tensorflow_jni can be
# removed once the Java API provides feature parity with it.
"//tensorflow/contrib/android:__pkg__",
])
licenses(["notice"]) # Apache 2.0
load("//tensorflow:tensorflow.bzl", "tf_cuda_library")
load("//tensorflow:tensorflow.bzl", "tf_cuda_library", "tf_copts")
tf_cuda_library(
name = "native",
srcs = glob(["*.cc"]) + [
srcs = glob(["*.cc"]) + select({
# The Android toolchain makes "jni.h" available in the include path.
# For non-Android toolchains, generate jni.h and jni_md.h.
"//tensorflow:android": [],
"//conditions:default": [
":jni.h",
":jni_md.h",
],
}),
hdrs = glob(["*.h"]),
includes = ["."],
copts = tf_copts(),
includes = select({
"//tensorflow:android": [],
"//conditions:default": ["."],
}),
deps = [
"//tensorflow/c:c_api",
] + select({
"//tensorflow:android": [
"//tensorflow/core:android_tensorflow_lib",
],
"//conditions:default": [
"//tensorflow/core:all_kernels",
"//tensorflow/core:direct_session",
"//tensorflow/core:ops",
],
}),
alwayslink = 1,
)
@ -29,15 +52,11 @@ tf_cuda_library(
# #include <jni.h>
# in the source headers work
# (in combination with the "includes" attribute of the tf_cuda_library rule
# above).
# above. Not needed when using the Android toolchain).
#
# Inspired from:
# https://github.com/bazelbuild/bazel/blob/f99a0543f8d97339d32075c7176b79f35be84606/src/main/native/BUILD
# but hopefully there is a simpler alternative to this.
#
# TODO(ashankar): This should not be necessary for Android builds as the
# toolchain makes <jni.h> available. Perhaps remove ":jni.h" and ":jni_md.h"
# from "srcs" and make these genrules a no-op when building for Android?
genrule(
name = "copy_jni_h",
srcs = ["@bazel_tools//tools/jdk:jni_header"],

View File

@ -14,6 +14,7 @@ limitations under the License.
==============================================================================*/
#include <stdarg.h>
#include <stdio.h>
#include "tensorflow/c/c_api.h"
#include "tensorflow/java/src/main/native/exception_jni.h"