• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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