|
1 | 1 | using System;
|
2 | 2 | using System.Diagnostics;
|
| 3 | +using System.Runtime.InteropServices; |
3 | 4 |
|
4 | 5 | using NUnit.Framework;
|
5 | 6 |
|
6 | 7 | using Android.Runtime;
|
7 | 8 |
|
8 | 9 | using Java.Interop;
|
9 | 10 |
|
| 11 | +using JValue = Java.Interop.JValue; |
| 12 | + |
10 | 13 | namespace Android.InteropTests {
|
11 | 14 |
|
| 15 | + delegate IntPtr IntPtrDelegate_CallObjectMethodA (IntPtr env, IntPtr instance, IntPtr method, JValue[] args); |
| 16 | + delegate void IntPtrDelegate_DeleteLocalRef (IntPtr env, IntPtr handle); |
| 17 | + |
12 | 18 | [TestFixture]
|
13 | 19 | public class TestsSample {
|
14 | 20 |
|
15 |
| - const int ToString_Iterations = 10000; |
| 21 | + const int Unified_ToString_Iterations = 10000; |
| 22 | + const int MaxLocalRefs = 500; |
16 | 23 |
|
17 |
| - [Test] |
18 |
| - public void XAMethodCallTimings () |
| 24 | + // offsetof (JNIEnv, CallObjectMethodA)/sizeof(void*) |
| 25 | + const int JNIEnvIndex_CallObjectMethodA = 36; |
| 26 | + |
| 27 | + // offsetof (JNIEnv, DeleteLocalRef)/sizeof(void*) |
| 28 | + const int JNIEnvIndex_DeleteLocalRef = 23; |
| 29 | + |
| 30 | + static readonly int JNIEnvOffset_CallObjectMethodA = JNIEnvIndex_CallObjectMethodA * IntPtr.Size; |
| 31 | + static readonly int JNIEnvOffset_DeleteLocalRef = JNIEnvIndex_DeleteLocalRef * IntPtr.Size; |
| 32 | + |
| 33 | + TimeSpan GetXAMethodCallTiming () |
19 | 34 | {
|
20 | 35 | var k = JNIEnv.FindClass ("java/lang/Object");
|
21 | 36 | var c = JNIEnv.GetMethodID (k, "<init>", "()V");
|
22 | 37 | var o = JNIEnv.NewObject (k, c);
|
23 | 38 | var t = JNIEnv.GetMethodID (k, "toString", "()Ljava/lang/String;");
|
24 | 39 |
|
25 | 40 | var sw = Stopwatch.StartNew ();
|
26 |
| - for (int i = 0; i < ToString_Iterations; ++i) { |
| 41 | + for (int i = 0; i < Unified_ToString_Iterations; ++i) { |
27 | 42 | var r = JNIEnv.CallObjectMethod (o, t);
|
28 | 43 | JNIEnv.DeleteLocalRef (r);
|
29 | 44 | }
|
30 | 45 | sw.Stop ();
|
31 |
| - Console.WriteLine ("Xamarin.Android Object.toString() Timing: {0}", sw.Elapsed); |
| 46 | + return sw.Elapsed; |
32 | 47 | }
|
33 | 48 |
|
34 |
| - [Test] |
35 |
| - public void JIMethodCallTimings () |
| 49 | + TimeSpan GetJIMethodCallTiming () |
36 | 50 | {
|
37 | 51 | using (var k = new JniType ("java/lang/Object"))
|
38 | 52 | using (var c = k.GetConstructor ("()V"))
|
39 | 53 | using (var o = k.NewObject (c))
|
40 | 54 | using (var t = k.GetInstanceMethod ("toString", "()Ljava/lang/String;")) {
|
41 | 55 |
|
42 | 56 | var sw = Stopwatch.StartNew ();
|
43 |
| - for (int i = 0; i < ToString_Iterations; ++i) { |
| 57 | + for (int i = 0; i < Unified_ToString_Iterations; ++i) { |
44 | 58 | using (var r = t.CallVirtualObjectMethod (o)) {
|
45 | 59 | }
|
46 | 60 | }
|
47 | 61 | sw.Stop ();
|
48 |
| - Console.WriteLine (" Java.Interop Object.toString() Timing: {0}", sw.Elapsed); |
| 62 | + return sw.Elapsed; |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + void GetXACallObjectMethodAndDeleteLocalRefTimings (out TimeSpan callObjectMethodTiming, out TimeSpan deleteLocalRefTiming) |
| 67 | + { |
| 68 | + var k = JNIEnv.FindClass ("java/lang/Object"); |
| 69 | + var c = JNIEnv.GetMethodID (k, "<init>", "()V"); |
| 70 | + var o = JNIEnv.NewObject (k, c); |
| 71 | + var t = JNIEnv.GetMethodID (k, "toString", "()Ljava/lang/String;"); |
| 72 | + |
| 73 | + do { |
| 74 | + var r = JNIEnv.CallObjectMethod (o, t); |
| 75 | + JNIEnv.DeleteLocalRef (r); |
| 76 | + } while (false); |
| 77 | + |
| 78 | + var rs = new IntPtr [MaxLocalRefs]; |
| 79 | + |
| 80 | + var sw = Stopwatch.StartNew (); |
| 81 | + for (int i = 0; i < MaxLocalRefs; ++i) { |
| 82 | + rs [i] = JNIEnv.CallObjectMethod (o, t); |
| 83 | + } |
| 84 | + sw.Stop (); |
| 85 | + callObjectMethodTiming = sw.Elapsed; |
| 86 | + |
| 87 | + sw.Restart (); |
| 88 | + for (int i = 0; i < MaxLocalRefs; ++i) { |
| 89 | + JNIEnv.DeleteLocalRef (rs [i]); |
| 90 | + } |
| 91 | + sw.Stop (); |
| 92 | + deleteLocalRefTiming = sw.Elapsed; |
| 93 | + } |
| 94 | + |
| 95 | + void GetJICallObjectMethodAndDeleteLocalRefTimings ( |
| 96 | + out TimeSpan callVirtualObjectMethodTime, out TimeSpan disposeTime, |
| 97 | + out TimeSpan safeCallObjectMethodTime, out TimeSpan safeDeleteLocalRefTime, |
| 98 | + out TimeSpan unsafeCallObjectMethodTime, out TimeSpan unsafeDeleteLocalRefTime) |
| 99 | + { |
| 100 | + |
| 101 | + using (var k = new JniType ("java/lang/Object")) |
| 102 | + using (var c = k.GetConstructor ("()V")) |
| 103 | + using (var o = k.NewObject (c)) |
| 104 | + using (var t = k.GetInstanceMethod ("toString", "()Ljava/lang/String;")) { |
| 105 | + |
| 106 | + using (var r = t.CallVirtualObjectMethod (o)) { |
| 107 | + } |
| 108 | + |
| 109 | + var rs = new JniLocalReference [MaxLocalRefs]; |
| 110 | + |
| 111 | + var sw = Stopwatch.StartNew (); |
| 112 | + for (int i = 0; i < rs.Length; ++i) { |
| 113 | + rs [i] = t.CallVirtualObjectMethod (o); |
| 114 | + } |
| 115 | + sw.Stop (); |
| 116 | + callVirtualObjectMethodTime = sw.Elapsed; |
| 117 | + |
| 118 | + sw.Restart (); |
| 119 | + for (int i = 0; i < rs.Length; ++i) { |
| 120 | + rs [i].Dispose (); |
| 121 | + } |
| 122 | + sw.Stop (); |
| 123 | + disposeTime = sw.Elapsed; |
| 124 | + |
| 125 | + IntPtr JNIEnv_CallObjectMethodA = Marshal.ReadIntPtr (Marshal.ReadIntPtr (JNIEnv.Handle), JNIEnvOffset_CallObjectMethodA); |
| 126 | + IntPtr JNIEnv_DeleteLocalRef = Marshal.ReadIntPtr (Marshal.ReadIntPtr (JNIEnv.Handle), JNIEnvOffset_DeleteLocalRef); |
| 127 | + |
| 128 | + var safeCall = (SafeHandleDelegate_CallObjectMethodA) |
| 129 | + Marshal.GetDelegateForFunctionPointer (JNIEnv_CallObjectMethodA, typeof (SafeHandleDelegate_CallObjectMethodA)); |
| 130 | + var safeDel = (SafeHandleDelegate_DeleteLocalRef) |
| 131 | + Marshal.GetDelegateForFunctionPointer (JNIEnv_DeleteLocalRef, typeof (SafeHandleDelegate_DeleteLocalRef)); |
| 132 | + var usafeCall = (IntPtrDelegate_CallObjectMethodA) |
| 133 | + Marshal.GetDelegateForFunctionPointer (JNIEnv_CallObjectMethodA, typeof (IntPtrDelegate_CallObjectMethodA)); |
| 134 | + var usafeDel = (IntPtrDelegate_DeleteLocalRef) |
| 135 | + Marshal.GetDelegateForFunctionPointer (JNIEnv_DeleteLocalRef, typeof (IntPtrDelegate_DeleteLocalRef)); |
| 136 | + |
| 137 | + var sh = JniEnvironment.Current.SafeHandle; |
| 138 | + var uh = sh.DangerousGetHandle (); |
| 139 | + var args = new JValue [0]; |
| 140 | + |
| 141 | + sw.Restart (); |
| 142 | + for (int i = 0; i < rs.Length; ++i) { |
| 143 | + rs [i] = safeCall (sh, o, t, args); |
| 144 | + } |
| 145 | + sw.Stop (); |
| 146 | + safeCallObjectMethodTime = sw.Elapsed; |
| 147 | + |
| 148 | + sw.Restart (); |
| 149 | + for (int i = 0; i < rs.Length; ++i) { |
| 150 | + safeDel (sh, rs [i].DangerousGetHandle ()); |
| 151 | + rs [i].SetHandleAsInvalid (); |
| 152 | + } |
| 153 | + sw.Stop (); |
| 154 | + safeDeleteLocalRefTime = sw.Elapsed; |
| 155 | + |
| 156 | + var urs = new IntPtr [MaxLocalRefs]; |
| 157 | + var ut = t.DangerousGetHandle (); |
| 158 | + var uo = o.DangerousGetHandle (); |
| 159 | + |
| 160 | + sw.Restart (); |
| 161 | + for (int i = 0; i < urs.Length; ++i) { |
| 162 | + urs [i] = usafeCall (uh, uo, ut, args); |
| 163 | + } |
| 164 | + sw.Stop (); |
| 165 | + unsafeCallObjectMethodTime = sw.Elapsed; |
| 166 | + |
| 167 | + sw.Restart (); |
| 168 | + for (int i = 0; i < urs.Length; ++i) { |
| 169 | + usafeDel (uh, urs [i]); |
| 170 | + } |
| 171 | + sw.Stop (); |
| 172 | + unsafeDeleteLocalRefTime = sw.Elapsed; |
49 | 173 | }
|
50 | 174 | }
|
| 175 | + |
| 176 | + [Test] |
| 177 | + public void CompareJniInvocationTiming () |
| 178 | + { |
| 179 | + var jiFullMethodCallTiming = GetJIMethodCallTiming (); |
| 180 | + var xaFullMethodCallTiming = GetXAMethodCallTiming (); |
| 181 | + |
| 182 | + TimeSpan xaCallObjectMethodTime, xaDeleteLocalRefTime; |
| 183 | + GetXACallObjectMethodAndDeleteLocalRefTimings (out xaCallObjectMethodTime, out xaDeleteLocalRefTime); |
| 184 | + |
| 185 | + TimeSpan callVirtualObjectMethodTiming, disposeTiming, safeCallTime, safeDelTime, unsafeCallTime, unsafeDelTime; |
| 186 | + GetJICallObjectMethodAndDeleteLocalRefTimings (out callVirtualObjectMethodTiming, out disposeTiming, out safeCallTime, out safeDelTime, out unsafeCallTime, out unsafeDelTime); |
| 187 | + |
| 188 | + Console.WriteLine ("# \"Full\" Invocations: JNIEnv::CallObjectMethod() + JNIEnv::DeleteLocalRef() for {0} iterations", Unified_ToString_Iterations); |
| 189 | + Console.WriteLine (" Java.Interop Object.toString() Timing: {0}; {1,12} ms/iteration -- ~{2}x", |
| 190 | + jiFullMethodCallTiming, |
| 191 | + jiFullMethodCallTiming.TotalMilliseconds / Unified_ToString_Iterations, |
| 192 | + jiFullMethodCallTiming.TotalMilliseconds / xaFullMethodCallTiming.TotalMilliseconds); |
| 193 | + Console.WriteLine (" Xamarin.Android Object.toString() Timing: {0}; {1,12} ms/iteration", |
| 194 | + xaFullMethodCallTiming, |
| 195 | + xaFullMethodCallTiming.TotalMilliseconds / Unified_ToString_Iterations); |
| 196 | + |
| 197 | + Console.WriteLine ("# JNIEnv::CallObjectMethod() for {0} iterations", MaxLocalRefs); |
| 198 | + Console.WriteLine (" Java.Interop Object.toString() Timing: {0}; {1,12} ms/CallVirtualObjectMethod() -- ~{2}x", |
| 199 | + callVirtualObjectMethodTiming, |
| 200 | + callVirtualObjectMethodTiming.TotalMilliseconds / MaxLocalRefs, |
| 201 | + callVirtualObjectMethodTiming.TotalMilliseconds / xaCallObjectMethodTime.TotalMilliseconds); |
| 202 | + Console.WriteLine (" Xamarin.Android CallObjectMethod() Timing: {0}; {1,12} ms/CallObjectMethod()", |
| 203 | + xaCallObjectMethodTime, |
| 204 | + xaCallObjectMethodTime.TotalMilliseconds / MaxLocalRefs); |
| 205 | + |
| 206 | + Console.WriteLine ("# JNIEnv::DeleteLocalRef() for {0} iterations", MaxLocalRefs); |
| 207 | + Console.WriteLine (" Java.Interop JniLocalReference.Dispose() Timing: {0}; {1,12} ms/Dispose() -- ~{2}x", |
| 208 | + disposeTiming, |
| 209 | + disposeTiming.TotalMilliseconds / MaxLocalRefs, |
| 210 | + disposeTiming.TotalMilliseconds / xaDeleteLocalRefTime.TotalMilliseconds); |
| 211 | + Console.WriteLine (" Xamarin.Android DeleteLocalRef() Timing: {0}; {1,12} ms/DeleteLocalRef()", |
| 212 | + xaDeleteLocalRefTime, |
| 213 | + xaDeleteLocalRefTime.TotalMilliseconds / MaxLocalRefs); |
| 214 | + |
| 215 | + Console.WriteLine ("## Breaking down the above Object.toString() + JniLocalReference.Dispose() timings, the JNI calls:"); |
| 216 | + Console.WriteLine ("# JNIEnv::CallObjectMethod: SafeHandle vs. IntPtr"); |
| 217 | + Console.WriteLine (" Java.Interop safeCall() Timing: {0}; {1,12} ms/SafeHandle JNIEnv::CallObjectMethodA() -- ~{2}x", |
| 218 | + safeCallTime, |
| 219 | + safeCallTime.TotalMilliseconds / MaxLocalRefs, |
| 220 | + safeCallTime.TotalMilliseconds / unsafeCallTime.TotalMilliseconds); |
| 221 | + Console.WriteLine (" Java.Interop unsafeCall() Timing: {0}; {1,12} ms/IntPtr JNIEnv::CallObjectMethodA()", |
| 222 | + unsafeCallTime, |
| 223 | + unsafeCallTime.TotalMilliseconds / MaxLocalRefs); |
| 224 | + Console.WriteLine ("# JNIEnv::DeleteLocalRef: SafeHandle vs. IntPtr"); |
| 225 | + Console.WriteLine (" Java.Interop safeDel() Timing: {0}; {1,12} ms/SafeHandle JNIEnv::DeleteLocalRef() -- ~{2}x", |
| 226 | + safeDelTime, |
| 227 | + safeDelTime.TotalMilliseconds / MaxLocalRefs, |
| 228 | + safeDelTime.TotalMilliseconds / unsafeDelTime.TotalMilliseconds); |
| 229 | + Console.WriteLine (" Java.Interop unsafeDel() Timing: {0}; {1,12} ms/IntPtr JNIEnv::DeleteLocalRef", |
| 230 | + unsafeDelTime, |
| 231 | + unsafeDelTime.TotalMilliseconds / MaxLocalRefs); |
| 232 | + } |
51 | 233 | }
|
52 | 234 | }
|
53 | 235 |
|
0 commit comments