201305 - Lambda by R. Forax

653 views
546 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
653
On SlideShare
0
From Embeds
0
Number of Embeds
127
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

201305 - Lambda by R. Forax

  1. 1. Dans les traboulesdes lambdasRémi ForaxUPEMMai 2012
  2. 2. 2MoiMCF à lUniversité Paris Est Marne-la-valléeJoue avec Java depuis trop longtemp pourlavouerCréateur de langages dynamiques ou pasExpert pour les JSR 292 (invokedynamic) etJSR 335 (lambda)Contributeur OpenJDK, ASM, Tatoo, PHP.reboot, JDart,etc...
  3. 3. 3Un exemple de lambdaenum Gastronomy {rosette,andouillette,coq_au_vin,tripe,cardoon}private static List<Gastronomy> getGastronomyList() {List<Gastronomy> list = Arrays.asList(Gastronomy.values());Collections.sort(list, new Comparator<Gastronomy>() {@Overridepublic int compare(Gastronomy g1, Gastronomy g2) {return g1.name().compareTo(g2.name());}});return list;}public static void main(String[] args) {System.out.println(getGastronomyList());}
  4. 4. 4Exemple un poil plus compliquéprivate static ArrayList<Gastronomy> prefixList(String prefix, List<Gastronomy> list) {ArrayList<Gastronomy> list2 = new ArrayList<>();for(Gastronomy gastronomy: list) {if (gastronomy.name().startsWith(prefix)) {list2.add(gastronomy);}}return list2;}private static List<Gastronomy> getGastronomyList(String prefix) {List<Gastronomy> list = Arrays.asList(Gastronomy.values());ArrayList<Gastronomy> list2 = prefixList(prefix, list);Collections.sort(list2, new Comparator<Gastronomy>() {@Overridepublic int compare(Gastronomy g1, Gastronomy g2) {return g1.name().compareTo(g2.name());}});return list2;}
  5. 5. 5Parties codantesprivate static ArrayList<Gastronomy> prefixList(String prefix, List<Gastronomy> list) {ArrayList<Gastronomy> list2 = new ArrayList<>();for(Gastronomy gastronomy: list) {if (gastronomy.name().startsWith(prefix)) {list2.add(gastronomy);}}return list2;}private static List<Gastronomy> getGastronomyList(String prefix) {List<Gastronomy> list = Arrays.asList(Gastronomy.values());ArrayList<Gastronomy> list2 = prefixList(prefix, list);Collections.sort(list2, new Comparator<Gastronomy>() {@Overridepublic int compare(Gastronomy g1, Gastronomy g2) {return g1.name().compareTo(g2.name());}});return list2;}
  6. 6. 6Closure ??Expression que lon veut voir comme une valeurLes closures/lambda existent dans plein dautreslangagesLisp/Clojure, Ruby, Groovy, Scala, C#,JavaScript/Python, et même C++/Objective CrécemmentEn Java, on a des classes annonymesYoupi !Nan, je rigole :)
  7. 7. 7Problème des classes anonymesVerbeux visuellementrapport signal/bruit pas terribleSémantiquement bizarreon veut envoyer une expression, créont une classe ...Perf discutable (pour des closures)création dune instance à chaque appel+ 1 classe sur le disque+ 1 instance de java.lang.Class etses métadatas en mémoire
  8. 8. 8Exemple de lambdas(sans lambdas)private static List<Gastronomy> getGastronomyList(String prefix) {List<Gastronomy> list = Arrays.asList(Gastronomy.values());ArrayList<Gastronomy> list2 = prefixList(prefix, list);Collections.sort(list2, new Comparator<Gastronomy>() {@Overridepublic int compare(Gastronomy g1, Gastronomy g2) {return g1.name().compareTo(g2.name());}});return list2;}
  9. 9. 9Exemple de lambdas(avec lambdas)private static List<Gastronomy> getGastronomyList(String prefix) {List<Gastronomy> list = Arrays.asList(Gastronomy.values());ArrayList<Gastronomy> list2 = prefixList(prefix, list);Collections.sort(list2, (Gastronomy g1, Gastronomy g2) -> {return g1.name().compareTo(g2.name());});return list2;}La syntaxe utilise -> (light arrow)et pas => (fat arrow) comme en Scala ou en C#
  10. 10. 10Inférence de type !Le compilateur peut calculer le type des paramétres dunelambda !la méthode sort() prend une liste de Gastronomydonc elle attends un Comparator<Gastronomy>private static List<Gastronomy> getGastronomyList(String prefix) {List<Gastronomy> list = Arrays.asList(Gastronomy.values());ArrayList<Gastronomy> list2 = prefixList(prefix, list);Collections.sort(list2, (Gastronomy g1, Gastronomy g2) -> {return g1.name().compareTo(g2.name());});return list2;}Le compilo nutilise pas lexpression de la lambda pour faire linférence !
  11. 11. 11Exemple de lambdas(avec lambdas + inférence)private static List<Gastronomy> getGastronomyList(String prefix) {List<Gastronomy> list = Arrays.asList(Gastronomy.values());ArrayList<Gastronomy> list2 = prefixList(prefix, list);Collections.sort(list2,(g1, g2) -> g1.name().compareTo(g2.name()) );return list2;}
  12. 12. 12Syntaxe: Lambda expressionsans paramètre() -> System.out.println("Vive lOL")avec un paramètre (+ inférence)employee -> employee.isManager()avec plusieurs paramètresen déclarant les types(int x, int y) -> x == ysans déclarer les types (+ inférence)(x, y) -> x == y
  13. 13. 13Syntaxe: Lambda instructionsans paramètre() -> {System.out.println("Vive lOL");}avec un paramètre (+ inférence)employee -> {return employee.isManager();}avec plusieurs paramètres (+ inférence)(index1, index2) -> {list.set(index1, list.get(index2));}
  14. 14. 14SémantiqueTypé par une functional interface (ex SAM)Runnable, Callable, Filter, Function, ...Une lambda nest pas un objet“this” représente la classe courante pas la lambdaUne lambda est convertissable en un objetqui implante une functional interfaceRunnable r = () -> System.out.println("LOL?")objet pas objet
  15. 15. 15Et les collections ?Ici on filtre une liste, donc au lieu deprivate static ArrayList<Gastronomy> prefixList(String prefix, List<Gastronomy> list) {ArrayList<Gastronomy> list2 = new ArrayList<>();for(Gastronomy gastronomy: list) {if (gastronomy.name().startsWith(prefix)) {list2.add(gastronomy);}}return list2;}
  16. 16. 16Et les collections ?On aimerait bien écrireprivate static List<Gastronomy> prefixList(String prefix, List<Gastronomy> list) {return list.filter(g -> g.name().startsWith(prefix)).toList()}Operation lazyOperation pas lazyHeu, toList() sur une liste, vraiment ??
  17. 17. 17Programmation déclarativelist.filter(...).map(...).reduce(...)on veut éviter les structures de donnéesintermédiairelambdaslist filter map reducelist filter mapreduceCréation (pipeline)exécution
  18. 18. 18Programmation déclarativePermet la programmation parallèleOn split les données en amont en on envoie le calcul surdifférentes threads● Disponible pour Java 8Permet dutiliser les GPUsOn émet le code PTX/HSAIL correspondant● Projet Sumatra (prototype avec Graal)With great power there must also come — greatresponsibility!Lutilisateur ne doit pas faire deffet de bord dansles lambdas !!
  19. 19. 19Interface java.util.StreamSequentielle ou parallelelist.stream() ou list.parallelStream()Deux types de methodeintermédiairefilter, map, sorted, distinct, flatMap ...terminalesforEach, reduce, collect, findFirst ...Limplantation nutilise pas de structuresintermédaires mais un pipeline !Un effet de bord, tes mort !
  20. 20. 20Sans lAPI des streamsprivate static ArrayList<Gastronomy> prefixList(String prefix, List<Gastronomy> list) {ArrayList<Gastronomy> list2 = new ArrayList<>();for(Gastronomy gastronomy: list) {if (gastronomy.name().startsWith(prefix)) {list2.add(gastronomy);}}return list2;}
  21. 21. 21Avec lAPI des streamsprivate static ArrayList<Gastronomy> prefixList(String prefix, List<Gastronomy> list) {return list.stream().filter(g -> g.name().startsWith(prefix)).collect(Collectors.toList());}Bonus! pas besoin de déclarer la variable locale final !
  22. 22. 22Le coté sombre>= JDK8< JDK 8Où sont passées mes belles interfaces ?
  23. 23. 23TraitsPour ajouter le support des lambdas aucollectionsLes méthodes stream() et parallelStream()Il faut pouvoir ajouter des méthodes à uneinterfaceIl faut mettre du code dans les interfacesDont panicCela sappelle des traits(trivia: les traits de Scala ne sont pas des traits)
  24. 24. 24Default methodsUne méthode qui a du code dans une interface doitêtre taggée defaultUne méthode par défaut est utilisée par une classe si ilny a pas de méthode définie ou fournie par une sousclassepublic interface Iterator<T> {public abstract boolean hasNext();public abstract T next();public default void remove() {throw new UnsupportedOperationException();}}
  25. 25. 25Et le problème du diamandUne interface accepte du codemais pas de champsPas de problème de redéfinition de champ doncProblème si on implante deux interfaces qui ontchacune une méthode ayant du code– Si une interface hérite de lautre, on prend la méthode de lasous-interface– Sinon, le compilo planteinterface I { default void m() { } }interface J { default void m() { } }class A implements I, J {// compile pas}
  26. 26. 26Super sur des interfacesLa syntaxe, I.super.m() permet dappeler laméthode m de linterface I.donc pour résoudre le conflitinterface I { default void m() { } }interface J { default void m() { } }class A implements I, J {public void m() {I.super.m();}}
  27. 27. 27RetrofitAvec Java 8, linterface java.util.List possède enfin uneméthode sort (par défaut)qui appele Collections.sort :)private static List<Gastronomy> getGastronomyList(String prefix) {List<Gastronomy> list = Arrays.asList(Gastronomy.values());ArrayList<Gastronomy> list2 = prefixList(prefix, list);list2.sort((g1, g2) -> g1.name().compareTo(g2.name()));return list2;}
  28. 28. 28Avoir une méthode intermédiaire nest plus nécessaire, on peuttout écrire dans une seule méthodeEt en utilisant Comparators.comparing()private static List<Gastronomy> getGastronomyList(String prefix) {return Arrays.stream(Gastronomy.values()).filter(g -> g.name().startsWith(prefix)).sorted(Comparators.comparing(g -> g.name())).collect(Collectors.toList());}public static void main(String[] args) {System.out.println(getGastronomyList("c"));}Exemple (suite)
  29. 29. 29Method ReferenceIl existe une syntaxe spécifique pour leslambdas qui appel juste une méthodeg -> g.name() peut sécrire Gastronomy::nameOn ne spécifie pas le type des paramètres,le compilateur utilise la fonctional interface pourles trouverIl est aussi possible de capturer une valeurCallable<String> c = "foo"::toUpperCase()System.out.println(c.call()); // prints FOO
  30. 30. 30Avec des Method ReferencesEt tout dans le main.public static void main(String[] args) {Arrays.stream(Gastronomy.values()).filter(g -> g.name().startsWith("c")).sorted(Comparators.comparing(Gastronomy::name)).forEach(System.out::println);}
  31. 31. 31Hunder the hoodHunder the hood
  32. 32. 32Compilation - CréationUne référence sur une méthode est crée enutilisant invokedynamicBiFunction<String, Integer, Character> fun= String::charAt;est transformé par le compilateur encode:0: invokedynamic lambda() #0()Ljava/util/function/BiFunction;5: astore_1
  33. 33. 33Compilation - Créationconstants:#0: #15 invokestatic java/lang/invoke/LambdaMetafactory.metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;Method arguments:#16 invokeinterface java/util/function/BiFunction.apply:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;#17 invokevirtual java/lang/String.charAt:(I)C#18 (Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/Character;code:0: invokedynamic lambda() #0()Ljava/util/function/BiFunction;5: astore_1Functional interfaceMethodede linterfaceMethoderéférencéeSignature générique réifiée
  34. 34. 34Intérêt dutiliser invokedynamicLa création de lobjet implantant la functionalinterface nest pas écrite dans le code généréPeux changer en fonction des versions du JDK,des optimisations implantées dans la VMSi lobjet est constant, il peut être ré-utiliséPour le jdk8, utilise ASM pour générer uneclasse proxy dynamiquementet sans vérification du bytecode
  35. 35. 35Compilation - AppelLappel se fait en utilisant la méthode de linterfaceBiFunction<String, Integer, Character> fun = ...char c = fun.apply("foo", 0);et en bytecode7: ldc #3 // String “foo”9: iconst_010: invokestatic #4 // Integer.valueOf:(I)Ljava/lang/Integer;13: invokeinterface #5, 3 // BiFunction.apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;18: checkcast #6 // class Character21: invokevirtual #7 // Character.charValue:()C24: istore_2
  36. 36. 36Et les perfs ? / un nano-testpublic class HoodPerf {private static int COUNTER;private static void test() {BiFunction<String, Integer, Character> fun = String::charAt;char c = fun.apply("foobar", 5);COUNTER += c; // side effect}public static void main(String[] args) {for(int i=0; i<10_000; i++) {test();}System.out.println(COUNTER);System.out.println(end – start);}}Ce code ne marchepas pour faire unbenchmark mais ...
  37. 37. 37pour voir de lassembleur ...{0x00007fd655851530} test ()V in HoodPerf...d64d074300: mov %eax,-0x14000(%rsp)...d64d074307: push %rbp...fd64d074308: sub $0x20,%rsp...d64d07430c: mov $0xef010e68,%r10 ; {oop(a HoodPerf$$Lambda$1)}...d64d074316: mov 0x8(%r10),%r8d ; getClass...d64d07431a: mov $0xeeedaf80,%r11 ; {oop(a java/lang/Integer[256] )}...d64d074324: mov 0x210(%r11),%r11d ;*aaload; - java.lang.Integer::valueOf@21 (line 809)...d64d07432b: cmp $0xc6860240,%r8d ; {metadata(HoodPerf$$Lambda$1)}...d64d074332: jne ...d64d0743e9 ;*invokeinterface apply; - HoodPerf::test@13 (line 10)...d64d074338: mov 0xc(%r11),%ebp ;*getfield value; - java.lang.Integer::intValue@1 (line 871); implicit exception: dispatches to 0x00007fd64d074486...d64d07433c: test %ebp,%ebp...d64d07433e: jl ...d64d074401 ;*iflt; - java.lang.String::charAt@1 (line 650)...d64d074344: cmp $0x3,%ebp...d64d074347: jge ...d64d074401 ;*if_icmplt; - java.lang.String::charAt@10 (line 650)...d64d07434d: cmp $0x3,%ebp...d64d074350: jae ...d64d0743b7ahahcharAt
  38. 38. 38Pour info, le code de String.charAtpublic char charAt(int index) {if ((index < 0) || (index >= value.length)) {throw new StringIOOBException(index);}return value[index];}...070d06a53c: test %edx,%edx...070d06a53e: jl ...070d06a575 ;*iflt; - java.lang.String::charAt@1 (line 650)...070d06a540: mov 0xc(%rsi),%ebp ;*getfield value; - java.lang.String::charAt@6 (line 650)...070d06a543: mov 0xc(%rbp),%r10d ;*arraylength; - java.lang.String::charAt@9 (line 650); implicit exception: dispatches to ...070d06a589...070d06a547: cmp %r10d,%edx...070d06a54a: jge ...070d06a575 ;*if_icmplt; - java.lang.String::charAt@10 (line 650)...070d06a54c: cmp %r10d,%edx...070d06a54f: jae ...070d06a562...070d06a551: movzwl 0x10(%rbp,%rdx,2),%eax
  39. 39. 39et encore de lassembleur ......d64d074352: mov $0xef015e10,%r10 ; {oop([C)}...d64d07435c: movzwl 0x10(%r10,%rbp,2),%ebp ;*caload; - java.lang.String::charAt@27 (line 653)...d64d074362: cmp $0x7f,%ebp...d64d074365: jg ...d64d074411 ;*if_icmpgt; - java.lang.Character::valueOf@3 (line 4570)...d64d07436b: cmp $0x80,%ebp...d64d074371: jae 0x00007fd64d0743c9...d64d074373: mov $0xeef0e828,%r10 ; {oop(a java/lang/Character[128] )}...d64d07437d: mov 0x10(%r10,%rbp,4),%r10d ;*aaload; - java.lang.Character::valueOf@10 (line 4571)...d64d074382: test %r10d,%r10d...d64d074385: je ...d64d0743d9 ;*invokevirtual charValue...d64d074387: mov $0xeef05ab8,%r11 ; {oop(a java/lang/Class = HoodPerf)}...d64d074391: mov 0x58(%r11),%r11d ;*invokestatic valueOf...d64d074395: movzwl 0xc(%r10),%r10d...d64d07439a: add %r10d,%r11d...d64d07439d: mov $0xeef05ab8,%r10 ; {oop(a java/lang/Class = HoodPerf)}...d64d0743a7: mov %r11d,0x58(%r10) ;*putstatic COUNTER...d64d0743ab: add $0x20,%rsp...d64d0743af: pop %rbp...d64d0743b0: test %eax,0x9006c4a(%rip) # ...d65607b000; {poll_return}...d64d0743b6: retq Ça sert à quoi ??charAt
  40. 40. 40... en résuméUne méthode référence (ou une lambda) qui nefait pas de capture est une constanteLappel a une lambda peut être dévirtualisé etinliné comme nimporte quel appelLe code montré est celui générer par lejdk8b88-lambda avec elision duboxing/unboxing desactivé :(
  41. 41. 41Whats next ?Updates du JDK8lambda constante sans check de classelambda non-constante mieux dévirtualiséeJava 9Value ObjectInteger, Float, etc ne devrait pas avoir didentité=> pas sûr que cela marcheUtilisation du/des GPUs dans les Stream parallele
  42. 42. 42Questions ?JDK8 Feature Freeze jeudi prochain !les lambdas sont déjà dans le JDK !https://jdk8.java.net/download.htmlRemonter les bugs surla mailing list: lambda-devhttp://mail.openjdk.java.net/mailman/listinfo/lambda-devou bugs.sun.com

×