-
Notifications
You must be signed in to change notification settings - Fork 57
[ApiXmlAdjuster] Use different data model structures for better performance #756
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
Conversation
// It's debatable whether we handle this "properly", as most callers just | ||
// do `First ()`, but it's been working for years so I'm not changing it. | ||
// Exposes an IReadOnlyDictionary so caller cannot bypass our AddType/RemoveType code. | ||
public IReadOnlyDictionary<string, List<JavaType>> Types { get; } |
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.
…alternatively, should this be Set<JavaType>
instead of List<JavaType>
? Does it make sense to allow for the same instance to be added multiple times?
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.
The same "compatibility"/"diff minimization" question/concern applies to Types
vs. AllTypes
: I suspect that if Types
became an IEnumerable<JavaType>
property and AllTypes
were the "Dictionary-like" property, fewer changes would be required.
IMO, the changed I added |
} | ||
|
||
public partial class JavaPackage | ||
{ | ||
private Dictionary<string, List<JavaType>> types = new Dictionary<string, List<JavaType>> (StringComparer.OrdinalIgnoreCase); |
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.
Java type names are not filesystem-bound constructs; they are case sensitive, and it is entirely valid to have a package example
and package Example
in the same app. (It'll cause pain & suffering on case-insensitive filesystems! But Java allows it.)
I think this should use StringComparer.Ordinal
.
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.
Good point, fixed.
@@ -67,11 +70,11 @@ public static void Load (this JavaPackage package, XmlReader reader, bool isRefe | |||
if (reader.LocalName == "class") { | |||
var kls = new JavaClass (package) { IsReferenceOnly = isReferenceOnly }; | |||
kls.Load (reader); | |||
package.Types.Add (kls); |
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.
…back on the "smaller diffs!" front, if we did have a class JavaTypes : KeyedCollection<string, JavaType>
class, this statement wouldn't need to change, nor the many other statements just like it…
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.
I tested out KeyedCollection
, but it cannot handle duplicate keys:
System.ArgumentException: 'An item with the same key has already been added. Key: key1'
Today, our ApiXmlAdjuster process builds an in-memory model of every Java type we know about, and then queries this model many times in order to ensure we can resolve every needed Java type to build a binding. The data structure of this model is:
Then it is queried using LINQ like this:
In the random GPS package I used for testing,
JavaApi
contained 310 packages and a total of 5941 types. Repeatedly looping through them looking for the correct type takes a considerable amount of time.Instead, we can use a
Dictionary
to store packages and types keyed by name to significantly speed up type resolution:For the GPS project, this reduced time taken considerably:
The only "interesting" detail is that we can have multiple types with the same Java name, such as
MyInterface
andMyInterfaceConsts
. Thus we need to useDictionary<string, List<JavaType>>
to ensure we collect them all.For good measure I ran a XA build with this to ensure
Mono.Android
ApiCompat
didn't find any issues: https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=4282925.