Reduce the transformation steps of tensor information.
authorChanhee Lee <ch2102.lee@samsung.com>
Thu, 30 Sep 2021 11:18:48 +0000 (20:18 +0900)
committerYoungjae Shin <yj99.shin@samsung.com>
Thu, 28 Oct 2021 10:27:30 +0000 (19:27 +0900)
* Reduce the transformations of tensor information.

- Instead of using ArrayLists transformed from TensorInfo instances,
  TensorInfo instances are directly transferred to and used in JNI.

* Simplify null checking.

* Replace hard-code into constant variables.

* Replace GetMethodID functions.

* Rename check_null_with_free function.

* Refactor private functions.

* Add exception handling for JNI function calls.

* Adjust try-catch positions.

* Remove unnecessary functions.

* Add release functions for JNI objects.

Change-Id: Ibb0fc2671d6dfd02b3c66a3b4cc3dfc2278c8245

subprojects/libbeyond-android/src/main/java/com/samsung/android/beyond/inference/tensor/TensorHandler.java
subprojects/libbeyond-android/src/main/java/com/samsung/android/beyond/inference/tensor/TensorInfo.java
subprojects/libbeyond-android/src/main/jni/JNIHelper.cc
subprojects/libbeyond-android/src/main/jni/JNIHelper.h
subprojects/libbeyond-android/src/main/jni/inference/tensor/beyond-tensor_jni.cc
subprojects/libbeyond-android/src/main/jni/inference/tensor/beyond-tensor_jni.h

index 7d553cd726f27957892ab7c0532b616efefae627..3f3f0d74f28c7bd1ffbb2eef1e7733da111088ad 100644 (file)
@@ -81,17 +81,9 @@ public class TensorHandler {
     }
 
     public TensorSet allocateTensorSet(TensorInfo[] tensorInfoArray) {
-        List<Integer> dataTypeValues = new ArrayList<>();
-        List<List<Integer>> dimensionsList = new ArrayList<>();
-        int[] dataSizes = new int[tensorInfoArray.length];
-        if (transformTensorsInfo(tensorInfoArray, dataTypeValues, dataSizes, dimensionsList) == false) {
-            Log.e(TAG, "Fail to organize the information of tensors.");
-            return null;
-        }
-
         ByteBuffer[] bufferArray = new ByteBuffer[tensorInfoArray.length];
-        long tensorsInstance = allocateTensors(inferenceHandler.getNativeInstance(), dataTypeValues, dataSizes, dimensionsList, tensorInfoArray.length, bufferArray);
-        if (tensorsInstance == 0) {
+        long tensorsInstance = allocateTensors(inferenceHandler.getNativeInstance(), tensorInfoArray, tensorInfoArray.length, bufferArray);
+        if (tensorsInstance == 0L) {
             Log.e(TAG, "Fail to allocate the buffers of tensors.");
             return null;
         }
@@ -99,26 +91,6 @@ public class TensorHandler {
         return organizeTensorSet(tensorsInstance, tensorInfoArray, bufferArray);
     }
 
-    private boolean transformTensorsInfo(TensorInfo[] tensorInfoArray, List<Integer> dataTypeValues, int[] dataSizes, List<List<Integer>> dimensionsList) {
-        if (tensorInfoArray.length == 0) {
-            Log.e(TAG, "The given tensorInfoArray is empty.");
-            return false;
-        }
-
-        for (int i = 0; i < tensorInfoArray.length; i++) {
-            TensorInfo tensorInfo = tensorInfoArray[i];
-            dataTypeValues.add(tensorInfo.getDataType().getTypeValue());
-            dataSizes[i] = tensorInfo.getDataByteSize();
-            List<Integer> dimensions = new ArrayList<>();
-            for (int j = 0; j < tensorInfo.getRank(); j++) {
-                dimensions.add(tensorInfo.getDimensions()[j]);
-            }
-            dimensionsList.add(dimensions);
-        }
-
-        return true;
-    }
-
     private TensorSet organizeTensorSet(long tensorsInstance, TensorInfo[] tensorInfoArray, ByteBuffer[] bufferArray) {
         Tensor[] tensorArray = new Tensor[tensorInfoArray.length];
         for (int i = 0; i < tensorInfoArray.length; i++) {
@@ -155,7 +127,7 @@ public class TensorHandler {
 
     private native boolean getOutputTensorsInfo(long inferenceHandle, List<Integer> dataTypeValue, List<List<Integer>> dimensions);
 
-    private native long allocateTensors(long inferenceHandle, List<Integer> dataTypeValue, int[] dataSizes, List<List<Integer>> dimensions, int tensorsNumber, ByteBuffer[] bufferArray);
+    private native long allocateTensors(long inferenceHandle, TensorInfo[] tensorInfoArray, int numberOfTensors, ByteBuffer[] bufferArray);
 
     private native long getOutput(long inferenceHandle, ByteBuffer[] bufferArray, int numberOfTensors);
 
index 6907b7886a7d7a135fac1b6fa5e0b63580dcc7bc..a0424a278be3f492d93dece3cf9308aaaa3ab236 100644 (file)
@@ -4,11 +4,13 @@ import android.util.Log;
 
 import static com.samsung.android.beyond.inference.Option.TAG;
 
+import java.util.ArrayList;
+
 public class TensorInfo {
 
     private final DataType dataType;
 
-    private final int rank;
+    private int rank;
 
     private final int[] dimensions;
 
@@ -35,6 +37,10 @@ public class TensorInfo {
         return dataType;
     }
 
+    public int getDataTypeValue() {
+        return dataType.getTypeValue();
+    }
+
     public int getRank() {
         return rank;
     }
index 0d4931b5ae0906a92edded4b2b9c7440839df086..cf064b246e21e6cda7c4dc8816f54e4000de19c4 100644 (file)
@@ -75,3 +75,12 @@ int JNIHelper::CallVoidMethod(JNIEnv *env, jobject obj, const char *methodName,
 
     return 0;
 }
+
+void JNIHelper::checkEnvException(JNIEnv *env)
+{
+    if (env->ExceptionOccurred()) {
+        env->ExceptionDescribe();
+        env->ExceptionClear();
+        throw std::exception();
+    }
+}
index 6a3bd587a6ea3946ac5c43514c89a36fcb906b39..6c218d805cc95c39e05a8b9643800f33b67adf00 100644 (file)
@@ -47,9 +47,10 @@ public:
 public:
     // NOTE:
     // The following static functions is going to be extracted to common implementation later
-    // Every BeyonD Java API are able to be implemeneted using the following functions.
+    // Every BeyonD Java API are able to be implemented using the following functions.
     static void PrintException(JNIEnv *env, const char *funcname = nullptr, int lineno = -1, bool clear = true);
     static int CallVoidMethod(JNIEnv *env, jobject obj, const char *methodName, const char *signature, ...);
+    static void checkEnvException(JNIEnv *env);
 
 private:
     JNIHelper(void);
index 92069ca9d994538b3e9a8ffbdf3c314e21ad817c..72853d7427d5851442ce8e1dcf243cc7f3f47f6d 100644 (file)
 #include <cstdlib>
 #include <cerrno>
 
-#include <map>
-#include <utility>
-
-#include <android/looper.h>
-
 #include <beyond/platform/beyond_platform.h>
 #include <inference/beyond-inference_jni.h>
 
 #define GET_BEYOND_INFERENCE(obj) (static_cast<beyond::Inference *>((obj)->GetBeyonDInstance()))
 
+#define JAVA_UTIL_ARRAYLIST_CLASS "java/util/ArrayList"
+#define JAVA_LANG_INTEGER_CLASS "java/lang/Integer"
+#define JAVA_CLASS_CONSTRUCTOR "<init>"
+
 TensorJNI::TensorJNI(void)
 {
 }
@@ -85,29 +84,29 @@ jboolean TensorJNI::Java_com_samsung_beyond_TensorHandler_getOutputTensorsInfo(J
 
 int TensorJNI::getTensorsInfo(JNIEnv *env, const beyond_tensor_info *tensors_info, int num_tensors, jobject datatype_values, jobject dimensions_list)
 {
-    jclass list_class = env->FindClass("java/util/ArrayList");
-    if (list_class == NULL) {
-        ErrPrint("Fail to find java/util/List class.");
+    jclass list_class = env->FindClass(JAVA_UTIL_ARRAYLIST_CLASS);
+    if (list_class == nullptr) {
+        ErrPrint("Fail to find %s class.", JAVA_UTIL_ARRAYLIST_CLASS);
         return -EFAULT;
     }
-    jmethodID list_constructor_id = env->GetMethodID(list_class, "<init>", "()V");
-    if (list_constructor_id == NULL) {
+    jmethodID list_constructor_id = env->GetMethodID(list_class, JAVA_CLASS_CONSTRUCTOR, "()V");
+    if (list_constructor_id == nullptr) {
         ErrPrint("Fail to find an <init> method.");
         return -EFAULT;
     }
     jmethodID add_method_id = env->GetMethodID(list_class, "add", "(Ljava/lang/Object;)Z");
-    if (add_method_id == NULL) {
+    if (add_method_id == nullptr) {
         ErrPrint("Fail to find an add method.");
         return -EFAULT;
     }
 
-    jclass integer_class = env->FindClass("java/lang/Integer");
-    if (integer_class == NULL) {
-        ErrPrint("Fail to find java/util/Integer class.");
+    jclass integer_class = env->FindClass(JAVA_LANG_INTEGER_CLASS);
+    if (integer_class == nullptr) {
+        ErrPrint("Fail to find %s class.", JAVA_LANG_INTEGER_CLASS);
         return -EFAULT;
     }
-    jmethodID integer_constructor_id = env->GetMethodID(integer_class, "<init>", "(I)V");
-    if (integer_constructor_id == NULL) {
+    jmethodID integer_constructor_id = env->GetMethodID(integer_class, JAVA_CLASS_CONSTRUCTOR, "(I)V");
+    if (integer_constructor_id == nullptr) {
         ErrPrint("Fail to find an <init> method.");
         return -EFAULT;
     }
@@ -116,7 +115,7 @@ int TensorJNI::getTensorsInfo(JNIEnv *env, const beyond_tensor_info *tensors_inf
     for (int i = 0; i < num_tensors; i++) {
         int type_value = tensors_info[i].type;
         jobject datatype_value = env->NewObject(integer_class, integer_constructor_id, type_value);
-        if (datatype_value == NULL) {
+        if (datatype_value == nullptr) {
             ErrPrint("Fail to create an Integer instance.");
             return -EFAULT;
         }
@@ -127,7 +126,7 @@ int TensorJNI::getTensorsInfo(JNIEnv *env, const beyond_tensor_info *tensors_inf
         }
 
         jobject list = env->NewObject(list_class, list_constructor_id);
-        if (list == NULL) {
+        if (list == nullptr) {
             ErrPrint("Fail to create an ArrayList instance.");
             return -EFAULT;
         }
@@ -136,7 +135,7 @@ int TensorJNI::getTensorsInfo(JNIEnv *env, const beyond_tensor_info *tensors_inf
             int dimension_value = tensors_info[i].dims->data[j];
             jobject dimension_object = env->NewObject(integer_class, integer_constructor_id,
                                                       dimension_value);
-            if (dimension_object == NULL) {
+            if (dimension_object == nullptr) {
                 ErrPrint("Fail to create an Integer instance.");
                 return -EFAULT;
             }
@@ -156,12 +155,11 @@ int TensorJNI::getTensorsInfo(JNIEnv *env, const beyond_tensor_info *tensors_inf
     return 0;
 }
 
-jlong TensorJNI::Java_com_samsung_beyond_TensorHandler_allocateTensors(JNIEnv *env, jobject thiz, jlong inference_handle, jobject datatype_values, jintArray data_sizes, jobject dimensions_list, jint num_tensors, jobjectArray buffer_array)
+jlong TensorJNI::Java_com_samsung_beyond_TensorHandler_allocateTensors(JNIEnv *env, jobject thiz, jlong inference_handle, jobjectArray tensor_info_array, jint num_tensors, jobjectArray buffer_array)
 {
     beyond_tensor_info *tensors_info = nullptr;
     int ret = transformTensorsInfo(env, tensors_info, num_tensors,
-                                   datatype_values, data_sizes,
-                                   dimensions_list);
+                                   tensor_info_array);
     if (ret < 0) {
         ErrPrint("Fail to transform the information of tensors, ret = %d\n", ret);
         return 0;
@@ -176,95 +174,109 @@ jlong TensorJNI::Java_com_samsung_beyond_TensorHandler_allocateTensors(JNIEnv *e
     }
     for (int i = 0; i < num_tensors; i++) {
         jobject bytebuffer = env->NewDirectByteBuffer(tensors[i].data, tensors_info[i].size);
-        if (bytebuffer == NULL) {
+        if (bytebuffer == nullptr) {
             ErrPrint("Fail to NewDirectByteBuffer().");
             GET_BEYOND_INFERENCE(inference_handle_)->FreeTensor(tensors, num_tensors);
             tensors = nullptr;
+            for (int j = 0; j < i; j++) {
+                env->DeleteLocalRef(env->GetObjectArrayElement(buffer_array, i));
+            }
             return 0;
         }
         env->SetObjectArrayElement(buffer_array, i, bytebuffer);
+        env->DeleteLocalRef(bytebuffer);
     }
 
     return reinterpret_cast<jlong>(tensors);
 }
 
-int TensorJNI::transformTensorsInfo(JNIEnv *env, beyond_tensor_info *&tensors_info, int num_tensors, jobject datatype_values, jintArray data_sizes, jobject dimensions_list)
+int TensorJNI::transformTensorsInfo(JNIEnv *env, beyond_tensor_info *&tensors_info, int num_tensors, jobjectArray tensor_info_array)
 {
-    jclass list_class = env->FindClass("java/util/ArrayList");
-    if (list_class == NULL) {
-        ErrPrint("Fail to find java/util/List class.");
-        return -EFAULT;
-    }
-    jmethodID get_method_id = env->GetMethodID(list_class, "get", "(I)Ljava/lang/Object;");
-    if (get_method_id == NULL) {
-        ErrPrint("Fail to find a get method.");
-        return -EFAULT;
-    }
-    jmethodID size_method_id = env->GetMethodID(list_class, "size", "()I");
-    if (size_method_id == NULL) {
-        ErrPrint("Fail to find a size method.");
-        return -EFAULT;
-    }
-
-    jclass integer_class = env->FindClass("java/lang/Integer");
-    if (integer_class == NULL) {
-        ErrPrint("Fail to find java/lang/Integer class.");
-        return -EFAULT;
-    }
-    jmethodID intValue_method_id = env->GetMethodID(integer_class, "intValue", "()I");
-    if (intValue_method_id == NULL) {
-        ErrPrint("Fail to find an intValue method.");
-        return -EFAULT;
-    }
-
     beyond_tensor_info *_info = static_cast<beyond_tensor_info *>(malloc(sizeof(beyond_tensor_info) * num_tensors));
     if (_info == nullptr) {
         ErrPrint("Fail to allocate beyond_tensor_infos.");
         return -EFAULT;
     }
-    jint *datasize_values = env->GetIntArrayElements(data_sizes, 0);
-    for (int i = 0; i < num_tensors; i++) {
-        jobject integer_object = env->CallObjectMethod(datatype_values, get_method_id, i);
-        if (integer_object == nullptr) {
-            ErrPrint("Fail to get an integerObject.");
-            freeTensorDimensions(_info, i);
-            return -EFAULT;
-        }
-        jint type = env->CallIntMethod(integer_object, intValue_method_id);
-        _info[i].type = static_cast<beyond_tensor_type>(type);
-
-        _info[i].size = datasize_values[i];
 
-        jobject dimension_list_object = env->CallObjectMethod(dimensions_list, get_method_id, i);
-        if (dimension_list_object == nullptr) {
-            ErrPrint("Fail to get a dimension_list_object.");
-            freeTensorDimensions(_info, i);
-            return -EFAULT;
-        }
-        jobject dimension_object = env->CallObjectMethod(dimension_list_object, get_method_id, i);
-        if (dimension_object == nullptr) {
-            ErrPrint("Fail to get a dimension_object.");
-            freeTensorDimensions(_info, i);
-            return -EFAULT;
-        }
-        jint dimension = env->CallIntMethod(dimension_object, intValue_method_id);
-        jint rank = env->CallIntMethod(dimension_list_object, size_method_id);
-        _info[i].dims = static_cast<beyond_tensor_info::dimensions *>(malloc(sizeof(beyond_tensor_info::dimensions) + sizeof(int) * rank));
-        if (_info[i].dims == nullptr) {
-            ErrPrint("Fail to allocate dimensions.");
-            freeTensorDimensions(_info, i);
-            return -EFAULT;
+    try {
+        jclass tensor_info_list_class = env->FindClass("com/samsung/android/beyond/inference/tensor/TensorInfo");
+        if (tensor_info_list_class == nullptr) {
+            ErrPrint("jclass \"com/samsung/android/beyond/inference/tensor/TensorInfo\" is null.");
+            JNIHelper::checkEnvException(env);
         }
-        _info[i].dims->size = rank;
-        for (int j = 0; j < rank; j++) {
-            _info[i].dims->data[j] = dimension;
+        jmethodID get_data_type_value_method_id = getJmethodId(env, tensor_info_list_class,
+                                                               "getDataTypeValue", "()I");
+        jmethodID get_rank_method_id = getJmethodId(env, tensor_info_list_class, "getRank", "()I");
+        jmethodID get_dimensions_method_id = getJmethodId(env, tensor_info_list_class,
+                                                          "getDimensions",
+                                                          "()[I");
+        jmethodID get_data_byte_size_method_id = getJmethodId(env, tensor_info_list_class,
+                                                              "getDataByteSize", "()I");
+
+        for (int i = 0; i < num_tensors; i++) {
+            jobject tensor_info_object = env->GetObjectArrayElement(tensor_info_array, i);
+            checkNull(env, tensor_info_object, "Fail to get a tensor_info_object.", _info, i);
+
+            jint type = env->CallIntMethod(tensor_info_object, get_data_type_value_method_id);
+            JNIHelper::checkEnvException(env);
+            _info[i].type = static_cast<beyond_tensor_type>(type);
+
+            jint data_byte_size = env->CallIntMethod(tensor_info_object, get_data_byte_size_method_id);
+            JNIHelper::checkEnvException(env);
+            _info[i].size = data_byte_size;
+
+            jintArray dimensions = (jintArray)(env->CallObjectMethod(tensor_info_object, get_dimensions_method_id));
+            JNIHelper::checkEnvException(env);
+            jint *dimension = env->GetIntArrayElements(dimensions, NULL);
+            JNIHelper::checkEnvException(env);
+            jint rank = env->CallIntMethod(tensor_info_object, get_rank_method_id);
+            JNIHelper::checkEnvException(env);
+            _info[i].dims = static_cast<beyond_tensor_info::dimensions *>(malloc(sizeof(beyond_tensor_info::dimensions) + sizeof(int) * rank));
+            if (_info[i].dims == nullptr) {
+                ErrPrint("Fail to allocate dimensions.");
+                env->ReleaseIntArrayElements(dimensions, dimension, 0);
+                env->DeleteLocalRef(tensor_info_object);
+                freeTensorDimensions(_info, i);
+                return -EFAULT;
+            }
+            _info[i].dims->size = rank;
+            for (int j = 0; j < rank; j++) {
+                _info[i].dims->data[j] = (int)(dimension[j]);
+            }
+
+            env->ReleaseIntArrayElements(dimensions, dimension, 0);
+            env->DeleteLocalRef(tensor_info_object);
         }
+        env->DeleteLocalRef(tensor_info_list_class);
+    } catch (std::exception &e) {
+        return -EFAULT;
     }
+
     tensors_info = _info;
 
     return 0;
 }
 
+jmethodID TensorJNI::getJmethodId(JNIEnv *env, jclass clazz, const char *name, const char *signature)
+{
+    jmethodID ret = env->GetMethodID(clazz, name, signature);
+    if (ret == nullptr) {
+        ErrPrint("jmethod (%s, %s) is null.", name, signature);
+        JNIHelper::checkEnvException(env);
+    }
+
+    return ret;
+}
+
+void TensorJNI::checkNull(JNIEnv *env, jobject object, const char *error_message, beyond_tensor_info *_info, int index)
+{
+    if (object == nullptr) {
+        ErrPrint("%s", error_message);
+        freeTensorDimensions(_info, index);
+        JNIHelper::checkEnvException(env);
+    }
+}
+
 void TensorJNI::freeTensorDimensions(beyond_tensor_info *&info, int &size)
 {
     if (info == nullptr || size == 0) {
@@ -293,7 +305,7 @@ jlong TensorJNI::Java_com_samsung_beyond_TensorHandler_getOutput(JNIEnv *env, jo
 
     for (int i = 0; i < num_tensors; i++) {
         jobject byte_buffer = env->NewDirectByteBuffer(tensors[i].data, tensors[i].size);
-        if (byte_buffer == NULL) {
+        if (byte_buffer == nullptr) {
             ErrPrint("Fail to NewDirectByteBuffer().");
             return 0;
         }
@@ -325,7 +337,7 @@ int TensorJNI::RegisterTensorNatives(JNIEnv *env)
     static JNINativeMethod tensor_jni_methods[] = {
         { "getInputTensorsInfo", "(JLjava/util/List;Ljava/util/List;)Z", reinterpret_cast<void *>(Java_com_samsung_beyond_TensorHandler_getInputTensorsInfo) },
         { "getOutputTensorsInfo", "(JLjava/util/List;Ljava/util/List;)Z", reinterpret_cast<void *>(Java_com_samsung_beyond_TensorHandler_getOutputTensorsInfo) },
-        { "allocateTensors", "(JLjava/util/List;[ILjava/util/List;I[Ljava/nio/ByteBuffer;)J", reinterpret_cast<void *>(Java_com_samsung_beyond_TensorHandler_allocateTensors) },
+        { "allocateTensors", "(J[Lcom/samsung/android/beyond/inference/tensor/TensorInfo;I[Ljava/nio/ByteBuffer;)J", reinterpret_cast<void *>(Java_com_samsung_beyond_TensorHandler_allocateTensors) },
         { "getOutput", "(J[Ljava/nio/ByteBuffer;I)J", reinterpret_cast<void *>(Java_com_samsung_beyond_TensorHandler_getOutput) },
         { "freeTensors", "(JJI)V", reinterpret_cast<void *>(Java_com_samsung_beyond_TensorHandler_freeTensors) },
     };
index cfef7b5af7d084992e5bf37feb6451545ab91b0f..da1b63933975e9d24273b634789f92c8b711940a 100644 (file)
@@ -33,11 +33,14 @@ private:
     static jboolean Java_com_samsung_beyond_TensorHandler_getInputTensorsInfo(JNIEnv *env, jobject thiz, jlong inference_handle, jobject datatype_values, jobject dimensions_list);
     static jboolean Java_com_samsung_beyond_TensorHandler_getOutputTensorsInfo(JNIEnv *env, jobject thiz, jlong inference_handle, jobject datatype_values, jobject dimensions_list);
     static int getTensorsInfo(JNIEnv *env, const beyond_tensor_info *tensors_info, int num_tensors, jobject datatype_values, jobject dimensions_list);
-    static jlong Java_com_samsung_beyond_TensorHandler_allocateTensors(JNIEnv *env, jobject thiz, jlong inference_handle, jobject datatype_values, jintArray dataSizes, jobject dimensions_list, jint num_tensors, jobjectArray buffer_array);
-    static int transformTensorsInfo(JNIEnv *env, beyond_tensor_info *&tensors_info, int num_tensors, jobject datatype_values, jintArray data_sizes, jobject dimensions_list);
+    static jlong Java_com_samsung_beyond_TensorHandler_allocateTensors(JNIEnv *env, jobject thiz, jlong inference_handle, jobjectArray tensor_info_array, jint num_tensors, jobjectArray buffer_array);
+    static int transformTensorsInfo(JNIEnv *env, beyond_tensor_info *&tensors_info, int num_tensors, jobjectArray tensor_info_array);
     static void freeTensorDimensions(beyond_tensor_info *&info, int &size);
     static jlong Java_com_samsung_beyond_TensorHandler_getOutput(JNIEnv *env, jobject thiz, jlong inference_handle, jobjectArray byte_array, jint num_tensors);
     static void Java_com_samsung_beyond_TensorHandler_freeTensors(JNIEnv *env, jobject thiz, jlong inference_handle, jlong tensors_instance, jint num_tensors);
+
+    static jmethodID getJmethodId(JNIEnv *env, jclass clazz, const char *name, const char *signature);
+    static void checkNull(JNIEnv *env, jobject object, const char *error_message, beyond_tensor_info *_info, int index);
 };
 
 #endif // __BEYOND_ANDROID_TENSOR_JNI_H__