From 1c677ea82ea567d3ea53151e7eed62584f6b5d4e Mon Sep 17 00:00:00 2001 From: alonre24 Date: Tue, 16 Feb 2021 17:47:00 +0200 Subject: [PATCH 1/5] Test and small refactor creating tensor from values through gears. --- src/tensor.c | 34 ++++++++-------------------------- src/tensor.h | 7 ++----- tests/flow/tests_withGears.py | 24 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/tensor.c b/src/tensor.c index 079614abc..c89dfb1d2 100644 --- a/src/tensor.c +++ b/src/tensor.c @@ -101,8 +101,7 @@ RAI_Tensor *RAI_TensorNew(void) { ret->len = LEN_UNKOWN; } -RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, int ndims, - int tensorAllocMode) { +RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, int ndims) { size_t dtypeSize = Tensor_DataTypeSize(dtype); if (dtypeSize == 0) { @@ -124,21 +123,7 @@ RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, in } DLContext ctx = (DLContext){.device_type = kDLCPU, .device_id = 0}; - void *data = NULL; - switch (tensorAllocMode) { - case TENSORALLOC_ALLOC: - data = RedisModule_Alloc(len * dtypeSize); - break; - case TENSORALLOC_CALLOC: - data = RedisModule_Calloc(len, dtypeSize); - break; - case TENSORALLOC_NONE: - /* shallow copy no alloc */ - default: - /* assume TENSORALLOC_NONE - shallow copy no alloc */ - break; - } + void *data = RedisModule_Alloc(len * dtypeSize); ret->tensor = (DLManagedTensor){.dl_tensor = (DLTensor){.ctx = ctx, .data = data, @@ -214,9 +199,9 @@ RAI_Tensor *_TensorCreateWithDLDataTypeAndRString(DLDataType dtype, size_t dtype return ret; } -RAI_Tensor *RAI_TensorCreate(const char *dataType, long long *dims, int ndims, int hasdata) { +RAI_Tensor *RAI_TensorCreate(const char *dataType, long long *dims, int ndims) { DLDataType dtype = RAI_TensorDataTypeFromString(dataType); - return RAI_TensorCreateWithDLDataType(dtype, dims, ndims, TENSORALLOC_ALLOC); + return RAI_TensorCreateWithDLDataType(dtype, dims, ndims); } #if 0 @@ -273,7 +258,7 @@ RAI_Tensor *RAI_TensorCreateByConcatenatingTensors(RAI_Tensor **ts, long long n) DLDataType dtype = RAI_TensorDataType(ts[0]); - RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims, TENSORALLOC_ALLOC); + RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims); for (long long i = 0; i < n; i++) { memcpy(RAI_TensorData(ret) + batch_offsets[i] * sample_size * dtype_size, @@ -300,7 +285,7 @@ RAI_Tensor *RAI_TensorCreateBySlicingTensor(RAI_Tensor *t, long long offset, lon DLDataType dtype = RAI_TensorDataType(t); - RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims, TENSORALLOC_ALLOC); + RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims); memcpy(RAI_TensorData(ret), RAI_TensorData(t) + offset * sample_size * dtype_size, len * sample_size * dtype_size); @@ -329,7 +314,7 @@ int RAI_TensorDeepCopy(RAI_Tensor *t, RAI_Tensor **dest) { DLDataType dtype = RAI_TensorDataType(t); - RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims, TENSORALLOC_ALLOC); + RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims); memcpy(RAI_TensorData(ret), RAI_TensorData(t), sample_size * dtype_size); *dest = ret; @@ -642,7 +627,6 @@ int RAI_parseTensorSetArgs(RedisModuleString **argv, int argc, RAI_Tensor **t, i const char *fmtstr; int datafmt = TENSOR_NONE; - int tensorAllocMode = TENSORALLOC_CALLOC; size_t ndims = 0; long long len = 1; long long *dims = (long long *)array_new(long long, 1); @@ -656,7 +640,6 @@ int RAI_parseTensorSetArgs(RedisModuleString **argv, int argc, RAI_Tensor **t, i remaining_args = argc - 1 - argpos; if (!strcasecmp(opt, "BLOB")) { datafmt = TENSOR_BLOB; - tensorAllocMode = TENSORALLOC_CALLOC; // if we've found the dataformat there are no more dimensions // check right away if the arity is correct if (remaining_args != 1 && enforceArity == 1) { @@ -669,7 +652,6 @@ int RAI_parseTensorSetArgs(RedisModuleString **argv, int argc, RAI_Tensor **t, i break; } else if (!strcasecmp(opt, "VALUES")) { datafmt = TENSOR_VALUES; - tensorAllocMode = TENSORALLOC_CALLOC; // if we've found the dataformat there are no more dimensions // check right away if the arity is correct if (remaining_args != len && enforceArity == 1) { @@ -699,7 +681,7 @@ int RAI_parseTensorSetArgs(RedisModuleString **argv, int argc, RAI_Tensor **t, i RedisModuleString *rstr = argv[argpos]; *t = _TensorCreateWithDLDataTypeAndRString(datatype, datasize, dims, ndims, rstr, error); } else { - *t = RAI_TensorCreateWithDLDataType(datatype, dims, ndims, tensorAllocMode); + *t = RAI_TensorCreateWithDLDataType(datatype, dims, ndims); } if (!(*t)) { array_free(dims); diff --git a/src/tensor.h b/src/tensor.h index 45062135c..452214f15 100644 --- a/src/tensor.h +++ b/src/tensor.h @@ -65,11 +65,10 @@ RAI_Tensor *RAI_TensorNew(void); * @param dataType string containing the numeric data type of tensor elements * @param dims n-dimensional array ( the dimension values are copied ) * @param ndims number of dimensions - * @param hasdata ( deprecated parameter ) * @return allocated RAI_Tensor on success, or NULL if the allocation * failed. */ -RAI_Tensor *RAI_TensorCreate(const char *dataType, long long *dims, int ndims, int hasdata); +RAI_Tensor *RAI_TensorCreate(const char *dataType, long long *dims, int ndims); /** * Allocate the memory and initialise the RAI_Tensor. Creates a tensor based on @@ -81,12 +80,10 @@ RAI_Tensor *RAI_TensorCreate(const char *dataType, long long *dims, int ndims, i * @param dtype DLDataType * @param dims n-dimensional array ( the dimension values are copied ) * @param ndims number of dimensions - * @param tensorAllocMode * @return allocated RAI_Tensor on success, or NULL if the allocation * failed. */ -RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, int ndims, - int tensorAllocMode); +RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, int ndims); /** * Allocate the memory for a new Tensor and copy data fom a tensor to it. diff --git a/tests/flow/tests_withGears.py b/tests/flow/tests_withGears.py index 320480a71..573821b0e 100644 --- a/tests/flow/tests_withGears.py +++ b/tests/flow/tests_withGears.py @@ -324,3 +324,27 @@ async def DAGRun_addOpsFromString(record): values = con.execute_command('AI.TENSORGET', 'test5_res{1}', 'VALUES') env.assertEqual(values, [b'4', b'9', b'4', b'9']) + +@skip_if_gears_not_loaded +def test_tensor_create_via_gears(env): + script = ''' + +import redisAI + +def TensorCreate_FromValues(record): + + tensor = redisAI.createTensorFromValues('DOUBLE', [2,2], [1.0, 2.0, 3.0, 4.0]) + redisAI.setTensorInKey('test1_res{1}', tensor) + return "test1_OK" + +GB("CommandReader").map(TensorCreate_FromValues).register(trigger="TensorCreate_FromValues_test1") + ''' + + con = env.getConnection() + ret = con.execute_command('rg.pyexecute', script) + env.assertEqual(ret, b'OK') + ret = con.execute_command('rg.trigger', 'TensorCreate_FromValues_test1') + env.assertEqual(ret[0], b'test1_OK') + + values = con.execute_command('AI.TENSORGET', 'test1_res{1}', 'VALUES') + env.assertEqual(values, [b'1', b'2', b'3', b'4']) From 93fbce4b277c9e982b496f201cb10c14d0ffa97c Mon Sep 17 00:00:00 2001 From: alonre24 Date: Wed, 17 Feb 2021 16:24:01 +0200 Subject: [PATCH 2/5] Test tensor create from blob. --- tests/flow/tests_withGears.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/flow/tests_withGears.py b/tests/flow/tests_withGears.py index 573821b0e..49fbaeeb5 100644 --- a/tests/flow/tests_withGears.py +++ b/tests/flow/tests_withGears.py @@ -336,8 +336,15 @@ def TensorCreate_FromValues(record): tensor = redisAI.createTensorFromValues('DOUBLE', [2,2], [1.0, 2.0, 3.0, 4.0]) redisAI.setTensorInKey('test1_res{1}', tensor) return "test1_OK" + +def TensorCreate_FromBlob(record): + tensor_blob = bytearray([5, 6, 7, 8]) + tensor = redisAI.createTensorFromBlob('INT8', [2,2], tensor_blob) + redisAI.setTensorInKey('test2_res{1}', tensor) + return "test2_OK" GB("CommandReader").map(TensorCreate_FromValues).register(trigger="TensorCreate_FromValues_test1") +GB("CommandReader").map(TensorCreate_FromBlob).register(trigger="TensorCreate_FromBlob_test2") ''' con = env.getConnection() @@ -348,3 +355,9 @@ def TensorCreate_FromValues(record): values = con.execute_command('AI.TENSORGET', 'test1_res{1}', 'VALUES') env.assertEqual(values, [b'1', b'2', b'3', b'4']) + + ret = con.execute_command('rg.trigger', 'TensorCreate_FromBlob_test2') + env.assertEqual(ret[0], b'test2_OK') + + values = con.execute_command('AI.TENSORGET', 'test2_res{1}', 'VALUES') + env.assertEqual(values, [5, 6, 7, 8]) From 076b70aaed0bfeec33a993b305438eafb806d813 Mon Sep 17 00:00:00 2001 From: alonre24 Date: Thu, 25 Feb 2021 15:27:24 +0200 Subject: [PATCH 3/5] Use RedisModule_Calloc to allocate tensor blob. --- src/tensor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tensor.c b/src/tensor.c index c89dfb1d2..889193b3a 100644 --- a/src/tensor.c +++ b/src/tensor.c @@ -123,7 +123,7 @@ RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, in } DLContext ctx = (DLContext){.device_type = kDLCPU, .device_id = 0}; - void *data = RedisModule_Alloc(len * dtypeSize); + void *data = RedisModule_Calloc(len, dtypeSize); ret->tensor = (DLManagedTensor){.dl_tensor = (DLTensor){.ctx = ctx, .data = data, From 16775f24baf049badb2ebb809341a4979239ef54 Mon Sep 17 00:00:00 2001 From: alonre24 Date: Sun, 28 Feb 2021 15:51:48 +0200 Subject: [PATCH 4/5] merge with master --- src/tensor.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tensor.c b/src/tensor.c index 889193b3a..80854cf68 100644 --- a/src/tensor.c +++ b/src/tensor.c @@ -122,10 +122,10 @@ RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, in strides[i] *= strides[i + 1] * shape[i + 1]; } - DLContext ctx = (DLContext){.device_type = kDLCPU, .device_id = 0}; + DLDevice device = (DLDevice){.device_type = kDLCPU, .device_id = 0}; void *data = RedisModule_Calloc(len, dtypeSize); - ret->tensor = (DLManagedTensor){.dl_tensor = (DLTensor){.ctx = ctx, + ret->tensor = (DLManagedTensor){.dl_tensor = (DLTensor){.device = device, .data = data, .ndim = ndims, .dtype = dtype, @@ -170,7 +170,7 @@ RAI_Tensor *_TensorCreateWithDLDataTypeAndRString(DLDataType dtype, size_t dtype strides[i] *= strides[i + 1] * shape[i + 1]; } - DLContext ctx = (DLContext){.device_type = kDLCPU, .device_id = 0}; + DLDevice device = (DLDevice){.device_type = kDLCPU, .device_id = 0}; size_t nbytes = len * dtypeSize; size_t blob_len; @@ -186,7 +186,7 @@ RAI_Tensor *_TensorCreateWithDLDataTypeAndRString(DLDataType dtype, size_t dtype RAI_HoldString(NULL, rstr); RAI_Tensor *ret = RAI_TensorNew(); - ret->tensor = (DLManagedTensor){.dl_tensor = (DLTensor){.ctx = ctx, + ret->tensor = (DLManagedTensor){.dl_tensor = (DLTensor){.device = device, .data = data, .ndim = ndims, .dtype = dtype, @@ -327,7 +327,7 @@ RAI_Tensor *RAI_TensorCreateFromDLTensor(DLManagedTensor *dl_tensor) { RAI_Tensor *ret = RAI_TensorNew(); ret->tensor = - (DLManagedTensor){.dl_tensor = (DLTensor){.ctx = dl_tensor->dl_tensor.ctx, + (DLManagedTensor){.dl_tensor = (DLTensor){.device = dl_tensor->dl_tensor.device, .data = dl_tensor->dl_tensor.data, .ndim = dl_tensor->dl_tensor.ndim, .dtype = dl_tensor->dl_tensor.dtype, From 5bef776c15d7a0ccdf35ac15606c2d0e943fac0e Mon Sep 17 00:00:00 2001 From: alonre24 Date: Mon, 1 Mar 2021 12:22:04 +0200 Subject: [PATCH 5/5] Use RM_Calloc for allocating tensor blob only when creating an empty tensor (for security reasons). Otherwise use RM_Alloc (for better performance). --- src/tensor.c | 53 ++++++++++++++++-------------------------- src/tensor.h | 4 +++- tests/flow/includes.py | 2 ++ 3 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/tensor.c b/src/tensor.c index 80854cf68..e4ae7f53e 100644 --- a/src/tensor.c +++ b/src/tensor.c @@ -99,9 +99,11 @@ RAI_Tensor *RAI_TensorNew(void) { RAI_Tensor *ret = RedisModule_Calloc(1, sizeof(*ret)); ret->refCount = 1; ret->len = LEN_UNKOWN; + return ret; } -RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, int ndims) { +RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, int ndims, + bool empty) { size_t dtypeSize = Tensor_DataTypeSize(dtype); if (dtypeSize == 0) { @@ -123,7 +125,15 @@ RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, in } DLDevice device = (DLDevice){.device_type = kDLCPU, .device_id = 0}; - void *data = RedisModule_Calloc(len, dtypeSize); + + // If we return an empty tensor, we initialize the data with zeros to avoid security + // issues. Otherwise, we only allocate without initializing (for better performance) + void *data; + if (empty) { + data = RedisModule_Calloc(len, dtypeSize); + } else { + data = RedisModule_Alloc(len * dtypeSize); + } ret->tensor = (DLManagedTensor){.dl_tensor = (DLTensor){.device = device, .data = data, @@ -199,27 +209,11 @@ RAI_Tensor *_TensorCreateWithDLDataTypeAndRString(DLDataType dtype, size_t dtype return ret; } +// Important note: the tensor data must be initialized after the creation. RAI_Tensor *RAI_TensorCreate(const char *dataType, long long *dims, int ndims) { DLDataType dtype = RAI_TensorDataTypeFromString(dataType); - return RAI_TensorCreateWithDLDataType(dtype, dims, ndims); -} - -#if 0 -void RAI_TensorMoveFrom(RAI_Tensor* dst, RAI_Tensor* src) { - if (--dst->refCount <= 0){ - RedisModule_Free(t->tensor.shape); - if (t->tensor.strides) { - RedisModule_Free(t->tensor.strides); - } - RedisModule_Free(t->tensor.data); - RedisModule_Free(t); - } - dst->tensor.ctx = src->tensor.ctx; - dst->tensor.data = src->tensor.data; - - dst->refCount = 1; + return RAI_TensorCreateWithDLDataType(dtype, dims, ndims, false); } -#endif RAI_Tensor *RAI_TensorCreateByConcatenatingTensors(RAI_Tensor **ts, long long n) { @@ -258,7 +252,7 @@ RAI_Tensor *RAI_TensorCreateByConcatenatingTensors(RAI_Tensor **ts, long long n) DLDataType dtype = RAI_TensorDataType(ts[0]); - RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims); + RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims, false); for (long long i = 0; i < n; i++) { memcpy(RAI_TensorData(ret) + batch_offsets[i] * sample_size * dtype_size, @@ -285,7 +279,7 @@ RAI_Tensor *RAI_TensorCreateBySlicingTensor(RAI_Tensor *t, long long offset, lon DLDataType dtype = RAI_TensorDataType(t); - RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims); + RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims, false); memcpy(RAI_TensorData(ret), RAI_TensorData(t) + offset * sample_size * dtype_size, len * sample_size * dtype_size); @@ -314,14 +308,14 @@ int RAI_TensorDeepCopy(RAI_Tensor *t, RAI_Tensor **dest) { DLDataType dtype = RAI_TensorDataType(t); - RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims); + RAI_Tensor *ret = RAI_TensorCreateWithDLDataType(dtype, dims, ndims, false); memcpy(RAI_TensorData(ret), RAI_TensorData(t), sample_size * dtype_size); *dest = ret; return 0; } -// Beware: this will take ownership of dltensor +// Beware: this will take ownership of dltensor. RAI_Tensor *RAI_TensorCreateFromDLTensor(DLManagedTensor *dl_tensor) { RAI_Tensor *ret = RAI_TensorNew(); @@ -404,19 +398,15 @@ int RAI_TensorSetValueFromLongLong(RAI_Tensor *t, long long i, long long val) { case 8: ((int8_t *)data)[i] = val; break; - break; case 16: ((int16_t *)data)[i] = val; break; - break; case 32: ((int32_t *)data)[i] = val; break; - break; case 64: ((int64_t *)data)[i] = val; break; - break; default: return 0; } @@ -425,19 +415,15 @@ int RAI_TensorSetValueFromLongLong(RAI_Tensor *t, long long i, long long val) { case 8: ((uint8_t *)data)[i] = val; break; - break; case 16: ((uint16_t *)data)[i] = val; break; - break; case 32: ((uint32_t *)data)[i] = val; break; - break; case 64: ((uint64_t *)data)[i] = val; break; - break; default: return 0; } @@ -681,7 +667,8 @@ int RAI_parseTensorSetArgs(RedisModuleString **argv, int argc, RAI_Tensor **t, i RedisModuleString *rstr = argv[argpos]; *t = _TensorCreateWithDLDataTypeAndRString(datatype, datasize, dims, ndims, rstr, error); } else { - *t = RAI_TensorCreateWithDLDataType(datatype, dims, ndims); + bool is_empty = (datafmt == TENSOR_NONE); + *t = RAI_TensorCreateWithDLDataType(datatype, dims, ndims, is_empty); } if (!(*t)) { array_free(dims); diff --git a/src/tensor.h b/src/tensor.h index 452214f15..1da2b548d 100644 --- a/src/tensor.h +++ b/src/tensor.h @@ -80,10 +80,12 @@ RAI_Tensor *RAI_TensorCreate(const char *dataType, long long *dims, int ndims); * @param dtype DLDataType * @param dims n-dimensional array ( the dimension values are copied ) * @param ndims number of dimensions + * @param empty True if creating an empty tensor (need to be initialized) * @return allocated RAI_Tensor on success, or NULL if the allocation * failed. */ -RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, int ndims); +RAI_Tensor *RAI_TensorCreateWithDLDataType(DLDataType dtype, long long *dims, int ndims, + bool empty); /** * Allocate the memory for a new Tensor and copy data fom a tensor to it. diff --git a/tests/flow/includes.py b/tests/flow/includes.py index fbe356990..31255c5d6 100755 --- a/tests/flow/includes.py +++ b/tests/flow/includes.py @@ -58,6 +58,8 @@ def send_and_disconnect(cmd, red): con = pool.get_connection(cmd[0]) ret = con.send_command(*cmd) con.disconnect() + # For making sure that Redis will have the time to exit cleanly. + time.sleep(1) return ret