Pyjnius: PyJnius vs JPype

Créé le 16 juil. 2020  ·  27Commentaires  ·  Source: kivy/pyjnius

Je suis l'auteur principal de JPype. Dans le cadre de la mise à jour de la documentation de JPype, j'ai ajouté PyJnius à la liste des codes alternatifs à JPype. Malheureusement, après deux heures de jeu avec PyJnius, je n'ai pas pu trouver quoi que ce soit qui ressemble à un avantage de PyJnius par rapport à JPype. Chaque aspect que j'ai examiné depuis les proxys, les personnalisateurs, la gestion des tableaux multidimensionnels, l'intégration javadoc, la gestion GC, les tampons, l'intégration de code scientifique (numpy, matplotlib, etc.), les méthodes sensibles à l'appelant, la documentation et même la vitesse d'exécution dans la plupart des cas est actuellement couvert dans JPype plus complètement que PyJnius. Cependant, étant l'auteur de JPype, je regarde peut-être des aspects que j'apprécie plutôt que ceux de l'équipe PyJnius. Pouvez-vous mieux articuler les avantages de ce projet ? Quelle est la proposition de valeur de ce projet, quel est son public cible et que vise-t-il à faire que les alternatives ne couvrent pas déjà ?

Commentaire le plus utile

J'ai contacté tous les codes de pont Java il y a environ 2 ans. Malheureusement, le code PyJnius a apparemment été manqué car il n'est jamais apparu dans ma recherche. Je l'aurais également manqué lors de ce tour, sauf que je cherchais la page qui a fait le bon communiqué de presse technique la dernière fois et que je suis tombé sur un blog discutant des deux projets. Je ne sais pas comment j'ai raté un autre projet actif dans le même domaine pendant deux ans, mais c'était clairement de ma faute.

On dirait que vous avez confondu JPype avec Py4J qui est l'autre code de pont majeur. Ils font tout en utilisant des sockets avec à la fois les avantages et les inconvénients que cela implique. J'ai également trouvé que le projet ne répondait pas à mes exigences.

Je n'ai fait aucune recherche sur ce qui est nécessaire pour prendre en charge Android. Cependant, si j'ai des spécifications techniques, cela devrait être possible. Il n'y a rien que nous fassions qui soit en dehors du JNI natif et des anciens appels à l'API C de Python.

En ce qui concerne l'approche, JPype utilise JNI exclusivement pour marier une JVM à Python à l'aide de la commande "startJVM()". Bien que la prochaine version (2.0) offrira également la possibilité de faire le contraire dans lequel Python peut être démarré à partir de Java. Il le fait par le biais d'une approche par couches. Il existe une couche Python qui contient toutes les classes de haut niveau qui servent de frontal, un module privé CPython avec des classes de base contenant les points d'entrée, une couche de support C++ qui traite toutes les conversions et correspondances de type ainsi que le module natif pour la bibliothèque Java, et une bibliothèque Java qui contient toutes les tâches utilitaires (contenant la durée de vie des objets, créant des classes de support pour les tranches et les exceptions, et les extracteurs/rendu javadoc).

Il y a 8 ans, JPype était un peu le bordel. Il essayait de prendre en charge à la fois Ruby et Python, donc la couche C++ était un enchevêtrement de wrappers avec lesquels travailler et le front-end était entièrement en Python, donc c'était très lent. Il a également été bifurqué sur les types de retour, car le support numpy pouvait être compilé, entraînant le retour d'objets différents. Il fallait beaucoup de classes d'adaptateur comme JException pour agir comme des proxys là où les objets Python et Java natifs différaient. Mais tous ces problèmes ont été résolus dans les 3 ans depuis que j'ai rejoint le projet. Les deux principaux objectifs (pour moi) de JPype sont de fournir une syntaxe suffisamment simple pour que les physiciens soient familiarisés avec la programmation pour pouvoir utiliser Java et avoir des niveaux élevés d'intégration avec le code Python scientifique. Pour ce faire, nous faisons en sorte que les objets soutenus par Java aient des wrappers d'objets CPython "tous les fioritures". Plutôt que de convertir un tableau primitif Java, nous faisons d'un tableau primitif Java un nouveau type Python natif qui implémente tous les mêmes points d'entrée que numpy et tous ceux-ci sont soutenus par des transferts de mémoire tampon. Ainsi, nous avons des conversions rapides en appelant list(jarray) ou np.array(jarray) .

Pour l'adapter à Android, la séquence de démarrage devrait probablement être retravaillée et le code thunk qu'il utilise pour charger sa bibliothèque interne devrait être remplacé par un modèle JNI plus traditionnel. J'ai déjà supprimé le code thunk dans la prochaine version, donc la dernière a déjà été rencontrée. Seul le premier serait requis.

Les principales différences d'approche que je peux voir sont que PyJnius convertit les tableaux allant et venant. Cela semble être très prohibitif pour le codage scientifique où le passage de grands tableaux dans un sens (souvent qui ne sont jamais convertis) est le style JPype préféré. La décision d'exiger la conversion force alors des options telles que le passage par valeur et le passage par référence, mais cela entraîne potentiellement des problèmes plus importants, car si vous avez un appel multi-arguments, vous choisissez une politique pour tous les arguments. Cela rendrait également difficile le traitement des tableaux multidimensionnels. (Je pense qu'une classe d'adaptateur utilisée comme obj.method(1, jnius.byref(list1), list2) aurait fourni un meilleur contrôle si cela pouvait être réalisé). De plus, JPype a résolu de nombreux problèmes, tels que la liaison GC, les méthodes sensibles à l'appelant, etc. S'il n'y a rien d'autre, veuillez regarder le code JType et voir s'il y a de bonnes idées que vous pouvez utiliser.

Tous les 27 commentaires

Merci d'avoir tendu la main !

Je me souviens peut-être mal parce que cela fait des années que je n'ai pas regardé jpype, mais je pensais qu'il utilisait une approche différente, de parler à la JVM via un serveur (donc IPC) plutôt que de la mémoire partagée (mais votre fichier readme fait allusion au contraire, et je ne vois pas d'indices réfutant cela d'un rapide coup d'œil au code, donc je me trompe probablement totalement), et cette approche la rendait impraticable sur Android, par exemple (ce qui était en quelque sorte la raison principale du développement de pyjnius, bien que certaines personnes l'utilisent sur des plates-formes de bureau). D'un autre côté, je ne vois pas beaucoup d'indices sur la prise en charge d'Android dans la base de code JPype, à moins que ce ne soit le sujet du répertoire native , car son jni.h semble provenir d'AOSP ?

Mais honnêtement, nous n'étions pas du tout au courant de JPype lorsque le projet a été lancé, c'était juste un pas en avant pour avoir à coder manuellement le code jni pour s'interfacer à des classes Android spécifiques pour la prise en charge de kivy. Je crois que @tito a fait quelques recherches pour comparer plus tard, quand nous l'avons appris, mais je ne me souviens pas s'il a vu des raisons spécifiques de ne pas essayer de changer.

Salut. Je me souviens du nom JPype, mais au moment de chercher ce qu'on pouvait utiliser, je ne me souviens pas du tout pourquoi il n'était pas utilisé du tout honnêtement, il y a 8 ans :) Le seul objectif au départ était de pouvoir communiquer avec Android API, mais n'utilisant pas de serveur RPC intermédiaire comme le faisait le projet P4A à l'époque.

J'ai contacté tous les codes de pont Java il y a environ 2 ans. Malheureusement, le code PyJnius a apparemment été manqué car il n'est jamais apparu dans ma recherche. Je l'aurais également manqué lors de ce tour, sauf que je cherchais la page qui a fait le bon communiqué de presse technique la dernière fois et que je suis tombé sur un blog discutant des deux projets. Je ne sais pas comment j'ai raté un autre projet actif dans le même domaine pendant deux ans, mais c'était clairement de ma faute.

On dirait que vous avez confondu JPype avec Py4J qui est l'autre code de pont majeur. Ils font tout en utilisant des sockets avec à la fois les avantages et les inconvénients que cela implique. J'ai également trouvé que le projet ne répondait pas à mes exigences.

Je n'ai fait aucune recherche sur ce qui est nécessaire pour prendre en charge Android. Cependant, si j'ai des spécifications techniques, cela devrait être possible. Il n'y a rien que nous fassions qui soit en dehors du JNI natif et des anciens appels à l'API C de Python.

En ce qui concerne l'approche, JPype utilise JNI exclusivement pour marier une JVM à Python à l'aide de la commande "startJVM()". Bien que la prochaine version (2.0) offrira également la possibilité de faire le contraire dans lequel Python peut être démarré à partir de Java. Il le fait par le biais d'une approche par couches. Il existe une couche Python qui contient toutes les classes de haut niveau qui servent de frontal, un module privé CPython avec des classes de base contenant les points d'entrée, une couche de support C++ qui traite toutes les conversions et correspondances de type ainsi que le module natif pour la bibliothèque Java, et une bibliothèque Java qui contient toutes les tâches utilitaires (contenant la durée de vie des objets, créant des classes de support pour les tranches et les exceptions, et les extracteurs/rendu javadoc).

Il y a 8 ans, JPype était un peu le bordel. Il essayait de prendre en charge à la fois Ruby et Python, donc la couche C++ était un enchevêtrement de wrappers avec lesquels travailler et le front-end était entièrement en Python, donc c'était très lent. Il a également été bifurqué sur les types de retour, car le support numpy pouvait être compilé, entraînant le retour d'objets différents. Il fallait beaucoup de classes d'adaptateur comme JException pour agir comme des proxys là où les objets Python et Java natifs différaient. Mais tous ces problèmes ont été résolus dans les 3 ans depuis que j'ai rejoint le projet. Les deux principaux objectifs (pour moi) de JPype sont de fournir une syntaxe suffisamment simple pour que les physiciens soient familiarisés avec la programmation pour pouvoir utiliser Java et avoir des niveaux élevés d'intégration avec le code Python scientifique. Pour ce faire, nous faisons en sorte que les objets soutenus par Java aient des wrappers d'objets CPython "tous les fioritures". Plutôt que de convertir un tableau primitif Java, nous faisons d'un tableau primitif Java un nouveau type Python natif qui implémente tous les mêmes points d'entrée que numpy et tous ceux-ci sont soutenus par des transferts de mémoire tampon. Ainsi, nous avons des conversions rapides en appelant list(jarray) ou np.array(jarray) .

Pour l'adapter à Android, la séquence de démarrage devrait probablement être retravaillée et le code thunk qu'il utilise pour charger sa bibliothèque interne devrait être remplacé par un modèle JNI plus traditionnel. J'ai déjà supprimé le code thunk dans la prochaine version, donc la dernière a déjà été rencontrée. Seul le premier serait requis.

Les principales différences d'approche que je peux voir sont que PyJnius convertit les tableaux allant et venant. Cela semble être très prohibitif pour le codage scientifique où le passage de grands tableaux dans un sens (souvent qui ne sont jamais convertis) est le style JPype préféré. La décision d'exiger la conversion force alors des options telles que le passage par valeur et le passage par référence, mais cela entraîne potentiellement des problèmes plus importants, car si vous avez un appel multi-arguments, vous choisissez une politique pour tous les arguments. Cela rendrait également difficile le traitement des tableaux multidimensionnels. (Je pense qu'une classe d'adaptateur utilisée comme obj.method(1, jnius.byref(list1), list2) aurait fourni un meilleur contrôle si cela pouvait être réalisé). De plus, JPype a résolu de nombreux problèmes, tels que la liaison GC, les méthodes sensibles à l'appelant, etc. S'il n'y a rien d'autre, veuillez regarder le code JType et voir s'il y a de bonnes idées que vous pouvez utiliser.

@Thrameos une chose récente que nous cherchons à fusionner est l'utilisation de lambdas Python pour les interfaces fonctionnelles Java. Voir https://github.com/kivy/pyjnius/pull/515 ; Je n'ai pas vu ça dans JPype ?

JPype prend en charge les lambdas des interfaces fonctionnelles à partir de 1.0.0. Cela faisait partie des 30 tirages en 30 jours de mars à 1,0.

JPype a eu une longue période d'incubation. Commencé à l'origine en 2004 jusqu'en 2007. Il a ensuite connu un grand essor lorsque le groupe d'utilisateurs l'a ressuscité pour le porter sur Python 3 vers 2015. Puis en 2017, il a été récupéré pour être utilisé dans un laboratoire national qui l'a porté à partir de 0.6. 3 à 0.7.2. Au cours de cette période, tous les efforts ont été concentrés sur l'amélioration et le renforcement de la technologie de base qui fournit l'interface. Mais cela a finalement été achevé en mars après la deuxième réécriture du noyau afin que nous puissions enfin pousser la version 1.0.0. Depuis lors, nous avons ajouté tout ce qui "manquait" lors de ma campagne de liste de souhaits "30 pulls in 30 nights". L'arriéré de tout ce que je ne pouvais tout simplement pas mettre en œuvre parce que ce serait trop de travail a finalement été éliminé (les problèmes sont passés de 50 à 20, solliciter les utilisateurs pour demander ce dont ils ont besoin, etc.). Il se peut donc qu'un certain nombre de fonctionnalités que vous ne trouviez pas dans les versions précédentes soient désormais disponibles. J'ai traité la plupart des demandes de fonctionnalités en moins d'une semaine, de sorte qu'il ne me reste que les 3 gros (pont inversé, classes d'extension en Python, possibilité de démarrer une deuxième JVM).

Le projet retombera en sommeil car je suis dans un effort de 2 mois sur 6 mois pour terminer le code de pont inversé qui permettra à Java d'appeler Python et de générer des stubs pour les bibliothèques Python afin qu'elles puissent être utilisées comme bibliothèques natives Java. Il utilise ASM pour créer des classes Java à la volée afin que la prise en charge native de Python puisse être obtenue. Toujours pas complètement intégré comme Jython mais peut-être assez proche pour qu'il n'y ait pas beaucoup de différence.

Merci beaucoup pour les explications détaillées. le projet, alors félicitations pour tout le travail. Vous avez raison à propos de mon mélange avec Py4J, je suppose que lorsque j'ai regardé JPype, il devait être dans l'état désordonné que vous décrivez, et son utilisation a dû être beaucoup plus compliquée que PyJNIus à ce stade.

Vos points sur le passage par valeur/conversion sont très vrais et ont également déclenché une discussion récente ici, car @hx2A a examiné comment améliorer les performances et a rendu la conversion vers les types python facultative (comme passer une liste jetable à java, l'obtenir converti en une liste java, modifié par java ou non, et reconverti en python, juste pour le ramasser, était certainement sous-optimal, nous pouvons maintenant au moins éviter la deuxième partie, au prix d'utiliser des arguments de mot-clé, ce qui est sûr puisque java ne les prend pas en charge, il n'y a donc pas de conflit de signature, mais c'est certainement un peu plus bruyant en termes de syntaxe).

En ce qui concerne une différence possible entre JPype et PyJNIus, nous pouvons implémenter des interfaces java en utilisant des classes python et les passer à java pour être utilisées comme rappels, en revanche, nous aurions effectivement besoin de générer du bytecode java, si nous voulions étendre les classes java de python, car il est obligatoire d'utiliser une API Android, que nous ne pouvons pas couvrir pour le moment, je ne sais pas si je déduis correctement de votre commentaire, mais peut-être que vous n'avez pas la possibilité d'avoir des classes java pour appeler votre code python comme celui-ci (en utilisant des interfaces).

JType peut implémenter des interfaces en Python. Ajoutez simplement des décorateurs aux classes Python ordinaires.

from java.util.function import Consumer

@jpype.JImplements(Consumer)
class MyConsumer:
   @jpype.JOverride
   def apply(self, obj):
       pass

Utilisez une chaîne dans @JImplements si la classe est définie avant le démarrage de la JVM, plusieurs interfaces peuvent être implémentées à la fois mais toutes les méthodes sont vérifiées pour voir si elles sont implémentées. La principale différence étant que JPype utilise des méthodes de répartition (toutes les surcharges vont à la même méthode) plutôt que les méthodes surchargées. C'est parce que nous aimons pouvoir appeler les méthodes à la fois de Java et de Python. Je peux ajouter des surcharges individuelles si c'est une fonctionnalité souhaitée, mais personne ne l'a demandé.

(Edit : La raison pour laquelle nous n'avons pas utilisé l'héritage Python est qu'au moment où cela a été ajouté, nous soutenions toujours Python 2, ce qui causait de nombreux problèmes de métaclasse. évaluer une fois au moment de la déclaration étaient plus propres.)

Nous utilisons le même système pour implémenter des personnalisateurs (dunder ?) pour les classes

@jpype.JImplementationFor("java.util.ArrayList")
class ArrayListImpl:
    def __getitem__(self, i):
        return self.get(i)
    @jpype.JOverride
    def addAll(self, list):
        # Decide if we need to convert or can call directly.
        ...

Nous utilisons également des annotations pour définir les convertisseurs implicites tels que "Tous les objets Python Path convert to java.io.File"

 @jpype.JConversion("java.io.File", instanceof=pathlib.PurePath)
 def _JFileConvert(jcls, obj):
       Paths = jpype.JClass("java.nio.file.Paths")
       return Paths.get(str(obj))

Évidemment, l'utilisation de ce type de logique est un peu plus lente que les méthodes C spéciales, mais cela maintient la lisibilité et la flexibilité élevées. J'ai poussé les chemins critiques qui se sont avérés être des goulots d'étranglement vers C au besoin.

Nous fournissons également du sucre de syntaxe pour rendre les choses propres MyJavaClass@obj => transtypage en MyJavaClass (équivalent Java (MyJavaClass)obj ) ou cls=JInt[:] => créer un type de tableau ( cls=int[].class ) ou a=JDouble[10][5] => créer un tableau multidim ( double[][] a = new double[10][5] ).

J'ai travaillé sur le prototype pour étendre les classes de JPype. Nous avons le même problème que certaines classes Swing et d'autres API nécessitant des classes d'extension. La solution que j'ai concoctée jusqu'à présent consiste à créer une classe étendue avec un HashMap pour chacune des méthodes remplacées. S'il n'y a rien dans le hashmap pour ce point d'entrée, le transmet à super, sinon il appelle le gestionnaire de méthode proxy. Mais je décide que ce serait plus facile à mettre en œuvre une fois le pont inversé terminé afin que Java puisse réellement gérer les méthodes Python plutôt que de passer par la méthode proxy Java. Il me reste donc encore environ 6 mois avant que le prototype fonctionne. Vous pouvez consulter la branche epypj (mon nom pour le pont inverse) pour voir comment fonctionne l'appel de Java à Python ainsi que des modèles pour générer un invocateur à l'aide d'ASM pour créer des classes Java à la volée.

En ce qui concerne la gestion du ramasse-miettes, il y a peu de morceaux de JPype qui font ce travail. La première est la JPypeReferenceQueue (native/java/org/jpype/ref/JPypeReferenceQueue) qui lie la vie d'un objet Python à un objet Java. Ceci est utilisé pour créer des tampons et d'autres choses où Java a besoin d'accéder à un concept Python pendant un certain temps. La seconde consiste à utiliser des références globales afin que Python puisse contenir un objet Java dans la portée. Cela nécessite le lien du ramasse-miettes (native/common/jp_gc.cpp) qui écoute l'un ou l'autre système pour déclencher un GC et l'envoie à l'autre lorsque certaines conditions (taille des pools, croissance relative). Les derniers proxys doivent utiliser des références faibles car ils formeraient autrement des boucles (car le proxy contient une référence à la moitié Java et la moitié Java renvoie à l'implémentation Python). Finalement, j'ai l'intention d'utiliser un agent pour permettre à Python de traverser Java, mais c'est en cours de route.

Je fais partie de ceux qui utilisent pyjnius sur le bureau et non sur Android. Je ne connaissais pas JPype quand j'ai commencé à construire mon projet mais j'ai fait quelques recherches pour voir quelles sont les différences.

Une caractéristique unique de pyjnius est que l'appelant peut décider d'inclure ou non les méthodes et les champs protégés et privés. Ma préférence va au public uniquement, mais je comprends l'argument selon lequel rendre les champs et méthodes non publics disponibles est utile.

La performance est essentielle à mon projet. J'ai fait quelques tests avec la classe ci-dessous:

package org.pkg;

public class MyClass {

  public MyClass() {
  }

  public int number = 42;

  public float add1(float x, float y) {
    return x + y;
  }

  public float add2(float x, float y) {
    return x + y;
  }

  public float add2(int x, int y) {
    return x + y;
  }
}

Dans JType :

In [1]: import jpype
   ...: import jpype.imports
   ...: jpype.startJVM()
   ...: from org.pkg import MyClass
   ...: myInstance = MyClass()
   ...:

In [2]: %timeit myInstance.number
640 ns ± 2.65 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit myInstance.add1(10.3, 20.5)
2.13 µs ± 24.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [4]: %timeit myInstance.add2(10.3, 20.5)
2.19 µs ± 9.41 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

A Pyjnius :

In [1]: import jnius

In [2]: MyClass = jnius.autoclass('org.pkg.MyClass')

In [3]: myInstance = MyClass()

In [4]: %timeit myInstance.number
161 ns ± 0.104 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [5]: %timeit myInstance.add1(10.3, 20.5)
1.04 µs ± 8.16 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [6]: %timeit myInstance.add2(10.3, 20.5)
2.71 µs ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Pyjnius est un peu plus rapide, sauf pour la méthode surchargée. Nous devrions comparer les notes sur la façon de décider quelle méthode appeler lorsque la méthode est surchargée. Pyjnius a ce mécanisme de notation qui semble ajouter beaucoup de frais généraux. JPype prend cette décision beaucoup plus rapidement.

Enfin, à des fins de benchmark :

In [9]: def add(x, y):
   ...:     return x + y
   ...:

In [10]: %timeit add(10.3, 20.5)
82.9 ns ± 0.187 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

Bien sûr, les différences de quelques µs sont insignifiantes mais cela s'additionne si l'on fait des milliers de petits appels très rapidement, ce que je dois faire.

L'intégration de JPype avec numpy est assez agréable et facile à utiliser. Je peux voir comment les chercheurs pourraient l'utiliser pour écrire des scripts qui transmettent de grands tableaux aux bibliothèques Java sans syntaxe compliquée. J'ai également besoin de passer de grands tableaux et je le fais avec tobytes() et un code Java spécial qui peut recevoir les octets, mais évidemment ce n'est pas aussi net ou pratique.

La vitesse de JPype est malheureusement une sorte de perspective c'est ce que c'est. JPype est très défensif en essayant de se prémunir contre les mauvaises choses, ce qui signifie qu'il y a beaucoup de frais généraux non négligeables. Cela signifie, par exemple, qu'à chaque fois qu'un appel Java est effectué, il s'assure que le thread est connecté afin que nous ne fassions pas de segmentation s'il est appelé à partir d'un thread extérieur tel qu'un IDE. Mon groupe d'utilisateurs locaux sont tous des scientifiques, donc les points d'entrée sont tous très protégés contre des câblages croisés assez horribles. Si quelque chose leur tombe dessus (peu importe à quel point c'était fou), alors j'ai échoué. (Ce qui expliquerait les 1500 tests incluant la construction délibérée de mauvais objets.)

Deuxièmement, la vitesse des actions individuelles varie probablement beaucoup dans de très petites différences dans le travail effectué. L'exemple trivial que vous avez donné était l'un des pires cas. Selon le type de champ en cours d'accès, la vitesse d'accès peut en fait changer dans certains cas.

Pour votre exemple de vitesse, vous demandez un champ int.

  • Dans PyJnius, il crée un descripteur pour la recherche d'objet, accède au champ, crée un nouveau Python long, puis le renvoie.
  • Dans JPype, il crée un descripteur pour la recherche d'objet, accède au champ, crée un nouveau Python long, puis crée un type de wrapper pour un Java int, copie la mémoire longue Python dans le JInt (car Python n'a pas le moyen de créer un dérivé entier directement), puis lie le slot avec une valeur Java et renvoie enfin le JInt résultant.

Ainsi, même quelque chose d'aussi trivial que d'effectuer une analyse comparative de la vitesse d'accès à un champ n'est pas vraiment si trivial.
La différence étant que l'un a renvoyé un Python long et l'autre un entier Java réel (qui suit les règles de conversion Java) qui pourrait être transmis à une autre méthode surchargée et se lier correctement. Le travail pour renvoyer un type wrapper est bien plus que simplement renvoyer un type Python, d'où une énorme différence de vitesse.

J'ai essayé de le démontrer en testant quelques types de champs différents. Malheureusement, lorsque j'ai testé les champs d'objet, jnius a fait une erreur de segmentation sur le code "harness.objectField = harnais". Je ne sais pas pourquoi ce morceau de câblage croisé a causé un problème, mais il a échoué pour moi. Je ne me suis pas beaucoup intéressé à la vitesse de JPype au-delà de la suppression des contrevenants bruts, ce qui a donné un facteur de 3 à 5 pour accélérer les appels et 300 fois pour certains accès aux baies. Mais peut-être devrais-je revoir et voir quels domaines peuvent être améliorés. Je doute cependant que je puisse le réduire au strict minimum comme PyJnius sans supprimer la sécurité ou supprimer les contrats de retour (ce que je ne peux pas faire). Il y a au plus 10-30% d'accélération encore possible,

Quant à la fonctionnalité d'accès aux champs privés et protégés, il est certainement possible de le faire. Je préfère que les utilisateurs utilisent la réflexion ou d'autres méthodes d'accès internes plutôt que d'exposer directement l'objet. Si je devais fournir quelque chose comme ça, je créerais probablement un champ appelé _private qui contiendrait des champs qui ne sont pas exposés publiquement. JPype ne fournit qu'un wrapper de classe pour chaque type, donc je n'ai pas beaucoup de contrôles de grain fin. Vous ne pouviez donc pas choisir de créer une classe qui a un accès privé, puis de créer un deuxième objet du même type et de ne pas vous retrouver avec les privés exposés. J'ai emprunté cette voie avec la conversion de chaînes, et ce fut un désastre avec certaines bibliothèques choisissant une politique et d'autres une autre, ce qui entraînait des incompatibilités.

J'ai effectué des tests en utilisant la liste des tableaux.

import jpype
import timeit
jpype.startJVM()
ArrayList = jpype.JClass("java.util.ArrayList")

def pack():
    ja = ArrayList()
    for i in range(1000):
        ja.add(i)

def iter(ja):
    u = 0
    for i in ja:
        u+=i

def access(ja):
    u = 0
    for i in range(len(ja)):
        u+=ja.get(i)

def access2(ja):
    u = 0
    for i in range(len(ja)):
        u+=ja[i]


ja = ArrayList()
for i in range(1000):
   ja.add(i)

print("Pack arraylist %e"%( timeit.timeit("pack()", globals=globals(), number=1000)/1e6))
print("Iterate arraylist %e"%(timeit.timeit("iter(ja)", globals=globals(), number=1000)/1e6))
# Get is a direct call
print("Access(get) arraylist %e"%(timeit.timeit("access(ja)", globals=globals(), number=1000)/1e6))
# [] is emulated
print("Access([]) arraylist %e"%(timeit.timeit("access2(ja)", globals=globals(), number=1000)/1e6))

JType

Pack arraylist 2.768904e-06
Itérer la liste de tableaux 5.208071e-06
Accéder (obtenir) la liste de tableaux 4.037985e-06
Liste de tableaux Access([]) 4.690264e-06

Jnius

Pack arraylist 3.322248e-06
Itérer la liste de tableaux 4.099314e-06
Accéder (obtenir) la liste de tableaux 5.653444e-06
Liste de tableaux Access([]) 7.762727e-06

Ce n'est pas une histoire très cohérente si ce n'est de dire qu'ils sont probablement trivialement différents, sauf lorsqu'ils offrent des fonctionnalités très différentes. Si tout ce que vous faites est d'accéder à des méthodes, elles sont probablement assez similaires. Passer des tableaux préconvertis JPype est 100 fois plus rapide, convertir des listes et des tuples JPype est 2 fois plus lent (nous n'utilisons pas d'accès vectoriel ou n'avons pas de contournements spéciaux pour les tuples actuellement). Donc, en fin de compte, selon votre style de codage, cela peut être beaucoup plus rapide avec l'un ou l'autre. Mais mes utilisateurs choisissent généralement JPype pour sa facilité d'utilisation et sa construction à l'épreuve des balles plutôt que pour la vitesse. (Ah, je plaisante ! Ils sont tout aussi susceptibles de trébucher sur Internet parce que JPype s'est avéré être le premier lien qu'ils ont trouvé sur Google.)

Quant à la façon dont JPype lie les méthodes, ce détail devrait figurer dans le guide du développeur/utilisateur. La liaison de méthode commence par un pré-tri de la liste des méthodes pour l'envoi selon les règles données dans la spécification Java de sorte que si quelque chose cache quelque chose d'autre, il apparaisse en premier dans la liste (le code peut être trouvé dans native/java/org/jpype car nous utiliser une classe utilitaire Java pour effectuer le tri lors de la première création de la répartition). De plus, chaque méthode se voit attribuer une liste de préférences dont la méthode en cache une autre. La résolution commence par vérifier d'abord chaque argument pour voir s'il existe un "emplacement Java" pour l'argument. Les slots Java pointent vers des objets existants qui ne nécessitent aucune conversion, donc les éliminer avant la correspondance signifie que nous pouvons utiliser des règles directes plutôt que des règles implicites. Il fait ensuite correspondre les arguments basés sur les types en 4 niveaux (exact, implicite, explicite, aucun). Il raccourcit l'explicite et aucun pour passer à la méthode suivante. Si jamais il obtient un résultat exact, il raccourcit tout le processus pour passer à l'appel. S'il y a une correspondance, cela masquerait les méthodes qui ont une liaison moins spécifique. Si deux correspondances implicites sont trouvées qui n'étaient pas masquées, une erreur TypeError est générée. Une fois toutes les correspondances épuisées, les routines de conversion sont exécutées. Il libère ensuite le verrou global Python, effectue l'appel et réacquiert le verrou global. Le type de retour est recherché et un nouveau wrapper Python est créé en fonction du type renvoyé (il utilise des retours covariants afin que le type renvoyé soit le plus dérivé plutôt que le type de la méthode). Ceci est principalement linéaire avec le nombre de surcharges bien qu'il y ait quelques complexités pour les arguments variadiques, mais les tableaux pré-construits signifient qu'il essaierait foo(long, long) avant d'essayer foo(double, double) et un coup sur (long , long) empêcherait double, double de chaque correspondance en raison des règles de résolution de la méthode Java. Il y a encore quelques accélérations que je peux mettre en œuvre, mais cela nécessiterait des tables de mise en cache supplémentaires.

J'ai hérité du système de commande avec des raccourcis lorsque j'ai commencé le projet en 2017. J'ai ajouté la méthode masquant le cache et les emplacements Java pour éliminer la plupart de nos frais généraux.

J'ai optimisé le chemin d'exécution des méthodes. Les numéros révisés pour JPype sont

Pack arraylist 2.226081e-06
Itérer la liste de tableaux 4.082152e-06
Accéder (obtenir) la liste de tableaux 2.962606e-06
Liste de tableaux Access([]) 3.644642e-06

Mon groupe d'utilisateurs locaux sont tous des scientifiques, donc les points d'entrée sont tous très protégés contre des câblages croisés assez horribles. Si quelque chose leur tombe dessus (peu importe à quel point c'était fou), alors j'ai échoué.

Oui, les erreurs de segmentation sont horribles et j'en ai eu des centaines quand j'ai commencé à utiliser pyjnius. Je n'en ai pas eu depuis longtemps parce que j'ai peut-être résolu les problèmes de sécurité et les ai intégrés dans mon code. Maintenant, tout fonctionne de manière fiable. Je comprends votre cas d'utilisation cependant. Si vos utilisateurs sont des scientifiques travaillant directement avec les objets Java pour effectuer des analyses de données avec diverses bibliothèques Java, une erreur de segmentation leur ferait perdre tout leur travail. JPype semble mieux conçu pour effectuer un travail scientifique où les utilisateurs finaux travaillent directement avec les objets Java via Python. Le cas d'utilisation principal de pyjnius est cependant différent, qui communique avec Android. Dans ce cas, les problèmes de sécurité sont le problème du développeur, il est donc peut-être approprié de faire des choix différents sur la sécurité par rapport à la vitesse.

J'avoue que je ne suis pas un grand fan de "c'est sûr tant que vous marchez sur ces carrés dans cet ordre". Lorsque j'ai commencé à travailler sur JPype, il m'a fallu près d'un an pour mettre à l'épreuve tous les points d'entrée suffisamment pour que je puisse transmettre le code à mon groupe local. Et j'ai ajouté deux années supplémentaires d'armure API depuis. À part quelques rares personnes dont la JVM ne se charge pas (ce qui est très difficile à résoudre), il reste peu de problèmes car JPYpe a été mis aux normes du code de production.

En ce qui concerne le compromis entre vitesse et sécurité, la vitesse est excellente, mais si vous obtenez de la vitesse en lésinant sur les opérations sûres, c'est généralement un mauvais métier pour la plupart des utilisateurs. Que vous tourniez du code de prototypage ou que vous écriviez un système de production, devoir arrêter le travail et essayer de contourner une erreur de segmentation est une distraction à laquelle les utilisateurs ne devraient pas faire face.

Si quelqu'un est prêt à me donner des exemples de comment tester JPype sur un émulateur Android, je peux voir comment faire les modifications nécessaires.

Pour l'utiliser sur Android, nous empaquetons pyjnius en tant qu'art d'une distribution python construite par python-for-android, (en utilisant souvent buildozer comme interface plus simple, mais c'est la même chose), puis en construisant une application python qui embarque cette distribution, alors votre code python peut importer pyjnius ou toute autre bibliothèque python qui a été construite dans la distribution python lorsque l'utilisateur exécute l'application.

La première étape consiste donc à compiler jpype dans une distribution, ce qui est fait par p4a (https://github.com/kivy/python-for-android/) lorsque vous dites que cela fait partie de vos besoins, généralement ( mais pas toujours) une "recette" est nécessaire pour expliquer à p4a comment construire des bibliothèques qui ne sont pas du pur python, celle pour pyjnius peut être trouvée ici https://github.com/kivy/python-for-android/blob/ develop/pythonforandroid/recipes/pyjnius/__init__.py comme exemple. Si vous utilisez buildozer, vous pouvez utiliser le paramètre p4a.local_recipes dans buildozer.spec pour déclarer un répertoire dans lequel des recettes pour les exigences peuvent être trouvées, vous n'avez donc pas besoin de forker python-for-android pour faites utiliser votre recette.

Je conseillerais d'utiliser buildozer, car il automatise plus de choses https://buildozer.readthedocs.io/en/latest/installation.html#targeting -android et vous pouvez toujours configurer vos recettes locales pour essayer des choses. La première version prendra du temps, car elle doit créer python et un certain nombre de dépendances pour arm, et elle devra télécharger Android ndk et sdk pour cela. Vous pouvez probablement utiliser le bootstrap kivy par défaut pour l'application et créer une application de type "hello world", qui importerait jpype et afficherait simplement le résultat d'un code dans une étiquette, ou même dans le logcat en utilisant print, je ne le fais pas rappelez-vous à quel point kivy fonctionne dans l'émulateur Android, je ne l'ai jamais utilisé, mais je pense que certains utilisateurs l'ont fait, et avec la configuration de l'accélération, cela devrait fonctionner, sinon vous pouvez utiliser le wrapper sdl2 ou le webview, et utiliser un flacon ou serveur de bouteilles pour afficher des choses, j'essaierais d'abord avec le kivy car c'est de loin le plus testé.

Vous aurez besoin d'une machine Linux ou osx (VM est bien et WSL sur Windows 10 est bien), pour construire pour Android.

Si vous commencez à travailler sur une recette jpype pour python-for-android, il serait bienvenu d'ouvrir un PR en cours à ce sujet pour toute discussion qui pourrait survenir. Ce serait formidable si cela fonctionnait, surtout si cela pouvait effectivement résoudre certaines limitations de pyjnius de longue date. Comme discuté précédemment dans le fil, pyjnius couvre essentiellement nos exigences de base pour l'utilisation de kivy, mais il n'a pas assez de puissance de développement pour aller au-delà de manière significative.

@inclement J'ai mis en place un PR pour le port Android sur jpype-project/jpype#799. Malheureusement, je ne sais pas vraiment où aller à partir d'ici. Il semble essayer d'exécuter gradle, ce qui n'est vraiment pas le bon chemin de construction.

Les actions à réaliser sont les suivantes :

  • [x] Incluez tous les fichiers jpype/*.py dans le build (ou leurs versions pré-compilées).
  • [x] Exécutez Apache ant sur native/build.xml et placez le fichier jar résultant à un endroit où il est accessible.
  • [x] Incluez le fichier jar (ou équivalent) dans le build.
  • [x] Compilez le code C++ à partir de natif/commun et natif/python dans un module nommé _jpype à inclure dans la construction.
  • [x] Incluez un fichier main.py qui lance simplement un shell interactif afin que nous puissions le tester manuellement pour le moment.
  • [ ] À l'avenir, je dois inclure "ASM" ou quelque chose qui fonctionne comme ça pour Android afin que je puisse charger des classes créées dynamiquement.
  • [x] Corrigez le code C++ afin qu'il utilise un bootstrap personnalisé pour charger la JVM et le fichier jar compagnon et connecter toutes les méthodes natives.
  • [ ] Patchez le jvmfinder avec quelque chose qui fonctionne sur Android et "startJVM" est appelé automatiquement plutôt qu'au début de main.
  • [ ] Patch org.jpype afin que le système de navigation jar (qui est le fonctionnement des importations) puisse fonctionner sur Android.

J'ai examiné certains des documents, mais rien ne s'est vraiment démarqué quant à la façon d'y parvenir. La disposition de mon projet est quelque peu différente de la normale car nous ne mettons pas tout sous le module principal (car nous construisons en fait 3 modules qui composent le système. jpype, _jpype et org.jpype.) J'ai probablement besoin d'une recette personnalisée pour effectuer toutes ces actions ainsi que désactiver les modèles indésirables tels que l'exécution de gradle (à moins qu'il ne fasse quelque chose d'utile que je ne peux pas dire.)

Il semble essayer d'exécuter gradle, ce qui n'est vraiment pas le bon chemin de construction.

Gradle est l'outil de construction utilisé comme étape finale de l'empaquetage d'un APK, il n'est probablement pas lié à votre inclusion de jpype.

Incluez tous les fichiers jpype/*.py dans le build (ou leurs versions pré-compilées).

En général, si jpype fonctionne essentiellement comme un module Python normal, votre première tentative de recette fait probablement le gros du travail - le CppCompiledComponentsPythonRecipe fait quelque chose comme exécuter python setup.py build_ext et python setup.py install en utilisant l'environnement NDK. Cela devrait installer le package python jpype dans l'environnement python en cours de construction pour être inclus dans l'application.

Incluez le fichier jar (ou équivalent) dans le build.

C'est probablement une étape supplémentaire que la recette devra faire, il s'agira de copier votre fichier jar (ou tout ce dont vous avez besoin) dans un endroit approprié dans le projet Android que python-for-android est en train de construire.

Compilez le code C++ à partir de natif/commun et natif/python dans un module nommé _jpype à inclure dans la construction.

Si cela est géré par setup.py, cela devrait déjà fonctionner, mais peut nécessiter quelques ajustements. Si ce n'est pas le cas, vous pouvez inclure vos commandes de compilation dans la recette (et les faire construire pour l'environnement Android en définissant les variables d'environnement appropriées, comme vous le verrez en utilisant self.get_env dans d'autres recettes).

Corrigez le code C++ afin qu'il utilise un bootstrap personnalisé pour charger la JVM et le fichier jar compagnon et connecter toutes les méthodes natives.

J'espère que cette partie devrait être assez simple, en fonction de l'utilisation de la bonne fonction d'interface Android JNI. Nous faisons cela d'une manière légèrement bidon en patchant pyjnius plutôt que de faire une compilation conditionnelle appropriée, car différentes bibliothèques d'aide fournissent différentes enveloppes, comme le montre par exemple ce patch . Cette complexité n'aurait pas besoin de vous affecter, vous pouvez simplement appeler la bonne fonction api Android.

Patchez le jvmfinder avec quelque chose qui fonctionne sur Android et "startJVM" est appelé automatiquement plutôt qu'au début de main.

Je ne connais pas assez la JVM pour vraiment le savoir, mais je pense qu'Android veut que vous accédiez toujours à une JVM existante et que vous ne puissiez pas démarrer une nouvelle instance. Cela aura-t-il de l'importance (ou est-ce simplement faux ?) ?

Exécutez Apache ant sur native/build.xml et placez le fichier jar résultant à un endroit où il est accessible.

Je ne suis pas non plus sûr de celui-ci en raison de ma méconnaissance, est-ce juste une étape de construction jpype interne? Je ne sais pas comment les versions Java interagissent, mais je suppose que l'utilisation de la fourmi native du système devrait convenir ici.

Il a donné un avertissement que buildozer ne respecte pas setup.py donc il a sauté directement de l'étape de haut en bas. D'où la nécessité d'ajouter ces étapes intermédiaires manuellement. Notre setup.py effectue un tas d'étapes de compilation personnalisées telles que la création du fichier hooks pour fusionner le fichier jar avec dll qui ne sont probablement pas applicables ici, donc c'était bien qu'il ait été ignoré, mais il n'a pas non plus réussi à localiser les fichiers cpp et java qui sont définis dans setup.py. Une partie du problème est que mon setup.py était si volumineux que j'ai dû me diviser en module setupext.

Je suppose que je dois d'abord comprendre comment je peux exécuter une commande de nettoyage afin que le processus s'exécute une fois et affiche le journal. (Je l'ai lancé la première fois pendant que je jouais. Je n'ai donc pas de journal propre.) Nous aimerions également déplacer cette conversation vers le PR afin que nous puissions rester plus sur le sujet pour le fil.

FWIW J'ai pu porter le noyau de mon projet non Android vers JPype. Il y avait deux difficultés ou différences mineures :

  1. Le classpath kwarg dans jpype.startJVM() semble être ignoré quoi que je fasse. La fonction addClassPath() fonctionné cependant. Les développeurs qui se soucient de l'ordre des chemins de classe peuvent avoir besoin de faire quelques ajustements par rapport à la façon dont ils ont utilisé jnius_config.add_classpath() .

  2. L'implémentation d'interfaces Java fonctionne très bien mais est un peu différente. Dans mon code, j'avais une fonction qui renvoyait une liste de chaînes Python. Rétrospectivement, j'aurais probablement dû convertir chaque chaîne en chaîne Java, mais je n'y ai pas pensé et j'ai fait en sorte que la définition de la fonction d'interface renvoie une liste d'objets Java pour que cela fonctionne. Cela fonctionne bien dans pyjnius, ce qui fait des chaînes python une sous-classe d'objets Java. Cela ne fonctionne pas dans JPype, mais j'ai facilement corrigé cela en convertissant les chaînes avec la classe JString JPype.

Écrire @JOverride pour les méthodes implémentées est bien plus simple que du code comme @java_method('(Ljava/lang/String;[Ljava/lang/Object;)V') .

Une fois que j'ai réglé ces deux problèmes, tout a fonctionné très bien. J'ai du code passant des tableaux d'octets qui devront changer, mais JPype a un bon support pour cela. De plus, les conversions de type implicite de JPype sont super pratiques et pourraient remplacer beaucoup de décorateurs que je devais saupoudrer partout.

@Thrameos, je respecte votre détermination ici. Bonne chance pour que JPype fonctionne sur Android.

Merci pour les commentaires.

Je ne sais pas ce qui ne va pas dans le chemin de classe, mais si je devais deviner, ce serait d'où vous démarrez Python. Parfois, les gens démarrent la JVM à partir d'un module et parce que les règles pour les chemins Java et les chemins Python par rapport au répertoire de départ sont différentes, cela peut faire manquer des fichiers jar. (Il y a une section dans le guide de l'utilisateur concernant ce problème).

J'utilise la fonctionnalité classpath quotidiennement et cela fait partie de nos modèles de test. Donc, si les classpaths n'étaient pas respectés, cela entraînerait des échecs assez importants. Cela dit, vous ne seriez pas le premier à avoir des difficultés à trouver l'ensemble magique d'emplacements et de chemins absolus requis pour faire fonctionner un modèle complexe.

Voici un exemple où je démarre mon système de test à partir d'un répertoire Py relatif à la zone de développement.

import jpype
import os

devel = os.path.dirname(__file__)
devel = os.path.join(devel, '..', '..')
devel = os.path.abspath(devel)  # Notice that I converted the path to absolute so that it doesn't matter where the
# PWD of Java will be when this script is called.   Otherwise, if I import this from a different location it will use the
# original PWD and Java will find nothing in the classpath.

classpath = [
    '%s/gov.llnl.math/dist/*' % devel,
    '%s/gov.llnl.rdak/dist/*' % devel,
    '%s/gov.llnl.rnak/dist/*' % devel,
    '%s/gov.llnl.rtk/dist/*' % devel,
    '%s/gov.llnl.rtk.gadras/dist/*' % devel,
    '%s/gov.llnl.rtk.response/dist/*' % devel,
    '%s/gov.llnl.utility/dist/*' % devel,
    ]

jpype.startJVM(classpath=classpath, convertStrings=False)

Les chemins relatifs et absolus fonctionnent, mais cela dépendra beaucoup de votre point de départ. Le addClassPath a une magie spéciale pour s'assurer que tous les chemins sont par rapport à l'emplacement de l'appelant. Je suppose que la même logique est nécessaire dans les arguments du mot-clé classpath.

Je peux voir le problème avec le retour d'une liste de chaînes. Cela dépendra du type de retour quant au comportement qui est déclenché. Si la méthode était déclarée comme renvoyant String[] je m'attendrais à forcer chaque chaîne Python dans un Java lors du retour. Si la méthode était déclarée comme renvoyant List<String> il y aurait un problème car les génériques Java l'enlèveraient donc ce serait List<Object> . Selon la façon dont JPype voit la liste, il peut ou non essayer de convertir, mais cela doit être défini comme comportement, je vais donc le vérifier. La solution sûre est une compréhension de liste qui devrait être capable de convertir tous les éléments pour le retour.

Je ne sais pas ce qui ne va pas dans le chemin de classe, mais si je devais deviner, ce serait d'où vous démarrez Python. Parfois, les gens démarrent la JVM à partir d'un module

C'est ce que je faisais !

J'utilise la fonctionnalité classpath quotidiennement et cela fait partie de nos modèles de test. Donc, si les classpaths n'étaient pas respectés, cela entraînerait des échecs assez importants. Cela dit, vous ne seriez pas le premier à avoir des difficultés à trouver l'ensemble magique d'emplacements et de chemins absolus requis pour faire fonctionner un modèle complexe.

Oui, j'ai pensé que c'était quelque chose que vous remarqueriez tout de suite et que je n'étais pas la première personne à avoir ce problème. Merci pour l'exemple de code, c'est utile. J'ai donc apporté quelques améliorations à mon code.

La solution sûre est une compréhension de liste qui devrait être capable de convertir tous les éléments pour le retour.

C'est exactement ce que j'ai fait et cela fonctionne correctement maintenant. Merci!

Il y a quelques années, j'avais fait une comparaison entre py4j et jnius et j'avais trouvé que jnius était beaucoup plus rapide.

Chaque fois que j'ai vu jpype mentionné à certains endroits, j'ai toujours supposé qu'il faisait référence à - http://jpype.sourceforge.net/ et je suis resté à l'écart car il semblait non maintenu (je n'ai pas vu le nouveau projet bizarrement)

En lisant ce fil, j'ai réessayé mes benchmarks (mon test principal pour la vitesse est de lire et de noter les fichiers PMML) - et j'ai trouvé que jpype était assez performant.
Quelques résultats :
https://gist.github.com/AbdealiJK/1dd5b7677435ba22f9ab3e26016bb3e7

# jpype
# createjvm: 0.550s
# loadmodel: tot=1.466451 max=1.064521s avg=0.014665s
# fields   : tot=0.019881 max=0.009795s avg=0.000199s
# score    : tot=0.033356 max=0.023338s avg=0.000334s

# jnius
# createjvm: 0.249s
# loadmodel: tot=1.773011 max=1.385274s avg=0.017730s
# fields   : tot=0.039058 max=0.012234s avg=0.000391s
# score    : tot=0.067590 max=0.031904s avg=0.000676s

# py4j
# createjvm: 0.222s
# loadmodel: tot=0.616913 max=0.027464s avg=0.006169s
# fields   : tot=0.699152 max=0.026426s avg=0.006992s
# score    : tot=0.389583 max=0.017620s avg=0.003896s

Pour être juste, JPype avait son API frontale écrite en Python (par opposition à CPython) jusqu'en mars 2020. Il existe donc de nombreux repères plus anciens pour montrer qu'il était plutôt lent pour ce qu'il fournissait. Il y avait tellement de problèmes de backend qui devaient être résolus avec la gestion de la mémoire, en supprimant des wrappers multicouches entiers pour prendre en charge Ruby, le multithreading, etc., que la vitesse était la dernière chose sur laquelle travailler.

À ce stade, cela devrait être assez rapide. Mais si vous trouvez quelque chose qui nécessite un travail supplémentaire par rapport à d'autres packages, déposez simplement une note dans les problèmes. En raison des contrats de retour, il existe certaines limitations, mais les chemins principaux pour l'appel, les transferts de tableau et la mise en mémoire tampon sont assez travaillés à ce stade.

J'obtiens également de bons résultats avec JPype. En particulier, je tire une bonne valeur de l'intégration avec les tableaux numpy. J'ai pu ajouter de nouvelles fonctionnalités que je ne pouvais pas faire auparavant et qui m'ont permis d'améliorer les performances de manière significative.

Si quelqu'un peut avoir l'amabilité d'essayer de mettre à jour l'outil kivy-remote-shell pour le faire construire avec le Python3 et le buildozer actuels et d'améliorer les instructions pour être plus étape par étape, cela aiderait à l'effort de portage JPype très. J'ai terminé toutes les étapes requises jusqu'à la phase de démarrage et de test. Mais sans environnement pour terminer le processus d'amorçage, il sera difficile de progresser. Je peux réessayer de mettre à jour l'outil shell à distance ce week-end (et peut-être réussir) ou une partie intéressée ayant une meilleure connaissance de kivy peut effectuer cette tâche préalable, puis je peux passer le week-end à terminer le travail technique pour lequel je suis le plus qualifié. . Bien que j'offre librement mon temps pour aider les autres, il s'agit d'une ressource limitée et tout travail que je fais sur les efforts de portage Android est un retard sur le pont Python de Java qui intéresse également un certain nombre d'autres personnes.

J'espère que l'effort de portage Android pourra éviter de suivre le chemin de l'effort de portage PyPy où j'ai passé plusieurs semaines à retravailler le code de base pour pouvoir gérer les différences, mais j'ai ensuite rencontré un problème technique où une différence triviale dans le système objet a produit une erreur, et je n'ai pas pu trouver quelqu'un qui pourrait m'aider à tracer comment déboguer un rapport d'erreur généré dans le code généré. Bien que je ne pleure pas à propos du lait renversé et que tous ces efforts aient été déployés pour améliorer le code JPype de manière significative, à la fin de la journée, les utilisateurs qui voulaient utiliser JPype étaient restés au sec. Si cet effort échoue, tout n'est pas perdu car je vais y revenir, mais une fois que quelque chose est à la fin de la file d'attente, j'ai du mal à y revenir pendant 6 mois à moins que quelqu'un ne soit en mesure de prendre le temps de m'aider .

Mise à jour des progrès pour les parties intéressées.

J'ai réussi à faire démarrer JPype à partir de pythonforandroid et j'ai pu tester les fonctionnalités de base. Bien que certaines des fonctionnalités avancées puissent ne pas être possibles en raison de différences dans la JVM, je pense que la grande majorité de JPype sera disponible pour une utilisation sur la plate-forme Android. Le port a pris un certain temps car il nécessitait quelques mises à niveau des projets buildozer et pythonforandroid (donc beaucoup de lecture de la source et de demande d'aide). Un grand merci aux développeurs ici pour être réactifs afin que je puisse terminer la partie la plus difficile du processus en un week-end. Cela n'aurait pas été possible sans votre contribution. J'ai mis les changements associés dans les relations publiques, mais en regardant l'arriéré des relations publiques, il peut s'écouler un certain temps avant qu'il ne soit pris en considération. Maintenant que j'ai les spécifications techniques clés dont j'ai besoin, je devrais pouvoir l'intégrer et avoir un code de version fonctionnel quelque part autour de JPype ,1.2 nominalement sur le calendrier pour la fin de l'automne. Je peux le faire avancer s'il y a un grand intérêt des utilisateurs, mais il est en concurrence avec le Python de Java qui est une fonctionnalité importante pour d'autres projets.

Si quelqu'un souhaite aider à accélérer l'effort, la prochaine étape difficile consistera à déterminer comment construire une image docker avec tout en place avec un système partiellement construit afin que nous puissions exécuter une construction, charger un émulateur et exécuter le banc de test de l'androïde sur les pipelines azur (ou un autre système CI). Une fois que nous aurons un CI fonctionnel capable de détecter ce qui fonctionne et ce qui ne fonctionne pas, nous serons beaucoup plus près de pouvoir déployer en tant que logiciel stable. Je ne sais pas si cela devrait être hébergé dans le projet JPype ou si nous devrions avoir un projet de test Android séparé.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

tshirtman picture tshirtman  ·  23Commentaires

cmacdonald picture cmacdonald  ·  20Commentaires

stania picture stania  ·  6Commentaires

ignertic picture ignertic  ·  4Commentaires

etc0de picture etc0de  ·  5Commentaires