Skip to content

Allow MonoGCBridgeSCC objects to have an empty User Peer list#154

Merged
xmcclure merged 6 commits into
dotnet:masterfrom
xmcclure:bridgeless-scc-support
Aug 29, 2016
Merged

Allow MonoGCBridgeSCC objects to have an empty User Peer list#154
xmcclure merged 6 commits into
dotnet:masterfrom
xmcclure:bridgeless-scc-support

Conversation

@xmcclure
Copy link
Copy Markdown
Contributor

@xmcclure xmcclure commented Aug 9, 2016

Mono passes gc_cross_references a description of a graph. Each node in the graph has a list of User Peer objects which should be collected together in the upcoming GC. This list is required by both the spec and the current code to be nonempty. To enable new optimizations, the runtime team wants to modify the spec to allow the list to be empty. This patch makes that possible by creating a new temporary object of type GCUserPeer when a MonoGCBridgeSCC with an empty list is seen.

I think this patch should not be merged until tests with Mono have shown that this can actually produce a performance improvement. In the meantime you can test this with https://github.com/xmcclure/mono/tree/andi-phantom-test-1 and https://github.com/xmcclure/monodroid/commits/andi-phantom-test-1 .

Mono passes gc_cross_references a description of a graph. Each node in
the graph has a list of User Peer objects which should be collected
together in the upcoming GC. This list is required by both the spec
and the current code to be nonempty. To enable new optimizations, the
runtime team wants to modify the spec to allow the list to be empty.
This patch makes that possible by creating a new temporary object of
type GCUserPeer when a MonoGCBridgeSCC with an empty list is seen.
@xamarin-release-manager
Copy link
Copy Markdown
Collaborator

Hello! I'm the build bot for the Mono project.

I need approval from a Mono team member to build this pull request. A team member should reply with "approve" to approve a build of this pull request, "whitelist" to whitelist this and all future pull requests from this contributor, or "build" to explicitly request a build, even if one has already been done.

Contributors can ignore this message.

java_class = (*env)->GetObjectClass (env, handle);
add_method_id = (*env)->GetMethodID (env, java_class, "monodroidAddReference", "(Ljava/lang/Object;)V");
if (add_method_id) {
mono.mono_field_get_value (reffed_obj, bridge_info->handle, &reffed_handle);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice there is a pretty big bug here which this patch fixes. The same bridge_info is used for obj and reffed_obj. If obj is an Object and reffed_obj is a Throwable, this could lead to Something Very Bad happening.

Instead of loading java.util.ArrayList and mono.android.GCUserPeer
every GC, load them only once and save GREFs indefinitely.
In the GC bridge handling, we need to be able to map bridgeless SCCs
to their index in the temporary_peers array. Currently we store the
indices in a lookaside table. This patch instead hides the index in
a field in MonoGCBridgeSCC which is unused for bridgeless SCCs,
allowing us to avoid allocating the lookaside.
@xmcclure xmcclure force-pushed the bridgeless-scc-support branch from 048b0d9 to 8fabd8b Compare August 18, 2016 23:02
@@ -0,0 +1,19 @@
package mono.android;

class GCUserPeer {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this implement IGCUserPeer?

Copy link
Copy Markdown
Contributor Author

@xmcclure xmcclure Aug 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could, it doesn't need to. We load monodroidAddReference off of the class directly using GetMethodID. In fact, monodroid-glue doesn't use IGCUserPeer at all. We fetch a gref to it at system start but then never do anything with that gref.

Would you prefer I have GCUserPeer implement IGCUserPeer?

Comment thread src/monodroid/jni/monodroid-glue.c Outdated

// These will be loaded as needed and persist between GCs
// FIXME: This code assumes it is totally safe to hold onto these GREFs forever. Can mono.android.jar ever be unloaded?
static jobject jni_arraylist, jni_gcuserpeer;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convention is:

  1. Prefix based on the Java class name (excluding package), preserving case. Good: ArrayList. Bad: arraylist.
  2. Underscore.
  3. "Member name": the field/method name, or ctor for constructors, or class for jclass values.

These are also usually done one per line, organized by type:

Thus:

static jclass     ArrayList_class;
static jmethodID  ArrayList_ctor;
static jmethodID  ArrayList_get;
static jmethodID  ArrayList_add;

static jclass     GCUserPeer_class;
static jmethodID  GCUserPeer_ctor;

@xmcclure xmcclure changed the title Allow MonoGCBridgeSCC objects to have an empty User Peer list [do not merge] Allow MonoGCBridgeSCC objects to have an empty User Peer list Aug 29, 2016
{
if (!target.is_mono_object)
(*env)->DeleteLocalRef (env, target.jobj);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we set target.jobj=NULL?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I do not think so, any more than DeleteLocalRef should set it to NULL. This function is only used when the target object is done with (and currently is set in only one well-defined place).

Copy link
Copy Markdown
Contributor Author

@xmcclure xmcclure Aug 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, hold on: No, we should not set it to NULL, because target is not being passed by reference. "target" is here a local variable of target_release.

@xmcclure xmcclure merged commit 7080752 into dotnet:master Aug 29, 2016
monojenkins added a commit to mono/mono that referenced this pull request Aug 29, 2016
Make GC bridge perform well in the "double fan" scenario

The GC bridge currently performs VERY poorly when the object graph has a "double fan" shape. If M Java objects point to 1 C# object which points to N Java objects, the graph exported to Monodroid will currently drop the C# object node and replace it with (M\*N) edges. It is easy to write code where (M\*N) grows ludicrously big.

In testing on device, this patch solves that problem while leaving other cases unaffected. In testing on device, I found:
* All performance tests except double fan: No discernible performance difference after patch
* Double fan test with M=N=1000: Before the patch this took between three and seven seconds. After the patch this took between 0.2 and 0.3 seconds.
* Double fan test with M=N=4000: Before the patch this took anywhere from one to two minutes. After the patch, this took about 1.2 seconds.
* Double fan test with M=N=6000: Before the patch, this took so long I was not able to get it to ever complete. After the patch, this took about 1.3 seconds.
* Double fan test with M=N=20000: I did not attempt this test pre-patch. After the patch, it took about 5.6 seconds.

The commit messages describe the changes in some detail but the short version is:
* Add a mechanism for exporting non-bridged SCCs to the bridge client
* Turn that mechanism on whenever the product fanin*fanout grows too large
* Make the merge cache more aggressive

To function, this patch requires a corresponding change to monodroid. See dotnet/android#154
radical pushed a commit that referenced this pull request May 8, 2018
…#154)

Fixes: https://bugzilla.xamarin.com/show_bug.cgi?id=56537

If a class derives from `Java.Lang.Object` it should have the two properties
mentioned above generated, so that it is possible to find methods in the correct
instance of Java class instance that is wrapped by our managed class.

Generator used to decide whether or not to generate these properties based on
the presence of class members (fields, constructors, methods and properties) or
whether the class is an annotation in the API description file.

However there exist a number of classes (for instance `Inet4Address`) which don't
have any of the members present and yet they should have the Threshold*
properties generated for the reasons described above.

The `HasClassHandle` property which was used to determine whether the class should
get these properties (among a handful of other members) doesn't serve its
purpose correctly leading to corner cases when the `Threshold*` properties are
missing and causing runtime bugs (e.g. calling `GetAddress()` on an instance of
`Inet4Address` returns `null` even though the underlying Java class has the
information - the call never reaches `Inet4Address` instance and thus returns
nothing).

Replacing `HasClassHandle` with a simple check for whether the class in question
derives from `Java.Lang.Object` is the correct fix that ensures the missing
properties are generated.
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
Make GC bridge perform well in the "double fan" scenario

The GC bridge currently performs VERY poorly when the object graph has a "double fan" shape. If M Java objects point to 1 C# object which points to N Java objects, the graph exported to Monodroid will currently drop the C# object node and replace it with (M\*N) edges. It is easy to write code where (M\*N) grows ludicrously big.

In testing on device, this patch solves that problem while leaving other cases unaffected. In testing on device, I found:
* All performance tests except double fan: No discernible performance difference after patch
* Double fan test with M=N=1000: Before the patch this took between three and seven seconds. After the patch this took between 0.2 and 0.3 seconds.
* Double fan test with M=N=4000: Before the patch this took anywhere from one to two minutes. After the patch, this took about 1.2 seconds.
* Double fan test with M=N=6000: Before the patch, this took so long I was not able to get it to ever complete. After the patch, this took about 1.3 seconds.
* Double fan test with M=N=20000: I did not attempt this test pre-patch. After the patch, it took about 5.6 seconds.

The commit messages describe the changes in some detail but the short version is:
* Add a mechanism for exporting non-bridged SCCs to the bridge client
* Turn that mechanism on whenever the product fanin*fanout grows too large
* Make the merge cache more aggressive

To function, this patch requires a corresponding change to monodroid. See dotnet/android#154


Commit migrated from mono/mono@66fd6fd
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants