-
Notifications
You must be signed in to change notification settings - Fork 535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[linker] Avoid dictionary creation and lookup in JNI registration #2587
base: main
Are you sure you want to change the base?
Conversation
Initial measurements from profiler (5 runs of XF test on Pixel 2 XL) new:
old:
|
src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/MonoDroidMarkStep.cs
Show resolved
Hide resolved
What do the numbers in the earlier comment mean? Time in ms? |
Yeah, the profiler output columns. I have updated the comment to include the headers. |
fixed (char *src = str) { | ||
int c; | ||
char *s = src; | ||
while ((c = s[0]) != 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look safe if str
is null.
It also appears to require that str
be null
-terminated to work with strings with str.Length==0
. I'm not sure if this is a portable assumption; it's true for mono, but is it true for .NET? Will it be true in the future?
I'd kinda prefer a for
loop and removal of the conditional:
int i;
int len = str.Length;
for (i = 0; (i+1) < len; i += 2) {
hash1 = ((hash1 << 5) + hash1) ^ src [i];
hash2 = ((hash2 << 5) + hash2) ^ src [i+1];
}
if (i < len) {
hash1 = ((hash1 << 5) + hash1) ^ src [i];
}
Yes, this repeats the expression for hash1
, but it removes a conditional from the primary loop. It would be interesting to see how the performance compares.
{ | ||
// fill code added by the linker | ||
} | ||
// should stay in sync with MonoDroidMarkStep.GetStringHashCode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of a comment, why don't we stick this into a dedicated .cs
file which is included into both projects, possibly via (new?) shared project? Then we don't need to document that the methods be in sync, as the same source will be used.
|
||
static MagicRegistrationMap () | ||
{ | ||
Prefill (); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this static constructor at all then?
{ | ||
int hashCode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable declaration feels weird. Why is it here?
@jonpryor, it is work in progress, so I will address many of those things in the final version. BTW, the hash calculation method is taken from csc output disassembly. |
Actually I used the one from our corlib (for now): https://github.com/mono/mono/blame/master/mcs/class/corlib/ReferenceSources/String.cs#L414-L435 |
The first implementation tested the idea, but used linear search in the list of hash code values. I have tried 2 approaches to improve that, which led to interesting observations. Binary search: use unrolled binary search in hash code values, which is similar to
Modulo: use hashCode modulo number of hash code values. That looks like:
The results were interesting.
It shows how important is the JIT time here. The performance of the new lookup methods are similar (note that
The Mod version of So it looks like the time spent in JIT is much more important then I thought. I plan to look at mono performance counters to see whether I can get the JIT times per method. It also makes the profiled AOT even more interesting. |
Idea to consider: what if we generate this part of registration in C instead of IL and link the compiled object file to libmonodroid? Or add it to the object file @grendello plans to use and link on device? (IIRC) This way we can avoid jitting the code and don't need AOT to be employed. Maybe we can identify other parts of startup process, which can be implemented in C to speed the app start up? |
Note: after discussion and JIT times analysis it looks like it would be best to store the indexes to typemap files we use. That should help even before the typemap files are converted to object files. Another extension of that idea is to store metadata tokens in the typemap for the registration methods. That would make the process a lot simpler and faster. |
While this is a good idea and should be pursued, metadata tokens are a per-assembly construct, while the typemap files will contain types from all assemblies. This will need to be solved. |
We already have that solved here, as we use the imported references. So the tokens are valid in We already have the code like this today, which has token for a registration type for class
This is prepared in linker, so we already know the references and can import them. |
Start of implementation of #2586