Skip to content
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
040043d
Introduce kill switch mechanism for onnxruntime sessions (not ready yet)
alonre24 May 26, 2021
0961e66
Putting login in onnx backend (not ready yet)
alonre24 May 27, 2021
9d5fbf8
WIP
alonre24 May 27, 2021
22662bd
Refactor background workers + add support to kill switch in onnx (for…
alonre24 May 29, 2021
c3a45e9
Refactor - do not use rax, extend onnxRunSessions array whenever a ne…
alonre24 May 30, 2021
2684852
Refactor backends loading
alonre24 May 30, 2021
cd9baa1
Start testing - not finished
alonre24 May 31, 2021
5c09106
Support bool type tensor
alonre24 May 31, 2021
5d3dd2c
Support tensors of type bool. Add validation that a input value doesn…
alonre24 May 31, 2021
fa14217
Merge branch 'Support_BOOL_type_for_tensors' into ONNX_kill_switch
alonre24 May 31, 2021
04dac08
Support tensor of type bool in ONNX, Add tests for kill switch
alonre24 Jun 1, 2021
1d6b3ed
Add load time config for ONNX_TIMEOUT. Parallel tests seems not to work.
alonre24 Jun 1, 2021
ea3c174
Some fixes
alonre24 Jun 2, 2021
05c2a39
Merge master (resolve conflicts in backends.c)
alonre24 Jun 6, 2021
4bbfbcd
Remove debug print
alonre24 Jun 6, 2021
cd2936c
Merge master with updated changes of supporting tensor of type bool
alonre24 Jun 6, 2021
4aed8ca
Some fixes and documentation complement.
alonre24 Jun 6, 2021
6cd9652
Refactor load time config
alonre24 Jun 6, 2021
42059b8
Remove redundant include
alonre24 Jun 6, 2021
6c906aa
Merge branch 'master' into ONNX_kill_switch
alonre24 Jun 7, 2021
23749c4
PR fixes part 1: refactor config and run queue info files (and all pl…
alonre24 Jun 10, 2021
342afbb
Merge branch 'ONNX_kill_switch' of https://github.com/RedisAI/RedisAI…
alonre24 Jun 10, 2021
697faf9
linter...
alonre24 Jun 10, 2021
ee02cc0
Merge branch 'master' into ONNX_kill_switch
alonre24 Jun 10, 2021
1201cb2
linter...
alonre24 Jun 10, 2021
4360679
Merge branch 'master' into ONNX_kill_switch
alonre24 Jun 10, 2021
21737e6
More PR fixes, add the option to get the global run sessions array fr…
alonre24 Jun 10, 2021
73f2a91
Minor fixes
alonre24 Jun 10, 2021
6babc11
Add the ability to execute model through torch script - WIP
alonre24 Jun 13, 2021
3942d23
More PR fixes, among that:
alonre24 Jun 13, 2021
e9fed4f
Merge branch 'master' into ONNX_kill_switch
alonre24 Jun 13, 2021
349653c
Fix tests for the case that we run on GPU - since CPU queue always cr…
alonre24 Jun 13, 2021
af423a7
Update readies
alonre24 Jun 13, 2021
8231c53
Update readies + minor fixes
alonre24 Jun 14, 2021
fd8c672
PR fixes
alonre24 Jun 14, 2021
78da23e
Return error if onnx is executed in a non async manner (via gears for…
alonre24 Jun 14, 2021
a67da55
Merge branch 'master' into run_model_from_script
alonre24 Jun 14, 2021
3545f7e
Merge branch 'ONNX_kill_switch' into run_model_from_script
alonre24 Jun 14, 2021
ab505ba
basic test passes - running torch model from torch script is enabled.
alonre24 Jun 15, 2021
c4d8b55
Extend tests to include onnx and tf as well.
alonre24 Jun 15, 2021
fedc508
Fix device id - always use -1 when creating tensors for default CPU.
alonre24 Jun 16, 2021
dd0d797
Resolve conflicts after merging master
alonre24 Jun 16, 2021
56f815f
Remove test added for debug
alonre24 Jun 16, 2021
fbee927
Remove debug additions and add comments and documentation.
alonre24 Jun 16, 2021
d57e598
Change device id of default CPU to -1 in RDB loading as well.
alonre24 Jun 17, 2021
525acc4
Fix and test error raising when a redis torch script operation fails.
alonre24 Jun 17, 2021
e1b3745
Some PR fixes.
alonre24 Jun 17, 2021
4869acf
Update device_id to -1 in older rdb versions
alonre24 Jun 20, 2021
f02e0b5
Merge branch 'master' into run_model_from_script
alonre24 Jun 20, 2021
d47d04a
- Move ownership on the output tensor to torch instead of copying it.
alonre24 Jun 21, 2021
9d6b409
Merge branch 'run_model_from_script' of https://github.com/RedisAI/Re…
alonre24 Jun 21, 2021
8a7cac9
Added comment
alonre24 Jun 22, 2021
10988d3
Merge branch 'master' into run_model_from_script
alonre24 Jun 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ redis> AI.TENSORGET result{tag} VALUES
```

### Redis Commands support.
In RedisAI TorchScript now supports simple (non-blocking) Redis commnands via the `redis.execute` API. The following (usless) script gets a key name (`x{1}`), and an `int` value (3). First, the script `SET`s the value in the key. Next, the script `GET`s the value back from the key, and sets it in a tensor which is eventually stored under the key 'y{1}'. Note that the inputs are `str` and `int`. The script sets and gets the value and set it into a tensor.
RedisAI TorchScript now supports simple (non-blocking) Redis commands via the `redis.execute` API. The following (useless) script gets a key name (`x{1}`), and an `int` value (3). First, the script `SET`s the value in the key. Next, the script `GET`s the value back from the key, and sets it in a tensor which is eventually stored under the key 'y{1}'. Note that the inputs are `str` and `int`. The script sets and gets the value and set it into a tensor.

```
def redis_int_to_tensor(redis_value: int):
Expand All @@ -624,6 +624,30 @@ redis> AI.TENSORGET y{1} VALUES
1) (integer) 3
```

### RedisAI model execution support.
RedisAI TorchScript also supports executing models which are stored in RedisAI by calling `redisAI.model_execute` command.
The command receives 3 inputs:
1. model name (string)
2. model inputs (List of torch.Tensor)
3. number of model outputs (int)
Return value - the model execution output tensors (List of torch.Tensor)
The following script creates two tensors, and executes the (tensorflow) model which is stored under the name 'tf_mul{1}' with these two tensors as inputs.
```
def test_model_execute(keys:List[str]):
a = torch.tensor([[2.0, 3.0], [2.0, 3.0]])
b = torch.tensor([[2.0, 3.0], [2.0, 3.0]])
return redisAI.model_execute(keys[0], [a, b], 1) # assume keys[0] is the model name stored in RedisAI.
```
```
redis> AI.SCRIPTEXECUTE redis_scripts{1} test_model_execute KEYS 1 {1} LIST_INPUTS 1 tf_mul{1} OUTPUTS 1 y{1}
OK
redis> AI.TENSORGET y{1} VALUES
1) (float) 4
2) (float) 9
3) (float) 4
4) (float) 9
```

!!! warning "Intermediate memory overhead"
The execution of scripts may generate intermediate tensors that are not allocated by the Redis allocator, but by whatever allocator is used in the backends (which may act on main memory or GPU memory, depending on the device), thus not being limited by `maxmemory` configuration settings of Redis.

Expand Down
30 changes: 0 additions & 30 deletions src/backends/backedns_api.h

This file was deleted.

46 changes: 41 additions & 5 deletions src/backends/backends.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "redismodule.h"
#include "config/config.h"
#include "execution/background_workers.h"
#include "execution/execution_contexts/modelRun_ctx.h"

static bool _ValidateFuncExists(RedisModuleCtx *ctx, void *func_ptr, const char *func_name,
const char *backend_name, const char *path) {
Expand All @@ -40,6 +41,7 @@ static bool _ValidateFuncExists(RedisModuleCtx *ctx, void *func_ptr, const char
*/
int RAI_ExportFunc(const char *func_name, void **targetFuncPtr) {

// Retrieve info from RedisAI internals.
if (strcmp("GetThreadId", func_name) == 0) {
*targetFuncPtr = BGWorker_GetThreadId;
} else if (strcmp("GetNumThreadsPerQueue", func_name) == 0) {
Expand All @@ -48,6 +50,40 @@ int RAI_ExportFunc(const char *func_name, void **targetFuncPtr) {
*targetFuncPtr = Config_GetModelExecutionTimeout;
} else if (strcmp("GetThreadsCount", func_name) == 0) {
*targetFuncPtr = BGWorker_GetThreadsCount;

// Export RedisAI low level API functions.
} else if (strcmp("RedisAI_InitError", func_name) == 0) {
*targetFuncPtr = RAI_InitError;
} else if (strcmp("RedisAI_FreeError", func_name) == 0) {
*targetFuncPtr = RAI_FreeError;
} else if (strcmp("RedisAI_GetError", func_name) == 0) {
*targetFuncPtr = RAI_GetError;
} else if (strcmp("RedisAI_TensorCreateFromDLTensor", func_name) == 0) {
*targetFuncPtr = RAI_TensorCreateFromDLTensor;
} else if (strcmp("RedisAI_TensorGetDLTensor", func_name) == 0) {
*targetFuncPtr = RAI_TensorGetDLTensor;
} else if (strcmp("RedisAI_TensorGetShallowCopy", func_name) == 0) {
*targetFuncPtr = RAI_TensorGetShallowCopy;
} else if (strcmp("RedisAI_TensorFree", func_name) == 0) {
*targetFuncPtr = RAI_TensorFree;
} else if (strcmp("RedisAI_GetModelFromKeyspace", func_name) == 0) {
*targetFuncPtr = RAI_GetModelFromKeyspace;
} else if (strcmp("RedisAI_ModelRunCtxCreate", func_name) == 0) {
*targetFuncPtr = RAI_ModelRunCtxCreate;
} else if (strcmp("RedisAI_ModelRunCtxAddInput", func_name) == 0) {
*targetFuncPtr = RAI_ModelRunCtxAddInput;
} else if (strcmp("RedisAI_ModelRunCtxNumOutputs", func_name) == 0) {
*targetFuncPtr = RAI_ModelRunCtxNumOutputs;
} else if (strcmp("RedisAI_ModelRunCtxAddOutput", func_name) == 0) {
*targetFuncPtr = RAI_ModelRunCtxAddOutput;
} else if (strcmp("RedisAI_ModelRunCtxOutputTensor", func_name) == 0) {
*targetFuncPtr = RAI_ModelRunCtxOutputTensor;
} else if (strcmp("RedisAI_ModelRunCtxFree", func_name) == 0) {
*targetFuncPtr = RAI_ModelRunCtxFree;
} else if (strcmp("RedisAI_ModelRun", func_name) == 0) {
*targetFuncPtr = RAI_ModelRun;

// Export RedisModule API functions.
} else {
return RedisModule_GetApi(func_name, targetFuncPtr);
}
Expand Down Expand Up @@ -244,15 +280,15 @@ int RAI_LoadBackend_Torch(RedisModuleCtx *ctx, const char *path) {

RAI_LoadedBackend backend = {0}; // Initialize all the callbacks to NULL.

int (*init_backend)(int (*)(const char *, void *));
init_backend = (int (*)(int (*)(const char *, void *)))(unsigned long)dlsym(
int (*init_backend)(int (*)(const char *, void **));
init_backend = (int (*)(int (*)(const char *, void **)))(unsigned long)dlsym(
handle, "RAI_InitBackendTorch");
if (!_ValidateFuncExists(ctx, init_backend, "RAI_InitBackendTorch", "TORCH", path)) {
goto error;
}
// Here we use the input callback to export functions from Redis to the backend,
// by setting the backend's function pointers to the corresponding functions in Redis.
init_backend(RedisModule_GetApi);
// Here we use the input callback to export functions from Redis and Redis AI to the backend,
// by setting the backend's function pointers to the corresponding functions in Redis/RedisAI.
init_backend(RAI_ExportFunc);

backend.model_create =
(RAI_Model * (*)(RAI_Backend, const char *, RAI_ModelOpts, const char *, size_t,
Expand Down
70 changes: 70 additions & 0 deletions src/backends/backends_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#pragma once

#include <stdint.h>
#include "redismodule.h"

#ifdef BACKENDS_API_EXTERN
#define BACKENDS_API extern
#endif

#ifndef BACKENDS_API
#define BACKENDS_API
#endif

typedef struct RAI_Tensor RAI_Tensor;
typedef struct RAI_Model RAI_Model;
typedef struct RAI_ModelRunCtx RAI_ModelRunCtx;
typedef struct RAI_Error RAI_Error;

/**
* @return The internal id of RedisAI current working thread.
* id range is {0, ..., <threads_count>-1}. If this is called from a non
* RedisAI BG thread, return -1.
*/
BACKENDS_API long (*RedisAI_GetThreadId)(void);

/**
* @return The number of working threads in RedisAI. This number should be
* equal to the number of threads per queue (load time config) * number of devices
* registered in RedisAI (a new device is registered if a model is set to run on
* this device in AI.MODELSTORE command.
*/
BACKENDS_API uintptr_t (*RedisAI_GetThreadsCount)(void);

/**
* @return The number of working threads per device queue (load time config).
*/
BACKENDS_API long long (*RedisAI_GetNumThreadsPerQueue)(void);

/**
* @return The maximal number of milliseconds that a model run session should run
* before it is terminated forcefully (load time config).
* Currently supported only fo onnxruntime backend.
*/
BACKENDS_API long long (*RedisAI_GetModelExecutionTimeout)(void);

/**
* The following functions are part of RedisAI low level API (the full low level
* API is defined in redisai.h). For every function below named "RedisAI_X", its
* implementation can be found under the name "RAI_X" in RedisAI header files.
*/

BACKENDS_API int (*RedisAI_InitError)(RAI_Error **err);
BACKENDS_API void (*RedisAI_FreeError)(RAI_Error *err);
BACKENDS_API const char *(*RedisAI_GetError)(RAI_Error *err);

BACKENDS_API RAI_Tensor *(*RedisAI_TensorCreateFromDLTensor)(DLManagedTensor *dl_tensor);
BACKENDS_API DLTensor *(*RedisAI_TensorGetDLTensor)(RAI_Tensor *tensor);
BACKENDS_API RAI_Tensor *(*RedisAI_TensorGetShallowCopy)(RAI_Tensor *t);
BACKENDS_API void (*RedisAI_TensorFree)(RAI_Tensor *tensor);

BACKENDS_API RAI_ModelRunCtx *(*RedisAI_ModelRunCtxCreate)(RAI_Model *model);
BACKENDS_API int (*RedisAI_GetModelFromKeyspace)(RedisModuleCtx *ctx, RedisModuleString *keyName,
RAI_Model **model, int mode, RAI_Error *err);
BACKENDS_API int (*RedisAI_ModelRunCtxAddInput)(RAI_ModelRunCtx *mctx, const char *inputName,
RAI_Tensor *inputTensor);
BACKENDS_API int (*RedisAI_ModelRunCtxAddOutput)(RAI_ModelRunCtx *mctx, const char *outputName);
BACKENDS_API size_t (*RedisAI_ModelRunCtxNumOutputs)(RAI_ModelRunCtx *mctx);
BACKENDS_API RAI_Tensor *(*RedisAI_ModelRunCtxOutputTensor)(RAI_ModelRunCtx *mctx, size_t index);
BACKENDS_API void (*RedisAI_ModelRunCtxFree)(RAI_ModelRunCtx *mctx);
BACKENDS_API int (*RedisAI_ModelRun)(RAI_ModelRunCtx **mctx, long long n, RAI_Error *err);
34 changes: 30 additions & 4 deletions src/backends/libtorch_c/torch_c.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#define BACKENDS_API_EXTERN
#include "torch_c.h"
#include "torch/torch.h"
#include "backends/backends_api.h"
#include "redismodule.h"
#include "ATen/Functions.h"
#include "torch/csrc/jit/serialization/import.h"
Expand Down Expand Up @@ -157,14 +159,34 @@ at::ScalarType toScalarType(const DLDataType &dtype) {
torch::Tensor fromDLPack(const DLTensor *src) {
at::DeviceType device_type = getATenDeviceType(src->device.device_type);
at::ScalarType stype = toScalarType(src->dtype);
// torch::Device device(device_type, src->ctx.device_id);
torch::Device device(device_type, -1);
// torch::DeviceType device = device_type;
torch::Device device(device_type, src->device.device_id);
return torch::from_blob(src->data, at::IntArrayRef(src->shape, src->ndim),
at::IntArrayRef(src->strides, src->ndim),
torch::device(device).dtype(stype));
}

extern "C" void torchTensorFromRAITensor(RAI_Tensor *src, void *torch_tensor) {
DLTensor *dl_tensor = RedisAI_TensorGetDLTensor(src);
at::DeviceType device_type = getATenDeviceType(dl_tensor->device.device_type);
at::ScalarType stype = toScalarType(dl_tensor->dtype);
torch::Device device(device_type, dl_tensor->device.device_id);

// Capture the RAI_Tensor to be able to release it once torch is done with
// the tensor that we are about to create (to avoid copying of the blob).
auto free_tensor = [src](void *data) {
RedisAI_TensorFree(src);
};

// Create torch tensor with the tensor's blob, and send a deleter callback
// for torch to use to release the RAI_Tensor when it finishes.
*static_cast<torch::Tensor *>(torch_tensor) =
torch::Tensor(torch::from_blob(dl_tensor->data,
at::IntArrayRef(dl_tensor->shape, dl_tensor->ndim),
at::IntArrayRef(dl_tensor->strides, dl_tensor->ndim),
free_tensor,
torch::device(device).dtype(stype)));
}

struct ATenDLMTensor {
torch::Tensor handle;
DLManagedTensor tensor;
Expand All @@ -182,7 +204,7 @@ DLManagedTensor *toManagedDLPack(const torch::Tensor &src_) {
atDLMTensor->tensor.manager_ctx = atDLMTensor;
atDLMTensor->tensor.deleter = &deleter;
atDLMTensor->tensor.dl_tensor.data = src.data_ptr();
int64_t device_id = 0;
int64_t device_id = -1; // This should be used for the default 'CPU' device.
if (src.is_cuda()) {
device_id = src.get_device();
}
Expand All @@ -195,6 +217,10 @@ DLManagedTensor *toManagedDLPack(const torch::Tensor &src_) {
return &(atDLMTensor->tensor);
}

extern "C" DLManagedTensor *torchTensorPtrToManagedDLPack(const void *src) {
return toManagedDLPack(*static_cast<const torch::Tensor *>(src));
}

struct ModuleContext {
std::shared_ptr<torch::jit::script::Module> module;
std::shared_ptr<torch::jit::script::CompilationUnit> cu;
Expand Down
19 changes: 19 additions & 0 deletions src/backends/libtorch_c/torch_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,25 @@ size_t torchScript_FunctionArgumentCount(void *scriptCtx, size_t fn_index);
TorchScriptFunctionArgumentType torchScript_FunctionArgumentype(void *scriptCtx, size_t fn_index,
size_t arg_index);

/**
* @brief Creates a new dltensor representation from torch tensor, by taking
* ownership on the tensor and keeping it in the manager_context field. The tensor
* data will be freed by calling the deleter function on the manager context field.
* @param src - A pointer to torch tensor.
* @returns The newly created DLManaged tensor.
*/
DLManagedTensor *torchTensorPtrToManagedDLPack(const void *src);

/**
* @brief Creates a new torch tensor from a RedisAI tensor, by using its data
* and store it in torch_tensor pointer. Note that the ownership of the tensor
* is transferred to the torch tensor, and it will be released by calling the
* created deleter function, which is RAI_TensorFree
* @param src - the input RAI tensor
* @param torch_tensor - place holder for the newly created torch tensor.
*/
void torchTensorFromRAITensor(RAI_Tensor *src, void *torch_tensor);

#ifdef __cplusplus
}
#endif
Loading