-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
define the structure's field order with metadata #954
Conversation
// TODO(idosu 28 Apr 2018): Maybe deprecate this method to let users know they should use @FieldOrder | ||
protected List<String> getFieldOrder() { | ||
List<String> fields = new LinkedList<String>(); | ||
for (Class<?> clazz = getClass(); clazz != Structure.class; clazz = clazz.getSuperclass()) { |
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.
If you walk the inheritance structure here, you'll miss classes, that extend Structure, but don't use the annotation. So what I would do at this point:
- fetch the field order for the super class via a
super.getFieldOrder()
call - extract the local order from the annotation and append it
This way you can mix the definitions. As the order is caches, this should not have a big impact on performance.
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.
not fixed
I thought about it but I don't agree, because if a class overrides this method no sub class can call the default implementation i.e. getting with annotation
class Parent extends Structure {
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("a");
}
}
class Son extends C {
@Override
protected List<String> getFieldOrder() {
// super.getFieldOrder() returns [ "a" ]
// Structure.super.getFieldOrder() - this is not valid
}
}
plus calling getClass()
in super.getFieldOrder()
will result in the same class as it would if you write it here and therefore will result in a StackOverflowException
.
Could you show me an example perhaps?
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.
BTW I had another idea to check in this.fieldOrder
if we already got fields for the super class and use them instead of iterating the inheritance structure. what do you think, does it worth the effort?
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.
You are right - subclasses can still call super#getFieldOrder
and so build a complete list if a super class uses the @FieldOrder
annotation. It will fail if a child class is using the annotation and the super class is not. In this case the member check at runtime will blow. For the 5.0.0 release it would be ok to update all structures in the platform.
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.
Notice that if the the parent class's getFieldOrder
method was overridden there is no way to call the original code of Structure#getFieldOrder
therefor if the user wants to extend the parent class he must override getFieldOrder
and add the new fields manually.
I can create an helper method to use when using an old library(without the usage of @FieldOrder
) so the user could just override getFieldOrder
and return getNonAnnotatedAndAnnotatedFieldOrder()
, but it seems to be error prone(in this configuration: classWithAnnotation extends classWithout extends classWith extends classWithout)
protected List<String> getFieldOrder() {
return getAnnotatedFieldOrder();
}
protected final List<String> getAnnotatedFieldOrder() {
// the code of the current implementation of getFieldOrder
}
protected final List<String> getNonAnnotatedAndAnnotatedFieldOrder() {
List<String> parent = getFieldOrder();
List<String> annotated = getAnnotatedFieldOrder();
List<String> fieldOrder = new ArrayList<String>(parent.size() + annotated.size());
fieldOrder.addAll(parent);
fieldOrder.addAll(annotated);
return fieldOrder;
}
Should I add it anyway?
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 left some comments inline. Please have a look at these. In general I like the idea.
src/com/sun/jna/Structure.java
Outdated
* | ||
* When defining a new {@link Structure} you shouldn't override this | ||
* method, but use {@link FieldOrder} annotation to define your field | ||
* order(this also works with inheritance), For example, |
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 would not repeat the example here - if some one overlooks it, so be it. But please have another look at the java of the class - there is also a hint for the requirement of implementing getFieldOrder
, that text should also be adjusted.
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.
fixed
(remove the example) - I left the comment not to override, is that ok?
(fix other javadoc) - fixed
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.
Yes this sounds good.
src/com/sun/jna/Structure.java
Outdated
@@ -865,20 +871,79 @@ protected void writeField(StructField structField) { | |||
} | |||
} | |||
|
|||
/** Return this Structure's field names in their proper order. For | |||
* example, | |||
/** Used to decllare fields order as metadata instead of method. |
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.
Typo: decllare -> declare
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.
fixed
src/com/sun/jna/Structure.java
Outdated
* protected List getFieldOrder() { | ||
* return Arrays.asList(new String[] { ... }); | ||
* // New | ||
* <!-- -->{@literal @}FieldOrder({ "n", "s" }) |
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.
Is there a reason for these empty html comments?
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.
fixed
Yes... in vscode if a javadoc line starts with a '@' it implodes all the code lines into one line, You are right I should remove that and hope that vscode fix this problem.
microsoft/vscode#48898
src/com/sun/jna/Structure.java
Outdated
* } | ||
* } | ||
* </code></pre> | ||
* @author 28 Apr 2018 idosu |
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.
Please remove - it will be recorded in the git history
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.
not fixed
Could you please elaborate on that? because if this comment would not be written no one would know(from the code) how to use it.
Or did you want me to remove line 906(@author 28 Apr 2018 idosu
)
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 meant only the @author part.
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.
fixed
test/com/sun/jna/StructureTest.java
Outdated
@@ -227,6 +228,11 @@ protected void ensureAllocated() { | |||
return FIELDS; | |||
} | |||
} | |||
@FieldOrder({ "field0", "field1" }) | |||
public static class EasyTestStructure0 extends FilledStructure { |
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 name is not optimal - "Easy" is a personal evaluation - I think "Annotated" might fit better.
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.
fixed
Thank you so far - I think the change can be merged as is. If a mixed
You don't need a new PR, just rewrite the history and force push into the branch you created for this changeset. Please also make sure that you add valid author information (is idosu your full name?). |
9772846
to
f16aba0
Compare
5283b2b
to
1889a47
Compare
I've fixed it, and now its one commit |
Merged via: Thank you for your contribution. |
I know this has been merged, and I think it is a fine change. I'm a little concerned though about the usage of I imagine the rational to have been that since you're adding stuff at the head this is better? However, for small lists (dozens of fields), created in a few batches (a couple of inheritance levels), this is likely much worse than an
Would a PR to replace |
I think that we could profit from both worlds if we return If you decide to implement it, please change the type declaration of fields from |
From my POV a reasoned PR would be very nice. @idosu suggestion looks good from my POV. |
I'm of the opinion that Are you guys familiar with JMH? If so, check this benchmark. Small represents a struct with 27 fields and 3 levels of inheritance. Large represents a struct with 110 fields, 7 levels deep. Most structs will have no inheritance, which (regardless of how many fields) should favor
|
You are benchmarking the list creation, but that is not the bottle-neck (it happens once, as the result from |
Sure, but the only thing jna/src/com/sun/jna/Structure.java Lines 962 to 973 in 7ac44fe
Is there any doubt that |
I would convert to ArrayList at return of |
This can also be accomplished by Updated the benchmark:
|
You are still benchmarking creation of the list (that is invoked once per Structure, see |
I honestly though we agreed that benchmarking iteration was pointless, but here it is:
Personally, I'd just use But if you guys feel otherwise, I'm glad to drop this, and leave it as it is. |
Adding some context. I just had to drop JNA (for JNI) in a project because of how slow JNA was at marshaling an array of a few hundred fat structs. Something like 90% of the time was spent marshaling and GCing garbage created for marshaling. This isn't a chatty API (it's a single call, which does some actual work), but there's a lot of marshaling involved (both in and out). Just creating a "contiguous" array of structs (with The change in this PR, as it is, makes things measurably worse, because iterating a I thought it was a simple, non contentious, improvement. You use an I also had other ideas, but I'd need time to mature them, and I've moved on for now. But, I see you guys tend to use Another example: jna/src/com/sun/jna/CallbackReference.java Line 399 in 7b66282
The person who wrote this might be surprised to know that, the first thing the If my benchmarks are not enough to convince you that, for smallish lists (N<100), |
You are misunderstanding me. I'm fine with a change from Oh and please "I see you guys tend to use LinkedList in a lot of places.": There is no "you" here. Most probably the same guy wrote the code and it ended up in "a lot of places". I appreciate performance enhancing PR - so you can and should work on it, but maybe we should do it on the requested code change, that would make it easier. |
Nuno,
Just curious, is the few hundred fat structs scenario actually moving
data to and from native in all those structs? The first goal in JNA's
default behavior is to make things easy, and there are usually less-easy
approaches to improve performance.
Re: ArrayList, that's what the original field management used. To me,
it seems a simpler implementation for the field management, since the lists
are always append-only.
Matthias is right about usage of LinkedList, it likely is due to one or
more contributions from the same author; if the net benefit of a patch is
judged to be positive, then the reviewers aren't too nitpicky. Sometimes
using ArrayList vs LinkedList is just due to one's personal habits.
…On Sat, Jun 16, 2018 at 8:05 PM, Nuno Cruces ***@***.***> wrote:
Adding some context.
I just had to drop JNA (for JNI) in a project because of how slow JNA was
at marshaling an array of a few hundred fat structs. Something like 90% of
the time was spent marshaling and GCing garbage created for marshaling.
This isn't a chatty API (it's a single call, which does some actual work),
but there's a lot of marshaling involved (both in and out).
Just creating a "contiguous" array of structs (with Struct.toArray) to
pass as an argument was painfully slow. I came up with a workaround, but
had to make my structs Cloneable because most of the overhead is from the
constructor.
The change in this PR, *as it is*, makes things measurably worse, because
iterating a LinkedList with List.get is obviously slow (I've profiled it,
but can't really make an MCVE out of it).
I thought it was a simple, non contentious, improvement. You use an
ArrayList here
<https://github.com/java-native-access/jna/blob/7ac44fee3d6b0e47de6d5d10c32be258b9ed1bef/src/com/sun/jna/Structure.java#L993>,
after all.
I also had other ideas, but I'd need time to mature them, and I've moved
on for now.
------------------------------
But, I see you guys tend to use LinkedList in a *lot* of places.
Another example:
https://github.com/java-native-access/jna/blob/
7b66282/src/com/sun/jna/
CallbackReference.java#L399
The person who wrote this might be surprised to know that, the first thing
the LinkedList constructor in OpenJDK is going to do is call
Collection.toArray (see here
<http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/LinkedList.java#l408>),
so you might as well do that instead.
If my benchmarks are not enough to convince you that, for smallish lists
(N<100), ArrayList consumes less memory, creates less garbage, iterates
faster, even handles the worse case List.add(0, e) scenario faster, then
I'm afraid nothing will.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#954 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAuMrZxD1Fh54Y97cl-pY6nfaNa1J8Ndks5t9Z1hgaJpZM4TrjtB>
.
|
define a
Structure
using an annotation instead of implementing a method.instead of this:
use this:
In both examples calling
new Son().getFieldOrder()
will resolve in this list[ "a", "b", "c", "d" ]
It improves readability and makes that the user could not forget to add super's fields because it is added automatically by the library