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

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
ContributorAuthor

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
xmcclureforce-pushed the bridgeless-scc-support branch from 048b0d9 to 8fabd8bCompareAugust 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?

@xmcclurexmcclureAug 19, 2016

Copy link
Copy Markdown
ContributorAuthor

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 threadsrc/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;

@xmcclurexmcclure 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 listAug 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
ContributorAuthor

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).

@xmcclurexmcclureAug 29, 2016

Copy link
Copy Markdown
ContributorAuthor

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:masterAug 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 freeto 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

@xmcclure@xamarin-release-manager@jonpryor@dnfclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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

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
ContributorAuthor

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
xmcclureforce-pushed the bridgeless-scc-support branch from 048b0d9 to 8fabd8bCompareAugust 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?

@xmcclurexmcclureAug 19, 2016

Copy link
Copy Markdown
ContributorAuthor

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 threadsrc/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;

@xmcclurexmcclure 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 listAug 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
ContributorAuthor

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).

@xmcclurexmcclureAug 29, 2016

Copy link
Copy Markdown
ContributorAuthor

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:masterAug 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 freeto 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

@xmcclure@xamarin-release-manager@jonpryor@dnfclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

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
ContributorAuthor

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
xmcclureforce-pushed the bridgeless-scc-support branch from 048b0d9 to 8fabd8bCompareAugust 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?

@xmcclurexmcclureAug 19, 2016

Copy link
Copy Markdown
ContributorAuthor

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 threadsrc/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;

@xmcclurexmcclure 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 listAug 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
ContributorAuthor

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).

@xmcclurexmcclureAug 29, 2016

Copy link
Copy Markdown
ContributorAuthor

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:masterAug 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 freeto 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

@xmcclure@xamarin-release-manager@jonpryor@dnfclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

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
ContributorAuthor

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
xmcclureforce-pushed the bridgeless-scc-support branch from 048b0d9 to 8fabd8bCompareAugust 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?

@xmcclurexmcclureAug 19, 2016

Copy link
Copy Markdown
ContributorAuthor

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 threadsrc/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;

@xmcclurexmcclure 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 listAug 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
ContributorAuthor

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).

@xmcclurexmcclureAug 29, 2016

Copy link
Copy Markdown
ContributorAuthor

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:masterAug 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 freeto 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

@xmcclure@xamarin-release-manager@jonpryor@dnfclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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

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
ContributorAuthor

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
xmcclureforce-pushed the bridgeless-scc-support branch from 048b0d9 to 8fabd8bCompareAugust 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?

@xmcclurexmcclureAug 19, 2016

Copy link
Copy Markdown
ContributorAuthor

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 threadsrc/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;

@xmcclurexmcclure 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 listAug 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
ContributorAuthor

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).

@xmcclurexmcclureAug 29, 2016

Copy link
Copy Markdown
ContributorAuthor

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:masterAug 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 freeto 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

@xmcclure@xamarin-release-manager@jonpryor@dnfclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

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
ContributorAuthor

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
xmcclureforce-pushed the bridgeless-scc-support branch from 048b0d9 to 8fabd8bCompareAugust 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?

@xmcclurexmcclureAug 19, 2016

Copy link
Copy Markdown
ContributorAuthor

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 threadsrc/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;

@xmcclurexmcclure 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 listAug 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
ContributorAuthor

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).

@xmcclurexmcclureAug 29, 2016

Copy link
Copy Markdown
ContributorAuthor

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:masterAug 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 freeto 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

@xmcclure@xamarin-release-manager@jonpryor@dnfclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

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
ContributorAuthor

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
xmcclureforce-pushed the bridgeless-scc-support branch from 048b0d9 to 8fabd8bCompareAugust 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?

@xmcclurexmcclureAug 19, 2016

Copy link
Copy Markdown
ContributorAuthor

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 threadsrc/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;

@xmcclurexmcclure 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 listAug 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
ContributorAuthor

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).

@xmcclurexmcclureAug 29, 2016

Copy link
Copy Markdown
ContributorAuthor

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:masterAug 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 freeto 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

@xmcclure@xamarin-release-manager@jonpryor@dnfclas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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

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
ContributorAuthor

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
xmcclureforce-pushed the bridgeless-scc-support branch from 048b0d9 to 8fabd8bCompareAugust 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?

@xmcclurexmcclureAug 19, 2016

Copy link
Copy Markdown
ContributorAuthor

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 threadsrc/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;

@xmcclurexmcclure 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 listAug 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
ContributorAuthor

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).

@xmcclurexmcclureAug 29, 2016

Copy link
Copy Markdown
ContributorAuthor

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:masterAug 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 freeto 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

@xmcclure@xamarin-release-manager@jonpryor@dnfclas