• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/jni_zero/benchmarks/benchmark.h"
6 
7 #include <stdint.h>
8 
9 #include <atomic>
10 
11 #include "base/android/jni_array.h"
12 #include "base/android/jni_string.h"
13 #include "base/logging.h"
14 #include "base/numerics/safe_conversions.h"
15 #include "base/strings/strcat.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/timer/elapsed_timer.h"
18 #include "third_party/google_benchmark/src/include/benchmark/benchmark.h"
19 #include "third_party/jni_zero/benchmarks/benchmark_header/Benchmark_jni.h"
20 #include "third_party/jni_zero/benchmarks/system_jni_headers/Integer_jni.h"
21 #include "third_party/jni_zero/jni_zero.h"
22 
23 using benchmark::DoNotOptimize;
24 using jni_zero::JavaParamRef;
25 using jni_zero::JavaRef;
26 using jni_zero::ScopedJavaLocalRef;
27 
28 template <>
FromJniType(JNIEnv * env,const jni_zero::JavaRef<jobject> & j_integer)29 std::int32_t jni_zero::FromJniType<std::int32_t>(
30     JNIEnv* env,
31     const jni_zero::JavaRef<jobject>& j_integer) {
32   return static_cast<std::int32_t>(
33       JNI_Integer::Java_Integer_intValue(env, j_integer));
34 }
35 template <>
ToJniType(JNIEnv * env,const std::int32_t & input)36 jni_zero::ScopedJavaLocalRef<jobject> jni_zero::ToJniType<std::int32_t>(
37     JNIEnv* env,
38     const std::int32_t& input) {
39   return JNI_Integer::Java_Integer_valueOf__int(env, static_cast<jint>(input));
40 }
41 
42 const int kUsToNs = 1000;
43 
44 namespace jni_zero::benchmark {
45 
LookupClass(JNIEnv * env,const char * class_name)46 jclass LookupClass(JNIEnv* env, const char* class_name) {
47   std::atomic<jclass> cached_class;
48   return jni_zero::internal::LazyGetClass(env, class_name, &cached_class);
49 }
50 
LookupMethod(JNIEnv * env,jclass clazz,const char * method_name,const char * method_signature)51 jmethodID LookupMethod(JNIEnv* env,
52                        jclass clazz,
53                        const char* method_name,
54                        const char* method_signature) {
55   std::atomic<jmethodID> cached_method_id;
56   jni_zero::internal::JniJavaCallContext<true> call_context;
57   call_context.Init<jni_zero::MethodID::TYPE_STATIC>(
58       env, clazz, method_name, method_signature, &cached_method_id);
59   return call_context.method_id();
60 }
61 
JNI_Benchmark_RunLookupBenchmark(JNIEnv * env)62 static std::string JNI_Benchmark_RunLookupBenchmark(JNIEnv* env) {
63   std::stringstream benchmark_log;
64   const int kNumTries = 10000;
65   base::ElapsedTimer timer;
66   for (int i = 0; i < kNumTries; i++) {
67     Java_Benchmark_callMe(env);
68   }
69   double delta = timer.Elapsed().InMicrosecondsF();
70 
71   benchmark_log << "Called java method [Native -> Java] (" << kNumTries
72                 << " times): Elapsed time = " << delta << " us\n";
73   double average_us = delta / kNumTries;
74   benchmark_log << "Average per method call = " << average_us * kUsToNs
75                 << " ns\n";
76   return benchmark_log.str();
77 }
78 
JNI_Benchmark_RunGeneratedClassesBenchmark(JNIEnv * env)79 static std::string JNI_Benchmark_RunGeneratedClassesBenchmark(JNIEnv* env) {
80   std::stringstream benchmark_log;
81   static constexpr std::string_view kClassPrefix =
82       "org/jni_zero/benchmark/Placeholder";
83   static const char kMethodName[] = "callMe";
84   static const char kMethodSignature[] = "()V";
85   const int kNumClasses = 1000;
86   std::string class_names[kNumClasses];
87   for (int i = 1; i <= kNumClasses; i++) {
88     class_names[i] = base::StrCat({kClassPrefix, base::NumberToString(i)});
89   }
90 
91   jclass clazzes[kNumClasses];
92   base::ElapsedTimer timer;
93   for (int i = 1; i <= kNumClasses; i++) {
94     clazzes[i] = LookupClass(env, class_names[i].data());
95   }
96   double elapsed_us = timer.Elapsed().InMicrosecondsF();
97   benchmark_log << "Found different clazz (" << kNumClasses
98                 << " times): Elapsed time = " << elapsed_us << " us\n";
99 
100   elapsed_us = 0;
101   timer = {};
102   for (int i = 1; i <= kNumClasses; i++) {
103     LookupMethod(env, clazzes[i], kMethodName, kMethodSignature);
104   }
105   elapsed_us = timer.Elapsed().InMicrosecondsF();
106   benchmark_log << "Found different method (" << kNumClasses
107                 << " times): Elapsed time = " << elapsed_us << " us\n";
108   return benchmark_log.str();
109 }
110 
JNI_Benchmark_RunNativeToJavaParamSizesBenchmark(JNIEnv * env)111 static std::string JNI_Benchmark_RunNativeToJavaParamSizesBenchmark(
112     JNIEnv* env) {
113   std::stringstream benchmark_log;
114   const int kArraySize = 10000;
115   const int kNumTries = 1000;
116   std::vector<int> array;
117   array.resize(kArraySize);
118   for (int i = 0; i < kArraySize; i++) {
119     array[i] = i;
120   }
121 
122   base::ElapsedTimer timer;
123   for (int tries = 0; tries < kNumTries; tries++) {
124     Java_Benchmark_receiveLargeIntArray(env, array);
125   }
126   double elapsed_us = timer.Elapsed().InMicrosecondsF();
127   benchmark_log << "Sending " << kArraySize
128                 << " int vector with conversion [Native -> Java] (" << kNumTries
129                 << " times): Elapsed time = " << elapsed_us << " us\n";
130   double average_us = elapsed_us / kNumTries;
131   benchmark_log << "Average per " << kArraySize
132                 << " int vector = " << average_us * kUsToNs << " ns\n";
133 
134   elapsed_us = 0;
135   timer = {};
136   for (int tries = 0; tries < kNumTries; tries++) {
137     for (int i = 0; i < kArraySize; i++) {
138       Java_Benchmark_receiveSingleInt(env, i);
139     }
140   }
141   elapsed_us = timer.Elapsed().InMicrosecondsF();
142   benchmark_log << "Sending " << kArraySize
143                 << " ints one at a time [Native -> Java] (" << kNumTries
144                 << " times): Elapsed time = " << elapsed_us << " us\n";
145   average_us = elapsed_us / kNumTries;
146   benchmark_log << "Average per " << kArraySize
147                 << " ints = " << average_us * kUsToNs << " ns\n";
148 
149   elapsed_us = 0;
150   timer = {};
151   for (int tries = 0; tries < kNumTries; tries++) {
152     for (int i = 0; i < kArraySize; i++) {
153       Java_Benchmark_receiveSingleInteger(env, i);
154     }
155   }
156   elapsed_us = timer.Elapsed().InMicrosecondsF();
157   benchmark_log << "Sending " << kArraySize
158                 << " Integers with conversion one at a time [Native -> Java] ("
159                 << kNumTries << " times): Elapsed time = " << elapsed_us
160                 << " us\n";
161   average_us = elapsed_us / kNumTries;
162   benchmark_log << "Average per " << kArraySize
163                 << " Integers = " << average_us * kUsToNs << " ns\n";
164   return benchmark_log.str();
165 }
166 
JNI_Benchmark_RunAttachCurrentThreadBenchmark(JNIEnv * unused)167 static std::string JNI_Benchmark_RunAttachCurrentThreadBenchmark(
168     JNIEnv* unused) {
169   std::stringstream benchmark_log;
170   const int kNumTries = 10000;
171   base::ElapsedTimer timer;
172   for (int tries = 0; tries < kNumTries; tries++) {
173     AttachCurrentThread();
174   }
175   double elapsed_us = timer.Elapsed().InMicrosecondsF();
176   benchmark_log << "Calling AttachCurrentThread (" << kNumTries
177                 << " times): Elapsed time = " << elapsed_us << " us\n";
178   double average_us = elapsed_us / kNumTries;
179   benchmark_log << "Average per call = " << average_us * kUsToNs << " ns\n";
180   return benchmark_log.str();
181 }
182 
JNI_Benchmark_RunIntegerBoxingBenchmark(JNIEnv * env)183 static std::string JNI_Benchmark_RunIntegerBoxingBenchmark(JNIEnv* env) {
184   std::stringstream benchmark_log;
185   const int kNumTries = 10000;
186   ScopedJavaLocalRef<jobject> j_integerArray[kNumTries];
187   base::ElapsedTimer timer;
188   for (int i = 0; i < kNumTries; i++) {
189     j_integerArray[i] = JNI_Integer::Java_Integer_valueOf__int(env, i);
190   }
191   double elapsed_us = timer.Elapsed().InMicrosecondsF();
192   benchmark_log << "Calling Integer.valueOf (" << kNumTries
193                 << " times): Elapsed time = " << elapsed_us << " us\n";
194   double average_us = elapsed_us / kNumTries;
195   benchmark_log << "Average per call = " << average_us * kUsToNs << " ns\n";
196 
197   elapsed_us = 0;
198   timer = {};
199   for (int i = 0; i < kNumTries; i++) {
200     JNI_Integer::Java_Integer_intValue(env, j_integerArray[i]);
201   }
202   elapsed_us = timer.Elapsed().InMicrosecondsF();
203   benchmark_log << "Calling Integer.intValue (" << kNumTries
204                 << " times): Elapsed time = " << elapsed_us << " us\n";
205   average_us = elapsed_us / kNumTries;
206   benchmark_log << "Average per call = " << average_us * kUsToNs << " ns\n";
207   return benchmark_log.str();
208 }
209 
JNI_Benchmark_RunNativeToJavaMultipleParamsBenchmark(JNIEnv * env)210 static std::string JNI_Benchmark_RunNativeToJavaMultipleParamsBenchmark(
211     JNIEnv* env) {
212   std::stringstream benchmark_log;
213   const int kNumTries = 10000;
214   base::ElapsedTimer timer;
215   for (int i = 0; i < kNumTries; i++) {
216     Java_Benchmark_receive10Ints(env, i, i, i, i, i, i, i, i, i, i);
217   }
218   double elapsed_us = timer.Elapsed().InMicrosecondsF();
219   benchmark_log << "Sending 10 ints [Native -> Java] (" << kNumTries
220                 << " times): Elapsed time = " << elapsed_us << " us\n";
221   double average_us = elapsed_us / kNumTries;
222   benchmark_log << "Average per 10 ints = " << average_us * kUsToNs << " ns\n";
223 
224   elapsed_us = 0;
225   timer = {};
226   for (int i = 0; i < kNumTries; i++) {
227     Java_Benchmark_receive10IntegersConverted(env, i, i, i, i, i, i, i, i, i,
228                                               i);
229   }
230   elapsed_us = timer.Elapsed().InMicrosecondsF();
231   benchmark_log
232       << "Sending 10 Integers converted with @JniType [Native -> Java] ("
233       << kNumTries << " times): Elapsed time = " << elapsed_us << " us\n";
234   average_us = elapsed_us / kNumTries;
235   benchmark_log << "Average per 10 Integers = " << average_us * kUsToNs
236                 << " ns\n";
237   return benchmark_log.str();
238 }
239 
JNI_Benchmark_RunNativeToJavaStringsBenchmark(JNIEnv * env)240 static std::string JNI_Benchmark_RunNativeToJavaStringsBenchmark(JNIEnv* env) {
241   std::stringstream benchmark_log;
242   const int kNumTries = 10000;
243   const int kStringSize = 1000;
244   std::string u8_ascii_string = "";
245   std::string u8_non_ascii_string = "";
246   std::u16string u16_ascii_string = u"";
247   std::u16string u16_non_ascii_string = u"";
248   for (int i = 0; i < kStringSize; i++) {
249     u8_ascii_string += "a";
250     u8_non_ascii_string += "ق";
251     u16_ascii_string += u"a";
252     u16_non_ascii_string += u"ق";
253   }
254 
255   base::ElapsedTimer timer;
256   for (int i = 0; i < kNumTries; i++) {
257     Java_Benchmark_receiveU8String(env, u8_ascii_string);
258   }
259   double elapsed_us = timer.Elapsed().InMicrosecondsF();
260   benchmark_log << "Sending " << kStringSize
261                 << " chars utf-8 ASCII string [Native -> Java] (" << kNumTries
262                 << " times): Elapsed time = " << elapsed_us << " us\n";
263   double average_us = elapsed_us / kNumTries;
264   benchmark_log << "Average per " << kStringSize
265                 << " char string = " << average_us * kUsToNs << " ns\n";
266 
267   elapsed_us = 0;
268   timer = {};
269   for (int i = 0; i < kNumTries; i++) {
270     Java_Benchmark_receiveU16String(env, u16_ascii_string);
271   }
272   elapsed_us = timer.Elapsed().InMicrosecondsF();
273   benchmark_log << "Sending " << kStringSize
274                 << " chars utf-16 ASCII string [Native -> Java] (" << kNumTries
275                 << " times): Elapsed time = " << elapsed_us << " us\n";
276   average_us = elapsed_us / kNumTries;
277   benchmark_log << "Average per " << kStringSize
278                 << " char string = " << average_us * kUsToNs << " ns\n";
279 
280   elapsed_us = 0;
281   timer = {};
282   for (int i = 0; i < kNumTries; i++) {
283     Java_Benchmark_receiveU8String(env, u8_non_ascii_string);
284   }
285   elapsed_us = timer.Elapsed().InMicrosecondsF();
286   benchmark_log << "Sending " << kStringSize
287                 << " chars utf-8 non-ASCII string [Native -> Java] ("
288                 << kNumTries << " times): Elapsed time = " << elapsed_us
289                 << " us\n";
290   average_us = elapsed_us / kNumTries;
291   benchmark_log << "Average per " << kStringSize
292                 << " char string = " << average_us * kUsToNs << " ns\n";
293 
294   elapsed_us = 0;
295   timer = {};
296   for (int i = 0; i < kNumTries; i++) {
297     Java_Benchmark_receiveU16String(env, u16_non_ascii_string);
298   }
299   elapsed_us = timer.Elapsed().InMicrosecondsF();
300   benchmark_log << "Sending " << kStringSize
301                 << " chars utf-16 non-ASCII string [Native -> Java] ("
302                 << kNumTries << " times): Elapsed time = " << elapsed_us
303                 << " us\n";
304   average_us = elapsed_us / kNumTries;
305   benchmark_log << "Average per " << kStringSize
306                 << " char string = " << average_us * kUsToNs << " ns\n";
307   return benchmark_log.str();
308 }
309 
JNI_Benchmark_SendLargeIntArray(JNIEnv * env,const jni_zero::JavaParamRef<jintArray> & j_array)310 void JNI_Benchmark_SendLargeIntArray(
311     JNIEnv* env,
312     const jni_zero::JavaParamRef<jintArray>& j_array) {
313   size_t array_size = static_cast<size_t>(env->GetArrayLength(j_array.obj()));
314   jint* array = env->GetIntArrayElements(j_array.obj(), nullptr);
315   for (size_t i = 0; i < array_size; i++) {
316     DoNotOptimize(array[i]);
317   }
318   env->ReleaseIntArrayElements(j_array.obj(), array, 0);
319 }
320 
JNI_Benchmark_SendLargeIntArrayConverted(JNIEnv * env,std::vector<int32_t> & array)321 static void JNI_Benchmark_SendLargeIntArrayConverted(
322     JNIEnv* env,
323     std::vector<int32_t>& array) {
324   for (size_t i = 0; i < array.size(); i++) {
325     DoNotOptimize(array[i]);
326   }
327 }
328 
JNI_Benchmark_SendByteArrayUseView(JNIEnv * env,ByteArrayView & array_view)329 static void JNI_Benchmark_SendByteArrayUseView(JNIEnv* env,
330                                                ByteArrayView& array_view) {
331   for (size_t i = 0; i < array_view.size(); i++) {
332     DoNotOptimize(array_view.data());
333   }
334 }
335 
JNI_Benchmark_SendLargeObjectArray(JNIEnv * env,const JavaParamRef<jobjectArray> & j_array)336 static void JNI_Benchmark_SendLargeObjectArray(
337     JNIEnv* env,
338     const JavaParamRef<jobjectArray>& j_array) {
339   size_t array_size = static_cast<size_t>(env->GetArrayLength(j_array.obj()));
340   for (size_t i = 0; i < array_size; i++) {
341     DoNotOptimize(JNI_Integer::Java_Integer_intValue(
342         env, JavaParamRef(env, env->GetObjectArrayElement(j_array.obj(), i))));
343   }
344 }
345 
JNI_Benchmark_SendLargeObjectList(JNIEnv * env,const JavaParamRef<jobject> & j_list)346 static void JNI_Benchmark_SendLargeObjectList(
347     JNIEnv* env,
348     const JavaParamRef<jobject>& j_list) {
349   size_t array_size = static_cast<size_t>(CollectionSize(env, j_list));
350   for (size_t i = 0; i < array_size; i++) {
351     DoNotOptimize(
352         JNI_Integer::Java_Integer_intValue(env, ListGet(env, j_list, i)));
353   }
354 }
355 
JNI_Benchmark_SendSingleInt(JNIEnv * env,jint param)356 static void JNI_Benchmark_SendSingleInt(JNIEnv* env, jint param) {
357   DoNotOptimize(param);
358 }
359 
JNI_Benchmark_SendSingleInteger(JNIEnv * env,const JavaParamRef<jobject> & param)360 static void JNI_Benchmark_SendSingleInteger(
361     JNIEnv* env,
362     const JavaParamRef<jobject>& param) {
363   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, param));
364 }
365 
JNI_Benchmark_Send10Ints(JNIEnv * env,jint a,jint b,jint c,jint d,jint e,jint f,jint g,jint h,jint i,jint j)366 static void JNI_Benchmark_Send10Ints(JNIEnv* env,
367                                      jint a,
368                                      jint b,
369                                      jint c,
370                                      jint d,
371                                      jint e,
372                                      jint f,
373                                      jint g,
374                                      jint h,
375                                      jint i,
376                                      jint j) {
377   DoNotOptimize(a + b + c + d + e + f + g + h + i + j);
378 }
379 
JNI_Benchmark_Send10Integers(JNIEnv * env,const JavaParamRef<jobject> & a,const JavaParamRef<jobject> & b,const JavaParamRef<jobject> & c,const JavaParamRef<jobject> & d,const JavaParamRef<jobject> & e,const JavaParamRef<jobject> & f,const JavaParamRef<jobject> & g,const JavaParamRef<jobject> & h,const JavaParamRef<jobject> & i,const JavaParamRef<jobject> & j)380 static void JNI_Benchmark_Send10Integers(JNIEnv* env,
381                                          const JavaParamRef<jobject>& a,
382                                          const JavaParamRef<jobject>& b,
383                                          const JavaParamRef<jobject>& c,
384                                          const JavaParamRef<jobject>& d,
385                                          const JavaParamRef<jobject>& e,
386                                          const JavaParamRef<jobject>& f,
387                                          const JavaParamRef<jobject>& g,
388                                          const JavaParamRef<jobject>& h,
389                                          const JavaParamRef<jobject>& i,
390                                          const JavaParamRef<jobject>& j) {
391   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, a));
392   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, b));
393   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, c));
394   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, d));
395   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, e));
396   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, f));
397   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, g));
398   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, h));
399   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, i));
400   DoNotOptimize(JNI_Integer::Java_Integer_intValue(env, j));
401 }
402 
JNI_Benchmark_SendAsciiStringConvertedToU8(JNIEnv * env,std::string & param)403 static void JNI_Benchmark_SendAsciiStringConvertedToU8(JNIEnv* env,
404                                                        std::string& param) {}
405 
JNI_Benchmark_SendAsciiStringConvertedToU16(JNIEnv * env,std::u16string & param)406 static void JNI_Benchmark_SendAsciiStringConvertedToU16(JNIEnv* env,
407                                                         std::u16string& param) {
408 }
409 
JNI_Benchmark_SendNonAsciiStringConvertedToU8(JNIEnv * env,std::string & param)410 static void JNI_Benchmark_SendNonAsciiStringConvertedToU8(JNIEnv* env,
411                                                           std::string& param) {}
412 
JNI_Benchmark_SendNonAsciiStringConvertedToU16(JNIEnv * env,std::u16string & param)413 static void JNI_Benchmark_SendNonAsciiStringConvertedToU16(
414     JNIEnv* env,
415     std::u16string& param) {}
416 
JNI_Benchmark_CallMe(JNIEnv * env)417 static void JNI_Benchmark_CallMe(JNIEnv* env) {}
418 
JNI_Benchmark_SendListConverted(JNIEnv * env,std::vector<ScopedJavaLocalRef<jobject>> & vec)419 static void JNI_Benchmark_SendListConverted(
420     JNIEnv* env,
421     std::vector<ScopedJavaLocalRef<jobject>>& vec) {
422   for (size_t i = 0; i < vec.size(); i++) {
423     DoNotOptimize(vec[i].obj());
424   }
425 }
426 
JNI_Benchmark_SendListObject(JNIEnv * env,const JavaParamRef<jobject> & j_list)427 static void JNI_Benchmark_SendListObject(JNIEnv* env,
428                                          const JavaParamRef<jobject>& j_list) {
429   int size = CollectionSize(env, j_list);
430   for (int i = 0; i < size; i++) {
431     DoNotOptimize(ListGet(env, j_list, i).obj());
432   }
433 }
434 
435 }  // namespace jni_zero::benchmark
436