Skip to content

Commit 840f04a

Browse files
committed
JniMemberInfoLookup timing
Commit d1a9951 showed that *for just field lookup*, the idea of a `ref struct JniMemberInfoLookup` *might* be a good idea. Now that we've expanded `JniMemberInfoLookup` plumbing to include *method* lookup, we can throw it into `TimingTests.cs` and see how it compares! The answer is that `ref struct`s and `Span<T>` are *not* magical performance sauce with magical JIT support, and this is with CoreCLR! Method Lookup + Invoke Timing: Traditional: 00:00:00.0175778 No caching: 00:00:00.0202369 Dict w/ lock: 00:00:00.0181357 ConcurrentDict: 00:00:00.0220411 JniPeerMembers: 00:00:00.0209174 JPM+Lookup: 00:00:00.0186421 (I)I virtual+traditional: 00:00:00.0000600 (I)I virtual+JniPeerMembers: 00:00:00.0000588 (I)I virtual+JPM+Lookup: 00:00:00.0007137 The new timings are `JPM+Lookup` and `virtual+JPM+Lookup`. // JniPeerMembers return _members.InstanceMethods.InvokeVirtualObjectMethod("toString.()Ljava/lang/String;", this, null); // JPM+Lookup var member = new JniMemberInfoLookup("toString.()Ljava/lang/String;", "toString"u8, "()Ljava/lang/String;"u8); ReadOnlySpan<JniArgumentValue> args = null; return _members.InstanceMethods.InvokeVirtualObjectMethod (member, this, args); We see that JPM+Lookup is 11% *faster* when no arguments are involved. Nice! Throw an argument into the mix: // (I)I virtual+JniPeerMembers var args = stackalloc JniArgumentValue [1]; args [0] = new JniArgumentValue (value); return _members.InstanceMethods.InvokeVirtualInt32Method ("VirtualIntMethod1Args.(I)I", this, args); // (I)I virtual+JPM+Lookup var member = new JniMemberInfoLookup ( "VirtualIntMethod1Args.(I)I", "VirtualIntMethod1Args"u8, "(I)I"u8 ); var args = stackalloc JniArgumentValue [1]; args [0] = new JniArgumentValue (value); return _members.InstanceMethods.InvokeVirtualInt32Method (member, this, new ReadOnlySpan<JniArgumentValue> (args, 1)); and we're now a *whole order of magnitude worse*, taking 12.1x longer. Which quickly makes this idea as-is unworkable. Maybe it's the ReadOnlySpan<T> usage, and if I went back to straight `JniArgumentValue*` values it would be better?
1 parent bd7dddd commit 840f04a

File tree

3 files changed

+39
-1
lines changed

3 files changed

+39
-1
lines changed

tests/Java.Interop-PerformanceTests/Java.Interop-PerformanceTests.targets

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
BeforeTargets="BeforeBuild"
55
Inputs="@(JavaPerformanceTestJar)"
66
Outputs="$(OutputPath)performance-test.jar">
7-
<MakeDir Directories="$(IntermediateOutputPath)pt-classes" />
7+
<MakeDir Directories="$(IntermediateOutputPath)pt-classes;$(OutputPath)" />
88
<Exec Command="&quot;$(JavaCPath)&quot; $(_JavacSourceOptions) -d &quot;$(IntermediateOutputPath)pt-classes&quot; @(JavaPerformanceTestJar->'%(Identity)', ' ')" />
99
<Exec Command="&quot;$(JarPath)&quot; cf &quot;$(OutputPath)performance-test.jar&quot; -C &quot;$(IntermediateOutputPath)pt-classes&quot; ." />
1010
</Target>

tests/Java.Interop-PerformanceTests/Java.Interop/JavaTiming.cs

+24
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,19 @@ public virtual unsafe int Timing_VirtualIntMethod_Marshal1Args (int value)
128128
return _members.InstanceMethods.InvokeVirtualInt32Method ("VirtualIntMethod1Args.(I)I", this, args);
129129
}
130130

131+
public virtual unsafe int Timing_Lookup_VirtualIntMethod_Marshal1Args (int value)
132+
{
133+
var member = new JniMemberInfoLookup (
134+
"VirtualIntMethod1Args.(I)I",
135+
"VirtualIntMethod1Args"u8,
136+
"(I)I"u8
137+
);
138+
var args = stackalloc JniArgumentValue [1];
139+
args [0] = new JniArgumentValue (value);
140+
141+
return _members.InstanceMethods.InvokeVirtualInt32Method (member, this, new ReadOnlySpan<JniArgumentValue> (args, 1));
142+
}
143+
131144
public virtual int Timing_VirtualIntMethod_GenericMarshal1Args (int value)
132145
{
133146
return _members.InstanceMethods.InvokeGenericVirtualInt32Method ("VirtualIntMethod1Args.(I)I", this, value);
@@ -277,6 +290,17 @@ public unsafe JniObjectReference Timing_ToString_JniPeerMembers ()
277290
return _members.InstanceMethods.InvokeVirtualObjectMethod (id, this, null);
278291
}
279292

293+
public JniObjectReference Timing_ToString_JniPeerMembers_Lookup ()
294+
{
295+
var member = new JniMemberInfoLookup (
296+
toString_name + "." + toString_sig,
297+
"toString"u8,
298+
"()Ljava/lang/String;"u8
299+
);
300+
ReadOnlySpan<JniArgumentValue> args = null;
301+
return _members.InstanceMethods.InvokeVirtualObjectMethod (member, this, args);
302+
}
303+
280304
public static unsafe JniObjectReference CreateRunnable ()
281305
{
282306
return _members.StaticMethods.InvokeObjectMethod ("CreateRunnable.()Ljava/lang/Runnable;", null);

tests/Java.Interop-PerformanceTests/Java.Interop/TimingTests.cs

+14
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,12 @@ public void MethodLookupTiming ()
357357
}
358358
tp.Stop ();
359359

360+
var tl = Stopwatch.StartNew ();
361+
for (int i = 0; i < count; ++i) {
362+
var s = o.Timing_ToString_JniPeerMembers_Lookup ();
363+
JniObjectReference.Dispose (ref s);
364+
}
365+
tl.Stop ();
360366

361367
var vtt = Stopwatch.StartNew ();
362368
for (int i = 0; i < count; ++i) {
@@ -370,16 +376,24 @@ public void MethodLookupTiming ()
370376
}
371377
vti.Stop ();
372378

379+
var vtl = Stopwatch.StartNew ();
380+
for (int i = 0; i < count; ++i) {
381+
o.Timing_Lookup_VirtualIntMethod_Marshal1Args (i);
382+
}
383+
vtl.Stop ();
384+
373385

374386
Console.WriteLine ("Method Lookup + Invoke Timing:");
375387
Console.WriteLine ("\t Traditional: {0}", tt.Elapsed);
376388
Console.WriteLine ("\t No caching: {0}", ta.Elapsed);
377389
Console.WriteLine ("\t Dict w/ lock: {0}", td.Elapsed);
378390
Console.WriteLine ("\tConcurrentDict: {0}", tc.Elapsed);
379391
Console.WriteLine ("\tJniPeerMembers: {0}", tp.Elapsed);
392+
Console.WriteLine ("\t JPM+Lookup: {0}", tl.Elapsed);
380393
Console.WriteLine ();
381394
Console.WriteLine ("\t (I)I virtual+traditional: {0}", vtt.Elapsed);
382395
Console.WriteLine ("\t (I)I virtual+JniPeerMembers: {0}", vti.Elapsed);
396+
Console.WriteLine ("\t (I)I virtual+JPM+Lookup: {0}", vtl.Elapsed);
383397
}
384398
using (var o = new DerivedJavaTiming ()) {
385399
var ntt = Stopwatch.StartNew ();

0 commit comments

Comments
 (0)