I've been working on similar issues trying to optimise something heavily. I made a similar class to this one (I even had a similar API) but I found I called it MostlyFinal instead. private static final MostlyConstant<Integer> FOO = new MostlyConstant<>(42, int.class); private static final IntSupplier FOO_GETTER = FOO.intGetter(); By the way using a different interface than Supplier can give the JVM more class hierarchy analysis info and so potentially allow for inlining even without static final. You can also simply use a closure in some cases sort of like this: interface IntBox { int get(); } public IntBox makeBox(int x) { return () -> x; } This is better for inlining because the JVM trusts final fields in VM anonymous classes more than yours. Unfortunately TrustStaticFinalFields cannot be a thing by default yet for backwards compatibility reasons. I think a lot of these things are pretty neat but unfortunately hard to package in a generic and usable library because people delving into these will want to tear into all the internal details for maximum performance. I don't really understand your StableField class. How is it supposed to be any faster than MostlyConstant? I would suggest if you wanted the best speed (in some ways and at a cost of memory) you could spin a static final class with a method that returns a constantdynamic entry and then return a methodhandle to that entry. This seems possibly heavyweight IMO so I'm still thinking about this myself. If StringSwitchCallSite being a MutableCallSite seems possibly unneeded with a reworked API to me. I am highly suspicious TypeSwitch will increase performance in most cases. instanceof checks are highly optimized and give info that allow further optimizations. You might want to consider using/abusing the JVM's own inline caching behaviour for interfaces for some dispatching. It's not too hard to create a bag of interface implementations at runtime that all dispatch to separate CallSite implementations which can be faster than exactInvoker/your own MethodHandle lookup logic sometimes. I considered this for a ThreadLocalCallSite class I was making but I'm still not sure about the design. So basically one hack to get quicker thread local behaviour is to subclass Thread and add your own fields/methods like this: ((MyIface)Thread.currentThread()).doSpecific(); If you add your own bag of interface implementations then you can do this dynamically: MY_IFACE.invokeExact((BagThread)Thread.currentThread()).bag, ...); I'm not sure about the bytecode generation here though. I don't want to be too blase about that. It looks like you have some benchmarks setup but I don't see any txt files with any perf data listed. I mentioned a lot of gibberish earlier but problem my biggest advice would be to add more benchmarks and look at your benchmarks again and also get real world usage data.
De: "Steven Stewart-Gallus" <stevensele...@gmail.com>
À: "mechanical-sympathy" <mechanica...@googlegroups.com>
Envoyé: Jeudi 17 Janvier 2019 07:50:13
Objet: Re: Exotic classes
I've been working on similar issues trying to optimise something heavily. I made a similar class to this one (I even had a similar API) but I found I called it MostlyFinal instead. private static final MostlyConstant<Integer> FOO = new MostlyConstant<>(42, int.class); private static final IntSupplier FOO_GETTER = FOO.intGetter(); By the way using a different interface than Supplier can give the JVM more class hierarchy analysis info and so potentially allow for inlining even without static final.
You can also simply use a closure in some cases sort of like this: interface IntBox { int get(); } public IntBox makeBox(int x) { return () -> x; } This is better for inlining because the JVM trusts final fields in VM anonymous classes more than yours. Unfortunately TrustStaticFinalFields cannot be a thing by default yet for backwards compatibility reasons. I think a lot of these things are pretty neat but unfortunately hard to package in a generic and usable library because people delving into these will want to tear into all the internal details for maximum performance. I don't really understand your StableField class. How is it supposed to be any faster than MostlyConstant?
I would suggest if you wanted the best speed (in some ways and at a cost of memory) you could spin a static final class with a method that returns a constantdynamic entry and then return a methodhandle to that entry. This seems possibly heavyweight IMO so I'm still thinking about this myself.
If StringSwitchCallSite being a MutableCallSite seems possibly unneeded with a reworked API to me.
I am highly suspicious TypeSwitch will increase performance in most cases. instanceof checks are highly optimized and give info that allow further optimizations.
You might want to consider using/abusing the JVM's own inline caching behaviour for interfaces for some dispatching.
It's not too hard to create a bag of interface implementations at runtime that all dispatch to separate CallSite implementations which can be faster than exactInvoker/your own MethodHandle lookup logic sometimes.
I considered this for a ThreadLocalCallSite class I was making but I'm still not sure about the design. So basically one hack to get quicker thread local behaviour is to subclass Thread and add your own fields/methods like this: ((MyIface)Thread.currentThread()).doSpecific(); If you add your own bag of interface implementations then you can do this dynamically: MY_IFACE.invokeExact((BagThread)Thread.currentThread()).bag, ...);
I'm not sure about the bytecode generation here though. I don't want to be too blase about that. It looks like you have some benchmarks setup but I don't see any txt files with any perf data listed. I mentioned a lot of gibberish earlier but problem my biggest advice would be to add more benchmarks and look at your benchmarks again and also get real world usage data.
no, CHA only works on class, not on interface.
It's not the same semantics, you can not change a StableField more than once so you have the guarantee that once the field is initialized, no deoptimization can occur.The other things is the object is not constant, you will get an exception so it's hard to misuse that API.The last point is that i expect that at some point i will change the implementation so the slowpath will cost less than the slowpath of MostlyConstant, but i've never had the time to think how to do that in a VM independant way (i know how to do that with Hotspot only).
but it means that you have if/else branch for String that the program has never encounter.I prefer to not add all the branches statically but add then at runtime dynamically when i know i need then.
private static final class Helper {
static final Config CONFIG = initConfig(configStr);
}
private String configStr = null;
public static Config getConfig() {
return Helper.CONFIG;
}
private static Config initConfig(String initStr){
// ...
}It will not be inlined by the VM :(
About having a ThreadLocalCallSite, as part of project Loom, we are discussing about how associate a value to a part of the callstack.Anyway, the VM doesn't have a code cache per thread, there is only a global code cache.
De: "Steven Stewart-Gallus" <stevensele...@gmail.com>
À: "mechanical-sympathy" <mechanica...@googlegroups.com>
Envoyé: Samedi 19 Janvier 2019 05:19:06
Objet: Re: Exotic classes
On Friday, January 18, 2019 at 5:37:58 AM UTC-8, Rémi Forax wroteno, CHA only works on class, not on interface.You're probably know better than me. I seem to remember there's something like that for interfaces but very limited such as if you ever only have ever one implementation.
--
You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-symp...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
De: "mechanical-sympathy" <mechanica...@googlegroups.com>
À: "mechanical-sympathy" <mechanica...@googlegroups.com>
Envoyé: Lundi 22 Avril 2019 20:25:01
Objet: Re: Exotic classes
These classes (especially MostlyConstant) are pretty cool. I have some questions about them:1. In MostlyConstant I noticed that modifying the constant doesn't call MutableCallSite.syncAll(). Should it?
2. A followup to #1: MutableCallSite.syncAll() doesn't actually seem implemented in OpenJDK. Is it correct to call it?
3. In my non-scientific JMH measurements, it seems like modifying the call site takes about 1us. Does that sound about right? From what I can tell modifying the constant is akin to deoptimizing the code and recompiling it, which means that 1us seems really fast.
On Monday, February 26, 2018 at 11:29:19 AM UTC-8, Remi Forax wrote:Hi all,
i'm preparing a talk at DevoxxFR on how to make a field value, a returned value, etc constant for any decent JITs (everything but c1), so i've bundled together several patterns i use for implementing dynamic language runtimes into an Java API
https://github.com/forax/exotic
I would like to have your comments about those exotic classes (it's already has been done, it's stupid, it's not thread safe, etc)
regards,
Rémi
De: "mechanical-sympathy" <mechanica...@googlegroups.com>
À: "mechanical-sympathy" <mechanica...@googlegroups.com>
Envoyé: Lundi 22 Avril 2019 20:25:01
Objet: Re: Exotic classesThese classes (especially MostlyConstant) are pretty cool. I have some questions about them:1. In MostlyConstant I noticed that modifying the constant doesn't call MutableCallSite.syncAll(). Should it?yes, you're right from a spec POV i should call syncAll(),the thing is that the current OpenJDK implementation doesn't need syncAll, but it may change in the future or for another implementation.2. A followup to #1: MutableCallSite.syncAll() doesn't actually seem implemented in OpenJDK. Is it correct to call it?yes, it's not implemented because the native part of setTarget() do the deopt, but it's an implementation detail.3. In my non-scientific JMH measurements, it seems like modifying the call site takes about 1us. Does that sound about right? From what I can tell modifying the constant is akin to deoptimizing the code and recompiling it, which means that 1us seems really fast.it's quite fast but it may be because- it may be not optimized yet- the VM mark all the different generated assembly codes that reference the constant as should be removed from the code cache and will do it later,- the VM will not re-optimize directly if a code is deoptimized, but jump in the interpreter and re-optimize later.
so there is a good chance that what you are measuring is not all de-optimization cost.Rémi
On Monday, February 26, 2018 at 11:29:19 AM UTC-8, Remi Forax wrote:Hi all,
i'm preparing a talk at DevoxxFR on how to make a field value, a returned value, etc constant for any decent JITs (everything but c1), so i've bundled together several patterns i use for implementing dynamic language runtimes into an Java API
https://github.com/forax/exotic
I would like to have your comments about those exotic classes (it's already has been done, it's stupid, it's not thread safe, etc)
regards,
Rémi
1. Why
|
interface ObjectSupport<T> {
public boolean equals(T self, T other);
public int hashCode(T obj);
public static <T, U extends ObjectSupport<T>> T of(Lookup lookup, Class<U> iface, Class<T> obj) {
// impl details
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface ObjectSupportField {
}De: "Steven Stewart-Gallus" <stevensele...@gmail.com>
À: "mechanical-sympathy" <mechanica...@googlegroups.com>
Envoyé: Vendredi 26 Avril 2019 01:51:47
Objet: Re: Exotic classes
and not something like?
1. Why publicabstractclassObjectSupport{
publicabstractboolean equals(Objectself,Object other);
publicabstractint hashCode();
publicstaticObjectSupport of(Lookup lookup,String... fields){
// impl details
}
// impl details
}
interfaceObjectSupport<T>{
publicboolean equals(T self, T other);
publicint hashCode(T obj);
publicstatic<T, U extendsObjectSupport<T>>T of(Lookup lookup,Class<U> iface,Class<T> obj){
// impl details
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interfaceObjectSupportField{
}
2. I don't understand why you can't use https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#defineClass-byte:A- instead of unsafe. If you can use https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html things might be easier to optimise because the VM doesn't trust nonstatic final fields but I don't think you'll need to rely on that.
3. The raw class file isn't always available at runtime so you can't necessarily use ObjectSupportImpl as a template.
4. https://github.com/forax/exotic/blob/master/src/main/java/com.github.forax.exotic/com/github/forax/exotic/ObjectSupport.java#L180 Pretty sure you want a static final field here. You can do that if you're using ObjectSupportImpl as a raw template. Unfortunately defineClass doesn't accept arguments so you have to use wonky garbage like a hashmap to pass runtime data that can't be embedded in a class file easily.
interface HashSupport<T> {
public static <T> HashSupport<T> of(Lookup lookup, String... fields) {
var mh = ObjectSupportImpl.createHasher(lookup, fields);
return (obj) -> {
mh.invokeExact(obj);
};
}
int hashCode(T obj);
}To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsub...@googlegroups.com.
De: "Steven Stewart-Gallus" <stevensele...@gmail.com>
À: "mechanical-sympathy" <mechanica...@googlegroups.com>
Envoyé: Samedi 27 Avril 2019 00:15:07
Objet: Re: Exotic classes
It seems to me you have to do a lot of hacky stuff to get around the "ugly" API as you call it.
Maybe it'd be better to separate out the ObjectSupport class into two separate classes, a HasherSupport class and an EqualsSupport class.
interfaceHashSupport<T>{
publicstatic<T>HashSupport<T> of(Lookup lookup,String... fields){
var mh = createMh(lookup, fields);
return(obj)->{
mh.invokeExact(obj);
};
}
int hashCode(T obj);
}
and similar for EqualsSupport.
I feel like ObjectSupport is a bit nebulous and open ended and you'd inevitably end up needing more support methods such as a toString method.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-symp...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-symp...@googlegroups.com.
interface Getter<R, T> {
}
interface ObjectGetter<R, T> {
public T get(R record);
}
interface ValueGetter<R, T> extends Getter<R, T> {
}
interface IntGetter extends Getter<R, Integer> {
public int get(R record);
}
private static final Set<Getter<Point, ?>> GETTERS = Set.of((s) -> sx, (s) -> s.y);
private static final EqualsSupport<Point> EQUALS = EqualsSupport.of(GETTERS)
private static final HashsSupport<Point> HASHER = HashSupport.of(GETTERS);
private static final Stringer<Point> TOSTRING = Stringer.of(GETTERS);
// etc..
static <R> EqualsSupport<R> makeEquals(Set<Getter<R, ?>> getters) {
EqualsSupport<O> equals = (a, b) -> true;
for (var getter : getters) {
if (getter instanceof ObjectGetter) {
var g = (ObjectGetter<R, ?>) getter;
var oldEquals = equals;
equals = (a, b) -> oldEquals.apply(a, b) && Object.equals(g.get(a), g.get(b)));
} else if (getter instanceof IntGetter) {
var g = (IntGetter<R>) getter;
var oldEquals = equals;
equals = (a, b) -> oldEquals.apply(a, b) && g.get(a) == g.get(b));
} else {
// etc...
}
}
return equals;
}
static <R> EqualsSupport<R> makeEquals(Set<Getter<R, ?>> getters) {
EqualsSupport<O> equals = (a, b) -> true;
for (var getter : getters) {
if (getter instanceof ObjectGetter) {
var g = (ObjectGetter<R, ?>) getter;
var oldEquals = equals;
equals = (a, b) -> oldEquals.apply(a, b) && Object.equals(g.get(a), g.get(b)));
} else if (getter instanceof IntGetter) {
var g = (IntGetter<R>) getter;
var oldEquals = equals;
equals = (a, b) -> oldEquals.apply(a, b) && g.get(a) == g.get(b));
} else {
// etc...
}
}
return equals;
}