Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Panama Interconnecting Java with the native world.

Slide 3

Slide 3 text

$ whoami Brice Dutheil $ jobs Datadog / Senior Software Engineer Blablacar / Senior Software Engineer Libon / Software Engineer :█

Slide 4

Slide 4 text

https://www.oracle.com/a/ocom/img/obic-java-cup.svg https://upload.wikimedia.org/wikipedia/commons/1/18/OpenJDK_logo.svg 17

Slide 5

Slide 5 text

Le projet Panama atteint une nouvelle étape dans le JDK 17 ...toujours en incubation pour le JDK 18 ● JEP-412 Foreign Function & Memory API (Incubator) aka l’appel de fonctions étrangères au code Java ● JEP-414 Vector API (Second Incubator) aka l’API pour faire du calcul vectoriel

Slide 6

Slide 6 text

Qui a déjà utilisé du code natif depuis la JVM ?

Slide 7

Slide 7 text

Ce que je souhaite aborder ce soir ● Le projet panama, c’est quoi ? ● Appeler du code natif ○ Appels simple ○ Utiliser une API étrangère ● L’outil jextract ● Benchmark ● Pour finir

Slide 8

Slide 8 text

Ce que je souhaite aborder ce soir ● Le projet panama, c’est quoi ? ● Appeler du code natif ○ Appels simple ○ Utiliser une API étrangère ● L’outil jextract ● Benchmark ● Pour finir

Slide 9

Slide 9 text

Pourquoi Panama ? 1. Les libraries “natives” ○ Omniprésente ○ Fonction de l’OS, device, file system, réseau, fonctionnalités tierces par d’autres libraries 2. Interaction Java ⇔ Natif ardue ○ JNI, complexe à prendre en main, Possiblement: difficile à écrire du code correct et performant ○ Tant sur l’usage de l’API que du transfert de donnée À quoi doit-il succéder ? → JNI, JNA, JNR-FFI

Slide 10

Slide 10 text

Les technos actuelles : JNI À l’origine JNI (Java Native Interface) ● JNI est présent dans la JVM dès la version 1.1 ● Le code (vu de Java) ressemble à ça package q.r.s; class NativeBinding { static { System.load("{path/to/libNativeBinding.so}"); } // (1) public static native boolean isatty(int fileDescriptor); // (2) }

Slide 11

Slide 11 text

Les technos actuelles : JNI À l’origine JNI (Java Native Interface) ● JNI est présent dans la JVM dès la version 1.1 ● Le code (vu de Java) ressemble à ça package q.r.s; class NativeBinding { static { System.load("{path/to/libNativeBinding.so}"); } // (1) public static native boolean isatty(int fileDescriptor); // (2) }

Slide 12

Slide 12 text

Les technos actuelles : JNI À l’origine JNI (Java Native Interface) ● JNI est présent dans la JVM dès la version 1.1 ● Le code (vu de Java) ressemble à ça package q.r.s; class NativeBinding { static { System.load("{path/to/libNativeBinding.so}"); } // (1) public static native boolean isatty(int fileDescriptor); // (2) }

Slide 13

Slide 13 text

Les technos actuelles : JNI Il faut extraire des headers C/C++ de la classe Java $ javac -d classes -cp src -h jni q/r/s/NativeBinding.java 💡 Avant le JDK10 il faut utiliser javah. Voir JEP-313

Slide 14

Slide 14 text

Les technos actuelles : JNI Il faut extraire des headers C/C++ de la classe Java Coté natif il faut écrire du code de glue #include "q_r_s_NativeBinding.h" // (1) #include JNIEXPORT jboolean JNICALL Java_q_r_s_NativeBinding_isatty // (2) (JNIEnv *env, jclass cls, jint fileDescriptor) { return isatty(fileDescriptor)? JNI_TRUE: JNI_FALSE; } $ javac -d classes -cp src -h jni q/r/s/NativeBinding.java 💡 Avant le JDK10 il faut utiliser javah. Voir JEP-313

Slide 15

Slide 15 text

Les technos actuelles : JNI Il faut extraire des headers C/C++ de la classe Java Coté natif il faut écrire du code de glue Il faut compiler séparément #include "q_r_s_NativeBinding.h" // (1) #include JNIEXPORT jboolean JNICALL Java_q_r_s_NativeBinding_isatty // (2) (JNIEnv *env, jclass cls, jint fileDescriptor) { return isatty(fileDescriptor)? JNI_TRUE: JNI_FALSE; } $ gcc \ -I $JAVA_HOME/include \ # (1) -I $JAVA_HOME/include/linux \ # (2) -fPIC \ # (3) -shared \ # (4) -o NativeBinding.so \ # (5) NativeBinding.c $ javac -d classes -cp src -h jni q/r/s/NativeBinding.java 💡 Avant le JDK10 il faut utiliser javah. Voir JEP-313

Slide 16

Slide 16 text

Les technos actuelles : JNI À noter si le code utilise plutôt System.loadLibrary("NativeBinding") Alors ils faut indiquer ou chercher cette librairie $ java -Djava.library.path=path/to/jni-lib -cp classes q.r.s.NativeBinding

Slide 17

Slide 17 text

Les technos actuelles : JNI Pour appeler du code natif, la JVM doit effectuer quelques opérations. ● Initialiser une stack C valide ● Initialiser les registres du CPU (et les remettre en place) ● Instructions de barrières de mémoire (fencing) sur le CPU ● Empêche certaines optimizations comme l'inlining ● JNI permet de définir des sections critiques (Get*Critical) empêchant l’action du GC (au minimum sur une région) ● Échanger des données (objets, tableaux) peut demander de la copie et/ou de la sérialisation

Slide 18

Slide 18 text

Les technos actuelles : JNA ● Premier fragment montré dès 1999 et 1ère release en 2006 ● Windows uniquement au début ● Inventé par la team ayant bossé sur JNI ● Brique omniprésente ● Souvent le choix retenu pour interagir avec les librairies natives

Slide 19

Slide 19 text

Les technos actuelles : JNA JNA est facile à utiliser Il n’y a plus qu’à utiliser l’API public interface JNA_Library extends Library { JNA_Library INSTANCE = (JNA_Library) Native.loadLibrary( "c", JNA_Library.class ); // (1) boolean isatty(int fileDescriptor); // (2) } JNA_Library.INSTANCE.isattty( 0);

Slide 20

Slide 20 text

Les technos actuelles : JNA ● Support de types natif (wstring, structure, union, etc.) ● Beaucoup de reflection pour le ease-of-use ● Gère la sérialization, segments mémoire ● Repose tout de même sur JNI (invisible) ● Dispatch par la library native de JNA basé sur libffi libffi applique des routines écrites en assembleur à la main pour simuler un callsite C

Slide 21

Slide 21 text

Les technos actuelles : JNR-FFI JNR-FFI est un projet open source débuté en 2011. Son but : être aussi simple que JNA, avec les performances en plus. The Java Native Runtime Project https://github.com/jnr public interface IsATTY_JNRFFI { boolean isatty(int fileDescriptor); // (1) } import jnr.ffi.LibraryLoader; public class JnrffiIsATTY { public static void main(String[] args) { IsATTY_JNRFFI c = LibraryLoader.create(IsATTY_JNRFFI.class).load( "c"); System.out.printf("stdin : %s%n", c.isatty(0)); System.out.printf("stdout: %s%n", c.isatty(1)); System.out.printf("stderr: %s%n", c.isatty(3)); } }

Slide 22

Slide 22 text

Les technos actuelles : JNR-FFI ● Support de types natif (wstring, structure, union, etc.) ● Gestion des classloaders ● Génère du bytecode plutôt que de faire la reflection → nettement plus performant ● Tire parti également de libffi JNR-FFI est le projet qui inspirera le Projet Panama en 2014 ● JEP-191 Foreign Function Interface Initité notamment par Charles Nutter de JRuby

Slide 23

Slide 23 text

Projet Panama : Objectif monde natif Préserver la culture Java Opportunité : Tirer profit des MethodHandles Attention aux différences de : ● Syntaxe, sémantique ● Nommage, namespace ● Types de données ● Gestion de la mémoire ● Exceptions ● Performance ● Sécurité

Slide 24

Slide 24 text

Projet Panama Plus facile, plus sécurisé, plus rapide ➔ Support des Fonctions étrangères et des types de données étrangers (FFI) ➔ Flexible dans le transfert de donnée ➔ Support de types natif moderne, comme les vecteurs Initité notamment par John Rose (bosse sur le JVM depuis la version 1, participe à beaucoup de sous système : sécurité, Unsafe, indy, valhala, java-on-java, …)

Slide 25

Slide 25 text

JEP-412 Foreign Function & Memory API JEP-191 Foreign Function Interface JEP-338 Vector API (Incubator) JEP-370 Foreign Memory Access API (Incubator) JEP-383 Foreign Memory Access API (2nd Incubator) JEP-389 Foreign Linker API (Incubator) JEP-393 Foreign Memory Access API (3rd Incubator) JEP-414 Vector API (2nd Incubator) JEP-417 Vector API (3nd Incubator)

Slide 26

Slide 26 text

● Le projet panama, c’est quoi ? ● Appeler du code natif ○ Appels simple ○ Utiliser une API étrangère ● L’outil jextract ● Benchmark ● Pour finir

Slide 27

Slide 27 text

Code natif : Appels simples

Slide 28

Slide 28 text

libsodium Bibliothèque cryptographique (chiffrement, signatures, hashing, etc.) Écrite en C, multi-plateforme (OS, architectures) Usage facile et rapide

Slide 29

Slide 29 text

Étape 0 : Configuration JDK 17

Slide 30

Slide 30 text

Étape 0 : Configuration JDK 17 Incubation ⇒ le code est livré dans un module, à activer manuellement $ javac --add-modules jdk.incubator.foreign ... $ java --add-modules jdk.incubator.foreign ...

Slide 31

Slide 31 text

Étape 0 : Configuration JDK 17 Incubation ⇒ le code est livré dans un module, à activer manuellement Pour exécuter des appels natif il faut ajouter $ javac --add-modules jdk.incubator.foreign ... $ java --add-modules jdk.incubator.foreign ... $ java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign ... Toujours en discussion

Slide 32

Slide 32 text

Étape 0 : Configuration Faisable avec module-info.java module jmh.panama { requires jdk.incubator.foreign; // ... }

Slide 33

Slide 33 text

Étape 0 : Configuration Faisable avec module-info.java module jmh.panama { requires jdk.incubator.foreign; // ... } Module path… Toute les dépendances ne sont pas prêtes

Slide 34

Slide 34 text

Étape 1: Charger la bibliothèque 1. Utiliser une fonction système (eg de la libc, etc.) CLinker.systemLookup()

Slide 35

Slide 35 text

Étape 1: Charger la bibliothèque 1. Utiliser une fonction système (eg de la libc, etc.) CLinker.systemLookup() .lookup("getpid").get() → Addresse mémoire de la fonction (pour ce process)

Slide 36

Slide 36 text

Étape 1: Charger la bibliothèque System.load / System.loadLibrary monte la bibliothèque dans un segment mémoire Mémoire virtuelle du processus Java heap Stacks java Autres

Slide 37

Slide 37 text

Étape 1: Charger la bibliothèque System.load / System.loadLibrary monte la bibliothèque dans un segment mémoire Mémoire virtuelle du processus Java heap Stacks java Autres libsodium Le mécanisme de lookup va rechercher les symboles C dans ce(s) segment(s).

Slide 38

Slide 38 text

Étape 1: Charger la bibliothèque 1. Utiliser une fonction système (eg de la libc, etc.) 2. Charger une lib utilisateur a. En passant un chemin CLinker.systemLookup().lookup( "getpid").get() System.load("/usr/local/lib/libsodium.dylib "); SymbolLookup.loaderLookup().lookup( "crypto_box_sealbytes ").get(); 💡 lookup() retourne une adresse mémoire de la fonction native (pour ce process)

Slide 39

Slide 39 text

Étape 1: Charger la bibliothèque 1. Utiliser une fonction système (eg de la libc, etc.) 2. Charger une lib utilisateur a. En passant un chemin b. Avec le nom de la bibliothèque (Doit être présente dans un des dossiers de java.library.path) CLinker.systemLookup().lookup( "getpid").get() System.loadLibrary("sodium"); SymbolLookup.loaderLookup().lookup( "crypto_box_sealbytes ").get(); System.load("/usr/local/lib/libsodium.dylib "); SymbolLookup.loaderLookup().lookup( "crypto_box_sealbytes ").get(); 💡 lookup() retourne une adresse mémoire de la fonction native (pour ce process)

Slide 40

Slide 40 text

Ce qu’il se passe lors d’un downcallHandle *.lookup("getpid").get() ⇒ addresse lib Adresse de getpid

Slide 41

Slide 41 text

Ce qu’il se passe lors d’un downcallHandle CLinker.getInstance() Linker spécifique pour OS, Architecture (amd64|x86_64|aarch64) *.lookup("getpid").get() ⇒ addresse lib Adresse de getpid

Slide 42

Slide 42 text

Ce qu’il se passe lors d’un downcallHandle CLinker.getInstance() Linker spécifique pour OS, Architecture (amd64|x86_64|aarch64) *.lookup("getpid").get() ⇒ addresse Création de code exécutant la méthode native Code cache lib Adresse de getpid

Slide 43

Slide 43 text

Ce qu’il se passe lors d’un downcallHandle CLinker.getInstance() Linker spécifique pour OS, Architecture (amd64|x86_64|aarch64) Calcul de la taille de la stack native Arg 1 Arg 2 Arg 2 Stack 0x00CAFE *.lookup("getpid").get() ⇒ addresse Création de code exécutant la méthode native Code cache lib Adresse de getpid

Slide 44

Slide 44 text

Ce qu’il se passe lors d’un downcallHandle CLinker.getInstance() Linker spécifique pour OS, Architecture (amd64|x86_64|aarch64) Calcul de la taille de la stack native Copie des valeurs primitives dans la stack native Arg 1 Arg 2 Arg 2 Stack 0x00CAFE Java heap *.lookup("getpid").get() ⇒ addresse Création de code exécutant la méthode native Code cache lib Adresse de getpid

Slide 45

Slide 45 text

Ce qu’il se passe lors d’un downcallHandle CLinker.getInstance() Linker spécifique pour OS, Architecture (amd64|x86_64|aarch64) Calcul de la taille de la stack native Copie des valeurs primitives dans la stack native Arg 1 Arg 2 Arg 2 Stack 0x00CAFE Java heap Invocation native via JNI *.lookup("getpid").get() ⇒ addresse Création de code exécutant la méthode native Code cache lib Adresse de getpid

Slide 46

Slide 46 text

Ce qu’il se passe lors d’un downcallHandle CLinker.getInstance() Linker spécifique pour OS, Architecture (amd64|x86_64|aarch64) Calcul de la taille de la stack native Copie des valeurs primitives dans la stack native Arg 1 Arg 2 Arg 2 Stack 0x00CAFE Ret Stack 0x00CAFE Copie des valeurs primitives dans la stack native Java heap Java heap Invocation native via JNI *.lookup("getpid").get() ⇒ addresse Création de code exécutant la méthode native Code cache lib Adresse de getpid

Slide 47

Slide 47 text

Panama tire parti des MethodHandle pour appeler le code natif Dès Java 8, à la place de l’API de reflection Étape 2: Appel de méthode sans paramètre A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation, with optional transformations of arguments or return values. These transformations are quite general, and include such patterns as conversion, insertion, deletion, and substitution. JDK 1.7 MethodHandle mh = MethodHandles.lookup().findVirtual( TargetClass.class, "targetMetbod", MethodType.methodType(void.class, String.class) ); mh.invokeExact(targetInstance, "...");

Slide 48

Slide 48 text

Étape 2: Appel de méthode sans paramètre La génération de MethodHandle passe par CLinker Invocation classique de la référence (vers la méthode native) CLinker.getInstance().downcallHandle( methodAddress, // (1) MethodType.methodType( int.class), // (2) FunctionDescriptor.of(CLinker.C_INT) // (3) ); int result = (int) crypto_box_sealbytes.invokeExact();

Slide 49

Slide 49 text

Étape 3: Appel de méthode avec paramètres

Slide 50

Slide 50 text

Étape 3: Appel de méthode avec paramètres Pour passer des arguments vers le monde natif, il faut les copier en dehors de la Heap Java. Mémoire virtuelle du processus libsodium segment off-heap Java heap

Slide 51

Slide 51 text

Étape 3: Appel de méthode avec paramètres Pour passer des arguments vers le monde natif, il faut les copier en dehors de la Heap Java. Au retour de la méthode, il faut copier le résultat dans la Java Heap Mémoire virtuelle du processus libsodium segment off-heap Java heap

Slide 52

Slide 52 text

Étape 3: Appel de méthode avec paramètres Exemple avec crypto_box_keypair Cette méthode prend deux pointeurs ● vers une zone mémoire off-heap ● le consommateur à la responsabilité d’initialiser ces zones unsigned char recipient_pk[crypto_box_PUBLICKEYBYTES]; unsigned char recipient_sk[crypto_box_SECRETKEYBYTES]; crypto_box_keypair( recipient_pk, recipient_sk);

Slide 53

Slide 53 text

Étape 3: Appel de méthode avec paramètres MethodHandle crypto_box_keypair = CLinker.getInstance().downcallHandle( libsodiumLookup.lookup( "crypto_box_keypair" ).get(), MethodType.methodType( void.class, MemoryAddress.class, // pk MemoryAddress.class // sk ), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER) ); Représente les pointeurs vers pk et sk

Slide 54

Slide 54 text

Étape 3: Appel de méthode avec paramètres MethodHandle crypto_box_keypair = CLinker.getInstance().downcallHandle( libsodiumLookup.lookup( "crypto_box_keypair" ).get(), MethodType.methodType( void.class, MemoryAddress.class, // pk MemoryAddress.class // sk ), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER) ); var recipientPublicKey = MemorySegment.allocateNative(crypto_box_publickeybytes(), scope); var recipientSecretKey = MemorySegment.allocateNative(crypto_box_secretkeybytes(), scope); crypto_box_keypair.invokeExact( recipientPublicKey.address(), recipientSecretKey.address());

Slide 55

Slide 55 text

Étape 3: Appel de méthode avec paramètres MethodHandle crypto_box_keypair = CLinker.getInstance().downcallHandle( libsodiumLookup.lookup( "crypto_box_keypair" ).get(), MethodType.methodType( void.class, MemoryAddress.class, // pk MemoryAddress.class // sk ), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER) ); try (var scope = ResourceScope.newConfinedScope()) { var recipientPublicKey = MemorySegment.allocateNative(crypto_box_publickeybytes(), scope); var recipientSecretKey = MemorySegment.allocateNative(crypto_box_secretkeybytes(), scope); crypto_box_keypair.invokeExact(recipientPublicKey.address(), recipientSecretKey.address()); return new CryptoBoxKeyPair( recipientPublicKey.toByteArray(), recipientSecretKey.toByteArray() ); }

Slide 56

Slide 56 text

Scope et allocation ResourceScope : Aide à la gestion du cycle de vie des segments mémoire Le scope est passé à la création de la ressource, eg. MemorySegment::allocateNative ● globalScope ⇒ scope implicite, pas de gestion automatique RSS

Slide 57

Slide 57 text

Scope et allocation ResourceScope : Aide à la gestion du cycle de vie des segments mémoire Le scope est passé à la création de la ressource, eg. MemorySegment::allocateNative ● globalScope ⇒ scope implicite, pas de gestion automatique Pour une gestion explicite et déterministe (comprendre libère les segments à la sortie du try-with-resource) ● newConfinedScope ⇒ implique un restriction au thread en cours ● newSharedScope ⇒ pas de thread propriétaire RSS

Slide 58

Slide 58 text

Scope et allocation MemorySegment::allocateNative ⇒ effectue un malloc (allocateur système)

Slide 59

Slide 59 text

Scope et allocation MemorySegment::allocateNative ⇒ malloc (allocateur système) Stratégie d’allocation de la mémoire avec SegmentAllocator Interface avec 3 implémentations par défaut

Slide 60

Slide 60 text

Scope et allocation MemorySegment::allocateNative ⇒ malloc (allocateur système) Stratégie d’allocation de la mémoire avec SegmentAllocator Interface avec 3 implémentations par défaut ● ofScope ⇒ effectue un malloc

Slide 61

Slide 61 text

Scope et allocation MemorySegment::allocateNative ⇒ malloc (allocateur système) Stratégie d’allocation de la mémoire avec SegmentAllocator Interface avec 3 implémentations par défaut ● ofScope ⇒ effectue un malloc ● ofSegment ⇒ recycle le même segment

Slide 62

Slide 62 text

Scope et allocation MemorySegment::allocateNative ⇒ malloc (allocateur système) Stratégie d’allocation de la mémoire avec SegmentAllocator Interface avec 3 implémentations par défaut ● ofScope ⇒ effectue un malloc ● ofSegment ⇒ recycle le même segment ● arenaAllocator ⇒ Gestion de la mémoire par arène (région) new freed

Slide 63

Slide 63 text

Étape 3: Appel de méthode avec paramètres MethodHandle crypto_box_keypair = CLinker.getInstance().downcallHandle( libsodiumLookup.lookup( "crypto_box_keypair" ).get(), MethodType.methodType( void.class, MemoryAddress.class, // pk MemoryAddress.class // sk ), FunctionDescriptor.ofVoid(C_POINTER, C_POINTER) ); try (var scope = ResourceScope.newConfinedScope()) { var allocator = SegmentAllocator.ofScope(scope); var recipientPublicKey = allocator.allocate(crypto_box_publickeybytes()); var recipientSecretKey = allocator.allocate(crypto_box_secretkeybytes()); crypto_box_keypair.invokeExact(recipientPublicKey.address(), recipientSecretKey.address()); return new CryptoBoxKeyPair( recipientPublicKey.toByteArray(), recipientSecretKey.toByteArray() ); }

Slide 64

Slide 64 text

Étape 4: Appel de méthode avec paramètres Exemple avec crypto_box_seal Cette méthode ● prend trois pointeurs ○ le premier argument est le tableau ou sera stocké le message chiffré ● retourne un entier avec un status int crypto_box_seal( unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *pk)

Slide 65

Slide 65 text

Étape 4: Appel de méthode avec paramètres var crypto_box_seal = CLinker.getInstance().downcallHandle( libsodiumLookup.lookup( "crypto_box_seal ").get(), MethodType.methodType( int.class, MemoryAddress.class, // cipherText, output buffer MemoryAddress.class, // message long.class, // message length MemoryAddress.class // publicKey ), FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER, C_LONG_LONG, C_POINTER) );

Slide 66

Slide 66 text

Étape 4: Appel de méthode avec paramètres try (var scope = ResourceScope.newConfinedScope()) { var allocator = SegmentAllocator.ofScope(scope); var nativeMessage = CLinker.toCString(message, scope); var cipherText = allocator.allocate(crypto_box_sealbytes() + nativeMessage.byteSize()); var ret = (int) crypto_box_seal.invokeExact( cipherText.address(), nativeMessage.address(), (long) nativeMessage.byteSize(), allocator.allocateArray(C_CHAR, publicKey).address()); return cipherText.toByteArray(); }

Slide 67

Slide 67 text

Étape 4: Appel de méthode avec paramètres try (var scope = ResourceScope.newConfinedScope()) { var allocator = SegmentAllocator.ofScope(scope); var nativeMessage = CLinker.toCString(message, scope); var cipherText = allocator.allocate(crypto_box_sealbytes() + nativeMessage.byteSize()); var ret = (int) crypto_box_seal.invokeExact( cipherText.address(), nativeMessage.address(), (long) nativeMessage.byteSize(), allocator.allocateArray(C_CHAR, publicKey).address()); return cipherText.toByteArray(); }

Slide 68

Slide 68 text

Étape 4: Appel de méthode avec paramètres try (var scope = ResourceScope.newConfinedScope()) { var allocator = SegmentAllocator.ofScope(scope); var nativeMessage = CLinker.toCString(message, scope); var cipherText = allocator.allocate(crypto_box_sealbytes() + nativeMessage.byteSize()); var ret = (int) crypto_box_seal.invokeExact( cipherText.address(), nativeMessage.address(), (long) nativeMessage.byteSize(), allocator.allocateArray(C_CHAR, publicKey).address()); return cipherText.toByteArray(); }

Slide 69

Slide 69 text

Les classes essentielles ● CLinker ○ Définitions des types natifs char ⇒ C_CHAR, long long ⇒ C_LONG_LONG, pointeur ⇒ C_POINTER, varargs ○ Factory pour de MethodHandle via downcallHandle ○ Conversion Java String ⇔ C String ● MemorySegment ○ Représente une zone mémoire off-heap ○ Méthodes pour associer la disposition (e.g tableau de double) ○ Peut représenter un fichier memory mappé ● MemoryAddress ○ Représente une adresse dans la mémoire virtuelle du processus.

Slide 70

Slide 70 text

Les classes essentielles ● SegmentAllocator ○ Primitives de gestion de la mémoire ● ResourceScope ○ Supporte le périmètre et le cycle de vie de resources. ○ Imbricable Pour aller plus loin ● MemoryLayout ○ Outillage pour définir la disposition d’un segment de mémoire

Slide 71

Slide 71 text

Ce que je souhaite aborder ce soir ● Le projet panama, c’est quoi ? ● Appeler du code natif ○ Appels simple ○ Utiliser une API étrangère ● L’outil jextract ● Benchmark ● Pour finir

Slide 72

Slide 72 text

jextract jextract \ -d src/main/java \ -l sodium \ --source \ --target-package com.github.bric3.sodium \ -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ \ -I $(brew --prefix)/include/sodium \ @sodium.conf \ $(brew --prefix)/include/sodium.h jextract @sodium-only.conf $(brew --prefix)/include/sodium.h jextract \ -d src/main/java \ -l sodium \ --source \ --target-package com.github.bric3.sodium \ -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ \ -I $(brew --prefix)/include/sodium \ --dump-includes sodium.conf \ $(brew --prefix)/include/sodium.h Build 17-panama+3-167 https://jdk.java.net/panama/

Slide 73

Slide 73 text

Ce que je souhaite aborder ce soir ● Le projet panama, c’est quoi ? ● Appeler du code natif ○ Appels simple ○ Utiliser une API étrangère ● L’outil jextract ● Benchmark ● Pour finir

Slide 74

Slide 74 text

Demo REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial experiments, perform baseline and negative tests that provide experimental control, make sure the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. Do not assume the numbers tell you what you want them to tell. Benchmark Mode Cnt Score Error Units CryptoBoxSealBenchmark.jna avgt 50 160605.022 ± 1142.872 ns/op CryptoBoxSealBenchmark.jni avgt 50 150353.548 ± 750.460 ns/op CryptoBoxSealBenchmark.jnr avgt 50 213407.139 ± 26843.546 ns/op CryptoBoxSealBenchmark.panama avgt 50 151025.638 ± 637.727 ns/op

Slide 75

Slide 75 text

Demo Benchmark Mode Cnt Score Error Units CryptoBoxSealBenchmark.jna avgt 50 160197.866 ± 1009.495 ns/op CryptoBoxSealBenchmark.jni avgt 50 151259.674 ± 1446.900 ns/op CryptoBoxSealBenchmark.jnr avgt 50 179482.479 ± 1735.433 ns/op CryptoBoxSealBenchmark.panama avgt 50 152633.815 ± 1247.167 ns/op CryptoBoxSealBenchmark.panama_off_heap avgt 50 151091.119 ± 917.583 ns/op

Slide 76

Slide 76 text

Ce que je souhaite aborder ce soir ● Le projet panama, c’est quoi ? ● Appeler du code natif ○ Appels simple ○ Utiliser une API étrangère ● L’outil jextract ● Benchmark ● Pour finir

Slide 77

Slide 77 text

Pour finir

Slide 78

Slide 78 text

À voir aussi Appels natifs vers Java : upcallStub Memory mapping de fichiers MemoryAddress jm = CLinker.getInstance().upcallStub( MethodHandle, // the handle to the Java method FunctionDescriptor, // equivalent C signature ResourceScope // scope ); c_method.invokeExact( ..., jm.address() // pointeur vers la méthode Java ); MemorySegment.mapFile(path, // chemin du fichier 0, // offset Files.size(path), // taille du mapping FileChannel.MapMode.READ_ONLY, // mode scope);

Slide 79

Slide 79 text

À noter Changements à venir dans un prochain incubateur ● Ajustements autour des APIs de gestion de ressources, pooling, keep alive, … ● Outillage en cours de finalisation Une fois finalisée, l’API sera intégrée dans les modules par défaut, e.g. possible ajustements de signature, voire déplacement de méthode.

Slide 80

Slide 80 text

À noter Support de différents OS / architectures. Alignement…, etc. Support Android : Pas pour tout de suite ● Android SDK != JDK ● Déjà compliqué d’avoir un bon support de Java 8 (API Level 26 ajoute java.time, java.nio.file et java.lang.invoke)

Slide 81

Slide 81 text

Questions ? https://inside.java/ https://mail.openjdk.java.net/pipermail/panama-dev/ https://jdk.java.net/panama/ https://blog.arkey.fr/ @BriceDutheil