1 /**
2 * Copyright 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "run_tflite.h"
18
19 #include "tensorflow/lite/nnapi/nnapi_implementation.h"
20
21 #include <jni.h>
22 #include <string>
23 #include <iomanip>
24 #include <sstream>
25 #include <fcntl.h>
26
27 #include <android/asset_manager_jni.h>
28 #include <android/log.h>
29 #include <android/sharedmem.h>
30 #include <sys/mman.h>
31 #include "tensorflow/lite/nnapi/nnapi_implementation.h"
32
33 extern "C" JNIEXPORT jboolean JNICALL
Java_com_android_nn_benchmark_core_NNTestBase_hasNnApiDevice(JNIEnv * env,jobject,jstring _nnApiDeviceName)34 Java_com_android_nn_benchmark_core_NNTestBase_hasNnApiDevice(
35 JNIEnv *env, jobject /* this */, jstring _nnApiDeviceName) {
36 bool foundDevice = false;
37 const char *nnApiDeviceName =
38 _nnApiDeviceName == NULL ? NULL
39 : env->GetStringUTFChars(_nnApiDeviceName, NULL);
40 if (nnApiDeviceName != NULL) {
41 std::string device_name(nnApiDeviceName);
42 uint32_t numDevices = 0;
43 NnApiImplementation()->ANeuralNetworks_getDeviceCount(&numDevices);
44
45 for (uint32_t i = 0; i < numDevices; i++) {
46 ANeuralNetworksDevice *device = nullptr;
47 const char *buffer = nullptr;
48 NnApiImplementation()->ANeuralNetworks_getDevice(i, &device);
49 NnApiImplementation()->ANeuralNetworksDevice_getName(device, &buffer);
50 if (device_name == buffer) {
51 foundDevice = true;
52 break;
53 }
54 }
55 env->ReleaseStringUTFChars(_nnApiDeviceName, nnApiDeviceName);
56 }
57
58 return foundDevice;
59 }
60
61 extern "C"
62 JNIEXPORT jlong
63 JNICALL
Java_com_android_nn_benchmark_core_NNTestBase_initModel(JNIEnv * env,jobject,jstring _modelFileName,jint _tfliteBackend,jboolean _enableIntermediateTensorsDump,jstring _nnApiDeviceName,jboolean _mmapModel,jstring _nnApiCacheDir)64 Java_com_android_nn_benchmark_core_NNTestBase_initModel(
65 JNIEnv *env,
66 jobject /* this */,
67 jstring _modelFileName,
68 jint _tfliteBackend,
69 jboolean _enableIntermediateTensorsDump,
70 jstring _nnApiDeviceName,
71 jboolean _mmapModel,
72 jstring _nnApiCacheDir) {
73 const char *modelFileName = env->GetStringUTFChars(_modelFileName, NULL);
74 const char *nnApiDeviceName =
75 _nnApiDeviceName == NULL
76 ? NULL
77 : env->GetStringUTFChars(_nnApiDeviceName, NULL);
78 const char *nnApiCacheDir =
79 _nnApiCacheDir == NULL
80 ? NULL
81 : env->GetStringUTFChars(_nnApiCacheDir, NULL);
82 int nnapiErrno = 0;
83 void *handle = BenchmarkModel::create(
84 modelFileName, _tfliteBackend, _enableIntermediateTensorsDump, &nnapiErrno,
85 nnApiDeviceName, _mmapModel, nnApiCacheDir);
86 env->ReleaseStringUTFChars(_modelFileName, modelFileName);
87 if (_nnApiDeviceName != NULL) {
88 env->ReleaseStringUTFChars(_nnApiDeviceName, nnApiDeviceName);
89 }
90
91 if (_tfliteBackend == TFLITE_NNAPI && nnapiErrno != 0) {
92 jclass nnapiFailureClass = env->FindClass(
93 "com/android/nn/benchmark/core/NnApiDelegationFailure");
94 jmethodID constructor =
95 env->GetMethodID(nnapiFailureClass, "<init>", "(I)V");
96 jobject exception =
97 env->NewObject(nnapiFailureClass, constructor, nnapiErrno);
98 env->Throw(static_cast<jthrowable>(exception));
99 }
100
101 return (jlong)(uintptr_t)handle;
102 }
103
104 extern "C"
105 JNIEXPORT void
106 JNICALL
Java_com_android_nn_benchmark_core_NNTestBase_destroyModel(JNIEnv * env,jobject,jlong _modelHandle)107 Java_com_android_nn_benchmark_core_NNTestBase_destroyModel(
108 JNIEnv *env,
109 jobject /* this */,
110 jlong _modelHandle) {
111 BenchmarkModel* model = (BenchmarkModel *) _modelHandle;
112 delete(model);
113 }
114
115 extern "C"
116 JNIEXPORT jboolean
117 JNICALL
Java_com_android_nn_benchmark_core_NNTestBase_resizeInputTensors(JNIEnv * env,jobject,jlong _modelHandle,jintArray _inputShape)118 Java_com_android_nn_benchmark_core_NNTestBase_resizeInputTensors(
119 JNIEnv *env,
120 jobject /* this */,
121 jlong _modelHandle,
122 jintArray _inputShape) {
123 BenchmarkModel* model = (BenchmarkModel *) _modelHandle;
124 jint* shapePtr = env->GetIntArrayElements(_inputShape, nullptr);
125 jsize shapeLen = env->GetArrayLength(_inputShape);
126
127 std::vector<int> shape(shapePtr, shapePtr + shapeLen);
128 return model->resizeInputTensors(std::move(shape));
129 }
130
131 /** RAII container for a list of InferenceInOutSequence to handle JNI data release in destructor. */
132 class InferenceInOutSequenceList {
133 public:
134 InferenceInOutSequenceList(JNIEnv *env,
135 const jobject& inOutDataList,
136 bool expectGoldenOutputs);
137 ~InferenceInOutSequenceList();
138
isValid() const139 bool isValid() const { return mValid; }
140
data() const141 const std::vector<InferenceInOutSequence>& data() const { return mData; }
142
143 private:
144 JNIEnv *mEnv; // not owned.
145
146 std::vector<InferenceInOutSequence> mData;
147 std::vector<jbyteArray> mInputArrays;
148 std::vector<jobjectArray> mOutputArrays;
149 bool mValid;
150 };
151
InferenceInOutSequenceList(JNIEnv * env,const jobject & inOutDataList,bool expectGoldenOutputs)152 InferenceInOutSequenceList::InferenceInOutSequenceList(JNIEnv *env,
153 const jobject& inOutDataList,
154 bool expectGoldenOutputs)
155 : mEnv(env), mValid(false) {
156
157 jclass list_class = env->FindClass("java/util/List");
158 if (list_class == nullptr) { return; }
159 jmethodID list_size = env->GetMethodID(list_class, "size", "()I");
160 if (list_size == nullptr) { return; }
161 jmethodID list_get = env->GetMethodID(list_class, "get", "(I)Ljava/lang/Object;");
162 if (list_get == nullptr) { return; }
163 jmethodID list_add = env->GetMethodID(list_class, "add", "(Ljava/lang/Object;)Z");
164 if (list_add == nullptr) { return; }
165
166 jclass inOutSeq_class = env->FindClass("com/android/nn/benchmark/core/InferenceInOutSequence");
167 if (inOutSeq_class == nullptr) { return; }
168 jmethodID inOutSeq_size = env->GetMethodID(inOutSeq_class, "size", "()I");
169 if (inOutSeq_size == nullptr) { return; }
170 jmethodID inOutSeq_get = env->GetMethodID(inOutSeq_class, "get",
171 "(I)Lcom/android/nn/benchmark/core/InferenceInOut;");
172 if (inOutSeq_get == nullptr) { return; }
173
174 jclass inout_class = env->FindClass("com/android/nn/benchmark/core/InferenceInOut");
175 if (inout_class == nullptr) { return; }
176 jfieldID inout_input = env->GetFieldID(inout_class, "mInput", "[B");
177 if (inout_input == nullptr) { return; }
178 jfieldID inout_expectedOutputs = env->GetFieldID(inout_class, "mExpectedOutputs", "[[B");
179 if (inout_expectedOutputs == nullptr) { return; }
180 jfieldID inout_inputCreator = env->GetFieldID(inout_class, "mInputCreator",
181 "Lcom/android/nn/benchmark/core/InferenceInOut$InputCreatorInterface;");
182 if (inout_inputCreator == nullptr) { return; }
183
184
185
186 // Fetch input/output arrays
187 size_t data_count = mEnv->CallIntMethod(inOutDataList, list_size);
188 if (env->ExceptionCheck()) { return; }
189 mData.reserve(data_count);
190
191 jclass inputCreator_class = env->FindClass("com/android/nn/benchmark/core/InferenceInOut$InputCreatorInterface");
192 if (inputCreator_class == nullptr) { return; }
193 jmethodID createInput_method = env->GetMethodID(inputCreator_class, "createInput", "(Ljava/nio/ByteBuffer;)V");
194 if (createInput_method == nullptr) { return; }
195
196 for (int seq_index = 0; seq_index < data_count; ++seq_index) {
197 jobject inOutSeq = mEnv->CallObjectMethod(inOutDataList, list_get, seq_index);
198 if (mEnv->ExceptionCheck()) { return; }
199
200 size_t seqLen = mEnv->CallIntMethod(inOutSeq, inOutSeq_size);
201 if (mEnv->ExceptionCheck()) { return; }
202
203 mData.push_back(InferenceInOutSequence{});
204 auto& seq = mData.back();
205 seq.reserve(seqLen);
206 for (int i = 0; i < seqLen; ++i) {
207 jobject inout = mEnv->CallObjectMethod(inOutSeq, inOutSeq_get, i);
208 if (mEnv->ExceptionCheck()) { return; }
209
210 uint8_t* input_data = nullptr;
211 size_t input_len = 0;
212 std::function<bool(uint8_t*, size_t)> inputCreator;
213 jbyteArray input = static_cast<jbyteArray>(
214 mEnv->GetObjectField(inout, inout_input));
215 mInputArrays.push_back(input);
216 if (input != nullptr) {
217 input_data = reinterpret_cast<uint8_t*>(
218 mEnv->GetByteArrayElements(input, NULL));
219 input_len = mEnv->GetArrayLength(input);
220 } else {
221 inputCreator = [env, inout, inout_inputCreator, createInput_method](
222 uint8_t* buffer, size_t length) {
223 jobject byteBuffer = env->NewDirectByteBuffer(buffer, length);
224 if (byteBuffer == nullptr) { return false; }
225 jobject creator = env->GetObjectField(inout, inout_inputCreator);
226 if (creator == nullptr) { return false; }
227 env->CallVoidMethod(creator, createInput_method, byteBuffer);
228 if (env->ExceptionCheck()) { return false; }
229 return true;
230 };
231 }
232
233 jobjectArray expectedOutputs = static_cast<jobjectArray>(
234 mEnv->GetObjectField(inout, inout_expectedOutputs));
235 mOutputArrays.push_back(expectedOutputs);
236 seq.push_back({input_data, input_len, {}, inputCreator});
237
238 // Add expected output to sequence added above
239 if (expectedOutputs != nullptr) {
240 jsize expectedOutputsLength = mEnv->GetArrayLength(expectedOutputs);
241 auto& outputs = seq.back().outputs;
242 outputs.reserve(expectedOutputsLength);
243
244 for (jsize j = 0;j < expectedOutputsLength; ++j) {
245 jbyteArray expectedOutput =
246 static_cast<jbyteArray>(mEnv->GetObjectArrayElement(expectedOutputs, j));
247 if (env->ExceptionCheck()) {
248 return;
249 }
250 if (expectedOutput == nullptr) {
251 jclass iaeClass = mEnv->FindClass("java/lang/IllegalArgumentException");
252 mEnv->ThrowNew(iaeClass, "Null expected output array");
253 return;
254 }
255
256 uint8_t *expectedOutput_data = reinterpret_cast<uint8_t*>(
257 mEnv->GetByteArrayElements(expectedOutput, NULL));
258 size_t expectedOutput_len = mEnv->GetArrayLength(expectedOutput);
259 outputs.push_back({ expectedOutput_data, expectedOutput_len});
260 }
261 } else {
262 if (expectGoldenOutputs) {
263 jclass iaeClass = mEnv->FindClass("java/lang/IllegalArgumentException");
264 mEnv->ThrowNew(iaeClass, "Expected golden output for every input");
265 return;
266 }
267 }
268 }
269 }
270 mValid = true;
271 }
272
~InferenceInOutSequenceList()273 InferenceInOutSequenceList::~InferenceInOutSequenceList() {
274 // Note that we may land here with a pending JNI exception so cannot call
275 // java objects.
276 int arrayIndex = 0;
277 for (int seq_index = 0; seq_index < mData.size(); ++seq_index) {
278 for (int i = 0; i < mData[seq_index].size(); ++i) {
279 jbyteArray input = mInputArrays[arrayIndex];
280 if (input != nullptr) {
281 mEnv->ReleaseByteArrayElements(
282 input, reinterpret_cast<jbyte*>(mData[seq_index][i].input), JNI_ABORT);
283 }
284 jobjectArray expectedOutputs = mOutputArrays[arrayIndex];
285 if (expectedOutputs != nullptr) {
286 jsize expectedOutputsLength = mEnv->GetArrayLength(expectedOutputs);
287 if (expectedOutputsLength != mData[seq_index][i].outputs.size()) {
288 // Should not happen? :)
289 jclass iaeClass = mEnv->FindClass("java/lang/IllegalStateException");
290 mEnv->ThrowNew(iaeClass, "Mismatch of the size of expected outputs jni array "
291 "and internal array of its bufers");
292 return;
293 }
294
295 for (jsize j = 0;j < expectedOutputsLength; ++j) {
296 jbyteArray expectedOutput = static_cast<jbyteArray>(mEnv->GetObjectArrayElement(expectedOutputs, j));
297 mEnv->ReleaseByteArrayElements(
298 expectedOutput, reinterpret_cast<jbyte*>(mData[seq_index][i].outputs[j].ptr),
299 JNI_ABORT);
300 }
301 }
302 arrayIndex++;
303 }
304 }
305 }
306
307 extern "C"
308 JNIEXPORT jboolean
309 JNICALL
Java_com_android_nn_benchmark_core_NNTestBase_runBenchmark(JNIEnv * env,jobject,jlong _modelHandle,jobject inOutDataList,jobject resultList,jint inferencesSeqMaxCount,jfloat timeoutSec,jint flags)310 Java_com_android_nn_benchmark_core_NNTestBase_runBenchmark(
311 JNIEnv *env,
312 jobject /* this */,
313 jlong _modelHandle,
314 jobject inOutDataList,
315 jobject resultList,
316 jint inferencesSeqMaxCount,
317 jfloat timeoutSec,
318 jint flags) {
319
320 BenchmarkModel* model = reinterpret_cast<BenchmarkModel*>(_modelHandle);
321
322 jclass list_class = env->FindClass("java/util/List");
323 if (list_class == nullptr) { return false; }
324 jmethodID list_add = env->GetMethodID(list_class, "add", "(Ljava/lang/Object;)Z");
325 if (list_add == nullptr) { return false; }
326
327 jclass result_class = env->FindClass("com/android/nn/benchmark/core/InferenceResult");
328 if (result_class == nullptr) { return false; }
329 jmethodID result_ctor = env->GetMethodID(result_class, "<init>", "(F[F[F[[BII)V");
330 if (result_ctor == nullptr) { return false; }
331
332 std::vector<InferenceResult> result;
333
334 const bool expectGoldenOutputs = (flags & FLAG_IGNORE_GOLDEN_OUTPUT) == 0;
335 InferenceInOutSequenceList data(env, inOutDataList, expectGoldenOutputs);
336 if (!data.isValid()) {
337 return false;
338 }
339
340 // TODO: Remove success boolean from this method and throw an exception in case of problems
341 bool success = model->benchmark(data.data(), inferencesSeqMaxCount, timeoutSec, flags, &result);
342
343 // Generate results
344 if (success) {
345 for (const InferenceResult &rentry : result) {
346 jobjectArray inferenceOutputs = nullptr;
347 jfloatArray meanSquareErrorArray = nullptr;
348 jfloatArray maxSingleErrorArray = nullptr;
349
350 if ((flags & FLAG_IGNORE_GOLDEN_OUTPUT) == 0) {
351 meanSquareErrorArray = env->NewFloatArray(rentry.meanSquareErrors.size());
352 if (env->ExceptionCheck()) { return false; }
353 maxSingleErrorArray = env->NewFloatArray(rentry.maxSingleErrors.size());
354 if (env->ExceptionCheck()) { return false; }
355 {
356 jfloat *bytes = env->GetFloatArrayElements(meanSquareErrorArray, nullptr);
357 memcpy(bytes,
358 &rentry.meanSquareErrors[0],
359 rentry.meanSquareErrors.size() * sizeof(float));
360 env->ReleaseFloatArrayElements(meanSquareErrorArray, bytes, 0);
361 }
362 {
363 jfloat *bytes = env->GetFloatArrayElements(maxSingleErrorArray, nullptr);
364 memcpy(bytes,
365 &rentry.maxSingleErrors[0],
366 rentry.maxSingleErrors.size() * sizeof(float));
367 env->ReleaseFloatArrayElements(maxSingleErrorArray, bytes, 0);
368 }
369 }
370
371 if ((flags & FLAG_DISCARD_INFERENCE_OUTPUT) == 0) {
372 jclass byteArrayClass = env->FindClass("[B");
373
374 inferenceOutputs = env->NewObjectArray(
375 rentry.inferenceOutputs.size(),
376 byteArrayClass, nullptr);
377
378 for (int i = 0;i < rentry.inferenceOutputs.size();++i) {
379 jbyteArray inferenceOutput = nullptr;
380 inferenceOutput = env->NewByteArray(rentry.inferenceOutputs[i].size());
381 if (env->ExceptionCheck()) { return false; }
382 jbyte *bytes = env->GetByteArrayElements(inferenceOutput, nullptr);
383 memcpy(bytes, &rentry.inferenceOutputs[i][0], rentry.inferenceOutputs[i].size());
384 env->ReleaseByteArrayElements(inferenceOutput, bytes, 0);
385 env->SetObjectArrayElement(inferenceOutputs, i, inferenceOutput);
386 }
387 }
388
389 jobject object = env->NewObject(
390 result_class, result_ctor, rentry.computeTimeSec,
391 meanSquareErrorArray, maxSingleErrorArray, inferenceOutputs,
392 rentry.inputOutputSequenceIndex, rentry.inputOutputIndex);
393 if (env->ExceptionCheck() || object == NULL) { return false; }
394
395 env->CallBooleanMethod(resultList, list_add, object);
396 if (env->ExceptionCheck()) { return false; }
397
398 // Releasing local references to objects to avoid local reference table overflow
399 // if tests is set to run for long time.
400 if (meanSquareErrorArray) {
401 env->DeleteLocalRef(meanSquareErrorArray);
402 }
403 if (maxSingleErrorArray) {
404 env->DeleteLocalRef(maxSingleErrorArray);
405 }
406 env->DeleteLocalRef(object);
407 }
408 }
409
410 return success;
411 }
412
413 extern "C"
414 JNIEXPORT void
415 JNICALL
Java_com_android_nn_benchmark_core_NNTestBase_dumpAllLayers(JNIEnv * env,jobject,jlong _modelHandle,jstring dumpPath,jobject inOutDataList)416 Java_com_android_nn_benchmark_core_NNTestBase_dumpAllLayers(
417 JNIEnv *env,
418 jobject /* this */,
419 jlong _modelHandle,
420 jstring dumpPath,
421 jobject inOutDataList) {
422
423 BenchmarkModel* model = reinterpret_cast<BenchmarkModel*>(_modelHandle);
424
425 InferenceInOutSequenceList data(env, inOutDataList, /*expectGoldenOutputs=*/false);
426 if (!data.isValid()) {
427 return;
428 }
429
430 const char *dumpPathStr = env->GetStringUTFChars(dumpPath, JNI_FALSE);
431 model->dumpAllLayers(dumpPathStr, data.data());
432 env->ReleaseStringUTFChars(dumpPath, dumpPathStr);
433 }
434
435 extern "C"
436 JNIEXPORT jboolean
437 JNICALL
Java_com_android_nn_benchmark_core_NNTestBase_hasAccelerator()438 Java_com_android_nn_benchmark_core_NNTestBase_hasAccelerator() {
439 uint32_t device_count = 0;
440 NnApiImplementation()->ANeuralNetworks_getDeviceCount(&device_count);
441 // We only consider a real device, not 'nnapi-reference'.
442 return device_count > 1;
443 }
444
445 extern "C"
446 JNIEXPORT jboolean
447 JNICALL
Java_com_android_nn_benchmark_core_NNTestBase_getAcceleratorNames(JNIEnv * env,jclass,jobject resultList)448 Java_com_android_nn_benchmark_core_NNTestBase_getAcceleratorNames(
449 JNIEnv *env,
450 jclass, /* clazz */
451 jobject resultList
452 ) {
453 uint32_t device_count = 0;
454 auto nnapi_result = NnApiImplementation()->ANeuralNetworks_getDeviceCount(&device_count);
455 if (nnapi_result != 0) {
456 return false;
457 }
458
459 jclass list_class = env->FindClass("java/util/List");
460 if (list_class == nullptr) { return false; }
461 jmethodID list_add = env->GetMethodID(list_class, "add", "(Ljava/lang/Object;)Z");
462 if (list_add == nullptr) { return false; }
463
464 for (int i = 0; i < device_count; i++) {
465 ANeuralNetworksDevice* device = nullptr;
466 nnapi_result = NnApiImplementation()->ANeuralNetworks_getDevice(i, &device);
467 if (nnapi_result != 0) {
468 return false;
469 }
470 const char* buffer = nullptr;
471 nnapi_result = NnApiImplementation()->ANeuralNetworksDevice_getName(device, &buffer);
472 if (nnapi_result != 0) {
473 return false;
474 }
475
476 auto device_name = env->NewStringUTF(buffer);
477
478 env->CallBooleanMethod(resultList, list_add, device_name);
479 if (env->ExceptionCheck()) { return false; }
480 }
481 return true;
482 }
483
convertToJfloatArray(JNIEnv * env,const std::vector<float> & from)484 static jfloatArray convertToJfloatArray(JNIEnv* env, const std::vector<float>& from) {
485 jfloatArray to = env->NewFloatArray(from.size());
486 if (env->ExceptionCheck()) {
487 return nullptr;
488 }
489 jfloat* bytes = env->GetFloatArrayElements(to, nullptr);
490 memcpy(bytes, from.data(), from.size() * sizeof(float));
491 env->ReleaseFloatArrayElements(to, bytes, 0);
492 return to;
493 }
494
495 extern "C" JNIEXPORT jobject JNICALL
Java_com_android_nn_benchmark_core_NNTestBase_runCompilationBenchmark(JNIEnv * env,jobject,jlong _modelHandle,jint maxNumIterations,jfloat warmupTimeoutSec,jfloat runTimeoutSec)496 Java_com_android_nn_benchmark_core_NNTestBase_runCompilationBenchmark(
497 JNIEnv* env,
498 jobject /* this */,
499 jlong _modelHandle,
500 jint maxNumIterations,
501 jfloat warmupTimeoutSec,
502 jfloat runTimeoutSec) {
503 BenchmarkModel* model = reinterpret_cast<BenchmarkModel*>(_modelHandle);
504
505 jclass result_class = env->FindClass("com/android/nn/benchmark/core/CompilationBenchmarkResult");
506 if (result_class == nullptr) return nullptr;
507 jmethodID result_ctor = env->GetMethodID(result_class, "<init>", "([F[F[FI)V");
508 if (result_ctor == nullptr) return nullptr;
509
510 CompilationBenchmarkResult result;
511 bool success =
512 model->benchmarkCompilation(maxNumIterations, warmupTimeoutSec, runTimeoutSec, &result);
513 if (!success) return nullptr;
514
515 // Convert cpp CompilationBenchmarkResult struct to java.
516 jfloatArray compileWithoutCacheArray =
517 convertToJfloatArray(env, result.compileWithoutCacheTimeSec);
518 if (compileWithoutCacheArray == nullptr) return nullptr;
519
520 // saveToCache and prepareFromCache results may not exist.
521 jfloatArray saveToCacheArray = nullptr;
522 if (result.saveToCacheTimeSec) {
523 saveToCacheArray = convertToJfloatArray(env, result.saveToCacheTimeSec.value());
524 if (saveToCacheArray == nullptr) return nullptr;
525 }
526 jfloatArray prepareFromCacheArray = nullptr;
527 if (result.prepareFromCacheTimeSec) {
528 prepareFromCacheArray = convertToJfloatArray(env, result.prepareFromCacheTimeSec.value());
529 if (prepareFromCacheArray == nullptr) return nullptr;
530 }
531
532 jobject object = env->NewObject(result_class, result_ctor, compileWithoutCacheArray,
533 saveToCacheArray, prepareFromCacheArray, result.cacheSizeBytes);
534 if (env->ExceptionCheck()) return nullptr;
535 return object;
536 }
537