Al mio lavoro abbiamo una DSL per specfying formule matematiche, che in seguito applicheremo a molti punti (in milioni).MethodHandles o LambdaMetafactory?
A partire da oggi, costruiamo un AST della formula e visitiamo ciascun nodo per produrre quello che chiamiamo un "Valutatore". Passiamo quindi a quel valutatore gli argomenti della formula, e per ogni punto fa il calcolo.
Ad esempio, si ha che la formula: x * (3 + y)
┌────┐
┌─────┤mult├─────┐
│ └────┘ │
│ │
┌──v──┐ ┌──v──┐
│ x │ ┌───┤ add ├──┐
└─────┘ │ └─────┘ │
│ │
┌──v──┐ ┌──v──┐
│ 3 │ │ y │
└─────┘ └─────┘
nostro valutatore emetterà "Valutare" oggetti per ogni passo.
Questo metodo è facile da programmare, ma non molto efficiente.
Così ho iniziato a esaminare le maniglie del metodo per creare un handle del metodo "composto" per accelerare le cose ultimamente.
Qualcosa lungo questo: io ho la mia classe "aritmetica" con:
public class Arithmetics {
public static double add(double a, double b){
return a+b;
}
public static double mult(double a, double b){
return a*b;
}
}
E quando costruire la mia AST io uso MethodHandles.lookup() per ottenere direttamente una maniglia su quelli e li compongono. Qualcosa su questa linea, ma su un albero:
Method add = ArithmeticOperator.class.getDeclaredMethod("add", double.class, double.class);
Method mult = ArithmeticOperator.class.getDeclaredMethod("mult", double.class, double.class);
MethodHandle mh_add = lookup.unreflect(add);
MethodHandle mh_mult = lookup.unreflect(mult);
MethodHandle mh_add_3 = MethodHandles.insertArguments(mh_add, 3, plus_arg);
MethodHandle formula = MethodHandles.collectArguments(mh_mult, 1, mh_add_3); // formula is f(x,y) = x * (3 + y)
Purtroppo, sono piuttosto deluso dai risultati. Ad esempio, la costruzione effettiva dell'handle del metodo è molto lunga (a causa delle chiamate a MethodHandles :: insertArguments e ad altre funzioni di tali composizioni) e l'accelerazione aggiunta per la valutazione inizia a fare la differenza dopo oltre 600k di iterazioni.
A 10 milioni di iterazioni, il metodo Handle inizia a brillare veramente, ma milioni di iterazioni non sono (ancora?) Un tipico caso d'uso. Siamo più vicini a 10k-1M, dove il risultato è misto.
Inoltre, il calcolo effettivo è accelerato, ma non così tanto (~ 2-10 volte). Mi aspettavo la cosa di eseguire un po 'più veloce ..
Quindi, comunque, ho iniziato di nuovo purga StackOverflow, e vide i fili LambdaMetafactory come questi: https://stackoverflow.com/a/19563000/389405
E sto prurito per iniziare a provare questo. Ma prima di ciò, vorrei il vostro input su alcune domande:
Devo essere in grado di comporre tutti quei lambda. MethodHandles offre molti modi (lenti, ammisamente) per farlo, ma mi sento come se lambda avesse una "interfaccia" più rigida, e non riesco ancora a spiegarmi come farlo. Sai come?
lambda e le maniglie del metodo sono piuttosto interconnesse e non sono sicuro che otterrò una significativa accelerazione. Vedo questi risultati per semplici lambda:
direct: 0,02s, lambda: 0,02s, mh: 0,35s, reflection: 0,40
ma per quanto riguarda i lambda composti?
Grazie ragazzi!
C'è un componente dinamico che suggerisce tali approcci? Un albero ordinario di oggetti (preferibilmente immutabili) non è poi così male. – Holger
Il nostro albero di oggetti utilizza effettivamente un'interfaccia e abbiamo discusso il costo di tutto il dispatch dinamico e abbiamo pensato che avremmo probabilmente ottenuto un handle di metodo personalizzato per questo. I miei test dimostrano che questo è il caso di grandi alberi o con molte iterazioni per piccoli alberi. Ora mi chiedo se lambdas potrebbe migliorare un po 'di più – Gui13
Hai eseguito benchmark effettivi sul "costo di tutta la spedizione dinamica"? Se hai un albero immutabile, HotSpot è in genere molto bravo a fare inline aggressive. E dovrai fare affidamento su questa capacità anche quando usi lambdas, dato che sono progettati anche attorno alle interfacce. – Holger