1 #![cfg(feature = "invocation")]
2 #![feature(test)]
3
4 extern crate test;
5
6 use lazy_static::lazy_static;
7
8 use jni::{
9 descriptors::Desc,
10 objects::{JClass, JMethodID, JObject, JStaticMethodID, JValue},
11 signature::{JavaType, Primitive},
12 sys::jint,
13 InitArgsBuilder, JNIEnv, JNIVersion, JavaVM,
14 };
15
16 static CLASS_MATH: &str = "java/lang/Math";
17 static CLASS_OBJECT: &str = "java/lang/Object";
18 static METHOD_MATH_ABS: &str = "abs";
19 static METHOD_OBJECT_HASH_CODE: &str = "hashCode";
20 static METHOD_CTOR: &str = "<init>";
21 static SIG_OBJECT_CTOR: &str = "()V";
22 static SIG_MATH_ABS: &str = "(I)I";
23 static SIG_OBJECT_HASH_CODE: &str = "()I";
24
25 #[inline(never)]
native_abs(x: i32) -> i3226 fn native_abs(x: i32) -> i32 {
27 x.abs()
28 }
29
jni_abs_safe(env: &JNIEnv, x: jint) -> jint30 fn jni_abs_safe(env: &JNIEnv, x: jint) -> jint {
31 let x = JValue::from(x);
32 let v = env
33 .call_static_method(CLASS_MATH, METHOD_MATH_ABS, SIG_MATH_ABS, &[x])
34 .unwrap();
35 v.i().unwrap()
36 }
37
jni_call_static_unchecked<'c, C>( env: &JNIEnv<'c>, class: C, method_id: JStaticMethodID<'c>, x: jint, ) -> jint where C: Desc<'c, JClass<'c>>,38 fn jni_call_static_unchecked<'c, C>(
39 env: &JNIEnv<'c>,
40 class: C,
41 method_id: JStaticMethodID<'c>,
42 x: jint,
43 ) -> jint
44 where
45 C: Desc<'c, JClass<'c>>,
46 {
47 let x = JValue::from(x);
48 let ret = JavaType::Primitive(Primitive::Int);
49 let v = env
50 .call_static_method_unchecked(class, method_id, ret, &[x])
51 .unwrap();
52 v.i().unwrap()
53 }
54
jni_hash_safe(env: &JNIEnv, obj: JObject) -> jint55 fn jni_hash_safe(env: &JNIEnv, obj: JObject) -> jint {
56 let v = env
57 .call_method(obj, METHOD_OBJECT_HASH_CODE, SIG_OBJECT_HASH_CODE, &[])
58 .unwrap();
59 v.i().unwrap()
60 }
61
jni_call_unchecked<'m, M>(env: &JNIEnv<'m>, obj: JObject<'m>, method_id: M) -> jint where M: Desc<'m, JMethodID<'m>>,62 fn jni_call_unchecked<'m, M>(env: &JNIEnv<'m>, obj: JObject<'m>, method_id: M) -> jint
63 where
64 M: Desc<'m, JMethodID<'m>>,
65 {
66 let ret = JavaType::Primitive(Primitive::Int);
67 let v = env.call_method_unchecked(obj, method_id, ret, &[]).unwrap();
68 v.i().unwrap()
69 }
70
71 #[cfg(test)]
72 mod tests {
73 use super::*;
74 use std::rc::Rc;
75 use std::sync::Arc;
76 use test::{black_box, Bencher};
77
78 lazy_static! {
79 static ref VM: JavaVM = {
80 let args = InitArgsBuilder::new()
81 .version(JNIVersion::V8)
82 .build()
83 .unwrap();
84 JavaVM::new(args).unwrap()
85 };
86 }
87
88 #[bench]
native_call_function(b: &mut Bencher)89 fn native_call_function(b: &mut Bencher) {
90 b.iter(|| {
91 let _ = native_abs(black_box(-3));
92 });
93 }
94
95 #[bench]
jni_call_static_method_safe(b: &mut Bencher)96 fn jni_call_static_method_safe(b: &mut Bencher) {
97 let env = VM.attach_current_thread().unwrap();
98
99 b.iter(|| jni_abs_safe(&env, -3));
100 }
101
102 #[bench]
jni_call_static_method_unchecked_str(b: &mut Bencher)103 fn jni_call_static_method_unchecked_str(b: &mut Bencher) {
104 let env = VM.attach_current_thread().unwrap();
105 let class = CLASS_MATH;
106 let method_id = env
107 .get_static_method_id(class, METHOD_MATH_ABS, SIG_MATH_ABS)
108 .unwrap();
109
110 b.iter(|| jni_call_static_unchecked(&env, class, method_id, -3));
111 }
112
113 #[bench]
jni_call_static_method_unchecked_jclass(b: &mut Bencher)114 fn jni_call_static_method_unchecked_jclass(b: &mut Bencher) {
115 let env = VM.attach_current_thread().unwrap();
116 let class: JClass = CLASS_MATH.lookup(&env).unwrap();
117 let method_id = env
118 .get_static_method_id(class, METHOD_MATH_ABS, SIG_MATH_ABS)
119 .unwrap();
120
121 b.iter(|| jni_call_static_unchecked(&env, class, method_id, -3));
122 }
123
124 #[bench]
jni_call_object_method_safe(b: &mut Bencher)125 fn jni_call_object_method_safe(b: &mut Bencher) {
126 let env = VM.attach_current_thread().unwrap();
127 let s = env.new_string("").unwrap();
128 let obj = black_box(JObject::from(s));
129
130 b.iter(|| jni_hash_safe(&env, obj));
131 }
132
133 #[bench]
jni_call_object_method_unchecked(b: &mut Bencher)134 fn jni_call_object_method_unchecked(b: &mut Bencher) {
135 let env = VM.attach_current_thread().unwrap();
136 let s = env.new_string("").unwrap();
137 let obj = black_box(JObject::from(s));
138 let method_id = env
139 .get_method_id(obj, METHOD_OBJECT_HASH_CODE, SIG_OBJECT_HASH_CODE)
140 .unwrap();
141
142 b.iter(|| jni_call_unchecked(&env, obj, method_id));
143 }
144
145 #[bench]
jni_new_object_str(b: &mut Bencher)146 fn jni_new_object_str(b: &mut Bencher) {
147 let env = VM.attach_current_thread().unwrap();
148 let class = CLASS_OBJECT;
149
150 b.iter(|| {
151 let obj = env.new_object(class, SIG_OBJECT_CTOR, &[]).unwrap();
152 env.delete_local_ref(obj).unwrap();
153 });
154 }
155
156 #[bench]
jni_new_object_by_id_str(b: &mut Bencher)157 fn jni_new_object_by_id_str(b: &mut Bencher) {
158 let env = VM.attach_current_thread().unwrap();
159 let class = CLASS_OBJECT;
160 let ctor_id = env
161 .get_method_id(class, METHOD_CTOR, SIG_OBJECT_CTOR)
162 .unwrap();
163
164 b.iter(|| {
165 let obj = env.new_object_unchecked(class, ctor_id, &[]).unwrap();
166 env.delete_local_ref(obj).unwrap();
167 });
168 }
169
170 #[bench]
jni_new_object_jclass(b: &mut Bencher)171 fn jni_new_object_jclass(b: &mut Bencher) {
172 let env = VM.attach_current_thread().unwrap();
173 let class: JClass = CLASS_OBJECT.lookup(&env).unwrap();
174
175 b.iter(|| {
176 let obj = env.new_object(class, SIG_OBJECT_CTOR, &[]).unwrap();
177 env.delete_local_ref(obj).unwrap();
178 });
179 }
180
181 #[bench]
jni_new_object_by_id_jclass(b: &mut Bencher)182 fn jni_new_object_by_id_jclass(b: &mut Bencher) {
183 let env = VM.attach_current_thread().unwrap();
184 let class: JClass = CLASS_OBJECT.lookup(&env).unwrap();
185 let ctor_id = env
186 .get_method_id(class, METHOD_CTOR, SIG_OBJECT_CTOR)
187 .unwrap();
188
189 b.iter(|| {
190 let obj = env.new_object_unchecked(class, ctor_id, &[]).unwrap();
191 env.delete_local_ref(obj).unwrap();
192 });
193 }
194
195 #[bench]
jni_new_global_ref(b: &mut Bencher)196 fn jni_new_global_ref(b: &mut Bencher) {
197 let env = VM.attach_current_thread().unwrap();
198 let class = CLASS_OBJECT;
199 let obj = env.new_object(class, SIG_OBJECT_CTOR, &[]).unwrap();
200 let global_ref = env.new_global_ref(obj).unwrap();
201 env.delete_local_ref(obj).unwrap();
202
203 b.iter(|| env.new_global_ref(&global_ref).unwrap());
204 }
205
206 /// Checks the overhead of checking if exception has occurred.
207 ///
208 /// Such checks are required each time a Java method is called, but
209 /// can be omitted if we call a JNI method that returns an error status.
210 ///
211 /// See also #58
212 #[bench]
jni_check_exception(b: &mut Bencher)213 fn jni_check_exception(b: &mut Bencher) {
214 let env = VM.attach_current_thread().unwrap();
215
216 b.iter(|| env.exception_check().unwrap());
217 }
218
219 #[bench]
jni_get_java_vm(b: &mut Bencher)220 fn jni_get_java_vm(b: &mut Bencher) {
221 let env = VM.attach_current_thread().unwrap();
222
223 b.iter(|| {
224 let _jvm = env.get_java_vm().unwrap();
225 });
226 }
227
228 /// A benchmark measuring Push/PopLocalFrame overhead.
229 ///
230 /// Such operations are *required* if one attaches a long-running
231 /// native thread to the JVM because there is no 'return-from-native-method'
232 /// event when created local references are freed, hence no way for
233 /// the JVM to know that the local references are no longer used in the native code.
234 #[bench]
jni_noop_with_local_frame(b: &mut Bencher)235 fn jni_noop_with_local_frame(b: &mut Bencher) {
236 // Local frame size actually doesn't matter since JVM does not preallocate anything.
237 const LOCAL_FRAME_SIZE: i32 = 32;
238 let env = VM.attach_current_thread().unwrap();
239 b.iter(|| {
240 env.with_local_frame(LOCAL_FRAME_SIZE, || Ok(JObject::null()))
241 .unwrap()
242 });
243 }
244
245 /// A benchmark of the overhead of attaching and detaching a native thread.
246 ///
247 /// It is *huge* — two orders of magnitude higher than calling a single
248 /// Java method using unchecked APIs (e.g., `jni_call_static_unchecked`).
249 ///
250 #[bench]
jvm_noop_attach_detach_native_thread(b: &mut Bencher)251 fn jvm_noop_attach_detach_native_thread(b: &mut Bencher) {
252 b.iter(|| {
253 let env = VM.attach_current_thread().unwrap();
254 black_box(&env);
255 });
256 }
257
258 #[bench]
native_arc(b: &mut Bencher)259 fn native_arc(b: &mut Bencher) {
260 let env = VM.attach_current_thread().unwrap();
261 let class = CLASS_OBJECT;
262 let obj = env.new_object(class, SIG_OBJECT_CTOR, &[]).unwrap();
263 let global_ref = env.new_global_ref(obj).unwrap();
264 env.delete_local_ref(obj).unwrap();
265 let arc = Arc::new(global_ref);
266
267 b.iter(|| {
268 let _ = black_box(Arc::clone(&arc));
269 });
270 }
271
272 #[bench]
native_rc(b: &mut Bencher)273 fn native_rc(b: &mut Bencher) {
274 let _env = VM.attach_current_thread().unwrap();
275 let env = VM.get_env().unwrap();
276 let rc = Rc::new(env);
277
278 b.iter(|| {
279 let _ = black_box(Rc::clone(&rc));
280 });
281 }
282 }
283