4

I have build a very simple custom op zero_out.dll with bazel, it works when using python.

import tensorflow as tf
zero_out_module = tf.load_op_library('./zero_out.dll')
with tf.Session(''):
  zero_out_module.zero_out([[1, 2], [3, 4]]).eval()

However I have to run the inference using C++, is there any c++ api which has the similar function as tf.load_op_library, as it seems that a lot of registeration work has been done in tf.load_op_library, TF has not counterpart c++ API?

7oud
  • 75
  • 1
  • 8
  • Possible duplicate of [Dynamically load a function from a DLL](https://stackoverflow.com/questions/8696653/dynamically-load-a-function-from-a-dll). DLLs and this kind of functionality is handled through the Windows API. – Romen Sep 09 '19 at 16:15
  • @Romen It is not really a duplicate, as TensorFlow library loading does some special additional work of registering loaded ops/kernels. – jdehesa Sep 09 '19 at 16:38
  • @jdehsa, I think that may be a helpful link anyways then. I can't find an equivalent method either for loading ops from a library in the C++ TensorFlow API. – Romen Sep 09 '19 at 16:42

1 Answers1

3

While there does not seem to be a public API for that in C++, the library loading functions are exposed in the TensorFlow API for C (which is the API that tf.load_library uses). There is no "nice" documentation for it, but you can find them in c/c_api.h:

// --------------------------------------------------------------------------
// Load plugins containing custom ops and kernels

// TF_Library holds information about dynamically loaded TensorFlow plugins.
typedef struct TF_Library TF_Library;

// Load the library specified by library_filename and register the ops and
// kernels present in that library.
//
// Pass "library_filename" to a platform-specific mechanism for dynamically
// loading a library. The rules for determining the exact location of the
// library are platform-specific and are not documented here.
//
// On success, place OK in status and return the newly created library handle.
// The caller owns the library handle.
//
// On failure, place an error status in status and return NULL.
TF_CAPI_EXPORT extern TF_Library* TF_LoadLibrary(const char* library_filename,
                                                 TF_Status* status);

// Get the OpList of OpDefs defined in the library pointed by lib_handle.
//
// Returns a TF_Buffer. The memory pointed to by the result is owned by
// lib_handle. The data in the buffer will be the serialized OpList proto for
// ops defined in the library.
TF_CAPI_EXPORT extern TF_Buffer TF_GetOpList(TF_Library* lib_handle);

// Frees the memory associated with the library handle.
// Does NOT unload the library.
TF_CAPI_EXPORT extern void TF_DeleteLibraryHandle(TF_Library* lib_handle);

These functions do actually call C++ code (see source in c/c_api.cc). However, the called functions, defined in core/framework/load_library.cc does not have a header to include. The workaround to use it in C++ code, which they use in c/c_api.cc, is to declare the function yourself, and link the TensorFlow library.

namespace tensorflow {
// Helpers for loading a TensorFlow plugin (a .so file).
Status LoadLibrary(const char* library_filename, void** result,
                   const void** buf, size_t* len);
}

As far as I can tell there is no API to unload the library. The C API allows you only to delete the library handle object. This done just by freeing the pointer, but if you want to avoid trouble you should probably use the freeing function given by TensorFlow, tensorflow::port:free, declared in core/platform/mem.h. Again, if you cannot not or don't want to include that, you can declare the function yourself and it should work as well.

namespace tensorflow {
namespace port {
void Free(void* ptr);
}
}
jdehesa
  • 58,456
  • 7
  • 77
  • 121
  • very useful info! I try to load lib using `TF_LoadLibrary` `#include "c_api.h"` `TF_Status* status_load = TF_NewStatus();` `TF_Library* lib_handle = TF_LoadLibrary("C:\\custom_op\\zero_out.dll", status_load);` `cout << "message: " << TF_Message(status_load) << endl;` But it shows `zero_out.dll not found`. BTW, I build the TF lib and the custom op dll using bazel in Windows. It seems that some dependencies are not found, I have placed tensorflow.dll besides zero_out.dll – 7oud Sep 10 '19 at 02:24
  • @7oud Strange, but I haven't tried these myself so I don't know what issues could arise. If you look at the impl of `LoadLibrary` for Windows, in [`core/platform/windows/env.cc`](https://github.com/tensorflow/tensorflow/blob/v1.14.0/tensorflow/core/platform/windows/env.cc#L157-L170), it seems you should indeed use an absolute path (as `LOAD_WITH_ALTERED_SEARCH_PATH` is used)... but it seems that the error you get really just means that [`LoadLibraryExW`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw) failed, so it could be something else. – jdehesa Sep 10 '19 at 09:13
  • @jdejesa I find the `python36.dll` is needed, even thought it is not used. Now `TF_Code code = TF_GetCode(status_load);` the return code is `TF_OK`, then I use `TF_Buffer op_list_buf = TF_GetOpList(lib_handle);` to get op list, but `op_list_buf.length` returns 0, could you give some code snippet to get the interface function in custom op lib? – 7oud Sep 12 '19 at 09:36
  • @7oud Strange that you need Python dll, the point of the C lib is to be self-contained. Anyway, about `TF_GetOpList`, I haven't used these myself, so I'm not sure. You are supposed to make an [`OpList`](https://github.com/tensorflow/tensorflow/blob/v1.14.0/tensorflow/core/framework/op_def.proto#L167-L170) mesasge from the `data` in the `TF_Buffer` (e.g. with [`ParseFromArray`](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message_lite#MessageLite.ParseFromArray)) - if the length is zero, I guess it's an empty list. May be a problem registering the op? – jdehesa Sep 12 '19 at 09:59
  • So, after loading library succeed, it should be getting the op function name, do you have some standard precedure to get the op function name, and call it? – 7oud Sep 12 '19 at 10:55
  • @7oud So, from C API, as I said, you should get the list of ops in the library with `TF_GetOpList`, interpreting the buffer as `OpList`, which you can traverse. Then you could use `TF_NewOperation` / `TF_FinishOperation` to create operations in a graph with C. The C++ way is demonstrated in [`core/user_ops/fact.cc`](https://github.com/tensorflow/tensorflow/blob/v1.14.0/tensorflow/core/user_ops/fact.cc), which generates a [`Fact`](https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/fact.html) class, but if you really need dynamic runtime library loading, I'm not sure that supports it. – jdehesa Sep 12 '19 at 12:41
  • you mentioned "dynamic runtime lib loading" means TF_loadlibray? is it the standard method of loading the custom op, any other method else? – 7oud Sep 12 '19 at 12:54
  • @7oud Well, the other way would be to add the ops as in the case of `Fact`, where it gets compiled in the library with the rest of TensorFlow. What I'm not sure is if you can have an op like `Fact` in a separate dll and simply link the program to it on compilation, I'm not sure if that is enough to ensure the op gets registered or if you need to do the same that [`LoadLibrary`](https://github.com/tensorflow/tensorflow/blob/v1.14.0/tensorflow/core/framework/load_library.cc) does (`ProcessRegistrations`, ...). There's not much doc about this, and C API seems the only clear API for DLL loading. – jdehesa Sep 12 '19 at 13:06
  • Yes, I just compiled the custom dll in the case of `Fact`, placed the `.cc` in the `core/user_ops/`, and compiled using bazel. then the separeted dll is generated, but without `.lib` file. So I think it maybe need to load dynamicly, just like using python tf.load_op_library. Anyway is there any method to build the custom op into tensorflow lib, in that way, no need to load additonal lib – 7oud Sep 12 '19 at 13:53
  • @7oud Yes, you can just add the code for your op somewhere, like in [`core/user_ops`](https://github.com/tensorflow/tensorflow/tree/v1.14.0/tensorflow/core/user_ops), and it should pick it up. If you prefer to have it in a different directory, you would need to edit [`core/BUILD`](https://github.com/tensorflow/tensorflow/blob/v1.14.0/tensorflow/core/BUILD), see for example how `user_ops_op_lib` is [defined](https://github.com/tensorflow/tensorflow/blob/v1.14.0/tensorflow/core/BUILD#L1262-L1271) and [used](https://github.com/tensorflow/tensorflow/blob/v1.14.0/tensorflow/core/BUILD#L1389). – jdehesa Sep 12 '19 at 14:11
  • I built tf and custom op on Ubu and I got the op name, the question is how to call the op, I didn't find example using c/cc. `TF_Library* lib_handle = TF_LoadLibrary(); TF_Buffer op_list_buf = TF_GetOpList(lib_handle); tensorflow::OpList op_list; op_list.ParseFromArray(op_list_buf.data, op_list_buf.length); cout << "oplist name = " << op_list.op(0).name() << endl;` – 7oud Sep 24 '19 at 02:20
  • could you answer in this [question](https://stackoverflow.com/q/58072347/9873377) – 7oud Sep 24 '19 at 02:39
  • Does it mean we need to install tensor-flow C API to work with tf.load_op_library('xxx.so') ? – fisakhan Jun 15 '21 at 15:39
  • 1
    @Fisa No, if you are using `tf.load_op_library` from Python, you don't need to do anything else, that is part of the normal TensorFlow distribution for Python. The C API is only for the case where you want to run a model using a different programming language, e.g. C or C++, and need to use operations from an external library (so you need a way to do the equivalent of `tf.load_op_library` in that language). – jdehesa Jun 15 '21 at 17:26
  • Thanks @jdehesa but don't think its like that. Please have a look at https://github.com/microsoft/Deep3DFaceReconstruction/issues/139 – fisakhan Jun 16 '21 at 07:39