-
Notifications
You must be signed in to change notification settings - Fork 157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[circle-interpreter] Introduce a library for C-Python interaction #14369
Conversation
This commit introduces a library for C-Python interaction. ONE-DCO-1.0-Signed-off-by: seongwoo <[email protected]>
This is Python side codes. from cffi import FFI
ffi = FFI()
ffi.cdef("""
typedef struct InterpreterWrapper InterpreterWrapper;
InterpreterWrapper *Interpreter_new(const uint8_t *data, const size_t data_size);
void Interpreter_delete(InterpreterWrapper *intp);
void Interpreter_interpret(InterpreterWrapper *intp);
void Interpreter_writeInputTensor(InterpreterWrapper *intp, const int input_idx, const void *data);
void Interpreter_readOutputTensor(InterpreterWrapper *intp, const int output_idx, void *output, size_t output_size);
""")
C = ffi.dlopen('./libcircle_interpreter_cffi.so')
import torch
class add(torch.nn.Module):
def __init__(self):
super().__init__()
def forward(self, x, y):
return x + y
def get_inputs(self):
return (torch.randn(3), torch.randn(3))
m = add()
import circle_exir
cm = circle_exir.convert(m, m.get_inputs())
model_data = ffi.from_buffer(cm.circle_binary)
import numpy as np
intp = C.Interpreter_new(model_data, len(cm.circle_binary))
inputs = m.get_inputs()
arr1 = inputs[0].numpy()
arr2 = inputs[1].numpy()
c_arr1 = ffi.from_buffer(arr1)
c_arr2 = ffi.from_buffer(arr2)
print (f'Input1: {arr1}')
print (f'Input2: {arr2}')
# Set input
C.Interpreter_writeInputTensor(intp, 0, c_arr1)
C.Interpreter_writeInputTensor(intp, 1, c_arr2)
# Interpret
C.Interpreter_interpret(intp)
# Retrieve output tensor info
from circle_schema import circle
model = circle.Model.Model.GetRootAsModel(cm.circle_binary, 0)
graph = model.Subgraphs(0)
out_tensors = [graph.Tensors(graph.Outputs(o)) for o in range(graph.OutputsLength())]
out_shapes = [t.ShapeAsNumpy() for t in out_tensors]
out_types = [t.Type() for t in out_tensors]
# Get output
result = np.empty(out_shapes[0], dtype=np.float32)
c_result = ffi.from_buffer(result)
C.Interpreter_readOutputTensor(intp, 0, c_result, 12)
print (f'Output: {result}')
# Free memory
C.Interpreter_delete(intp) python eval.py
Input1: [ 1.5746298 2.3252928 -1.2210928]
Input2: [0.46135813 0.6819443 0.09515246]
Output: [ 2.0359879 3.0072372 -1.1259403] |
Q) any plans for unit tests ? |
Please refer to #14376. |
{ | ||
const auto output_nodes = loco::output_nodes(_module->graph()); | ||
const auto output_node = | ||
loco::must_cast<const luci::CircleOutput *>(output_nodes.at(output_idx)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about throwing an exception if output_size != getTensorSize(output_node) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding an exception if output_size != getTensorSize(output_node) ?
Why doesn't writeInputTensor have data_size argument? readOutputTensor has output_size argument.
I'll add input data size argument and add an exception to both sides.
With patched codes, the error message prints like this. $ python eval.py
C++ Exception: Invalid input size: 12 != 2 |
// Function to retrieve the last error message. | ||
extern "C" const char *get_last_error(void) { return last_error_message.c_str(); } | ||
|
||
// Clear the last error message | ||
extern "C" void clear_last_error(void) { last_error_message.clear(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are these functions for? Is it used in Python-side? If so, could you show the Python codes too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the python codes.
def check_for_errors():
error_message = ffi.string(C.get_last_error()).decode('utf-8')
if error_message:
C.clear_last_error()
raise RuntimeError(f'C++ Exception: {error_message}')
# ..
try:
C.Interpreter_writeInputTensor(intp, 0, c_arr1, 123456)
check_for_errors()
except RuntimeError as e:
print(e)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
The interface seems a bit tricky to use, but I guess it may be wrapped by other functions in python.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This commit introduces a library for C-Python interaction.
Related: #14352
ONE-DCO-1.0-Signed-off-by: seongwoo [email protected]