Pyjnius: PyJnius vs JPype

Dibuat pada 16 Jul 2020  ·  27Komentar  ·  Sumber: kivy/pyjnius

Saya adalah penulis utama JPype. Sebagai bagian dari pembaruan dokumentasi JPype, saya menambahkan PyJnius ke daftar kode alternatif JPype. Sayangnya setelah dua jam bermain-main dengan PyJnius, saya tidak dapat menemukan apa pun yang tampak seperti keunggulan PyJnius dibandingkan JPype. Setiap aspek yang saya lihat dari Proxies, penyesuai, penanganan array multidimensi, integrasi javadoc, penanganan GC, buffer, integrasi kode ilmiah (numpy, matplotlib, dll), metode sensitif pemanggil, dokumentasi, dan bahkan kecepatan eksekusi dalam banyak kasus saat ini tercakup di JPype lebih lengkap dari PyJnius. Namun, sebagai penulis JPype mungkin saya melihat aspek yang saya hargai daripada aspek tim PyJnius. Bisakah Anda mengartikulasikan keuntungan dari proyek ini dengan lebih baik? Apa proposisi nilai dari proyek ini, apa target audiensnya, dan apa yang ditargetkan untuk dilakukan yang belum tercakup oleh alternatif?

Komentar yang paling membantu

Saya menjangkau semua kode jembatan Java sekitar 2 tahun yang lalu. Sayangnya, kode PyJnius tampaknya terlewatkan karena tidak pernah muncul dalam pencarian saya. Saya akan melewatkannya di babak ini juga kecuali saya sedang mencari halaman yang melakukan siaran pers teknologi yang bagus terakhir kali dan menemukan sebuah blog yang membahas dua proyek. Tidak yakin bagaimana saya melewatkan proyek aktif lain di area yang sama selama dua tahun, tetapi itu jelas salah saya.

Sepertinya Anda membuat JPype bingung dengan Py4J yang merupakan kode jembatan utama lainnya. Mereka melakukan segalanya menggunakan soket dengan manfaat dan kerugian yang menyertainya. Saya juga menemukan bahwa proyek tidak memenuhi persyaratan saya.

Saya belum melakukan penelitian tentang apa yang diperlukan untuk mendukung android. Meskipun jika saya memiliki beberapa spesifikasi teknis dari itu seharusnya mungkin. Tidak ada yang kami lakukan di luar JNI asli dan panggilan lama biasa ke C API dari Python.

Sejauh pendekatannya, JPype menggunakan JNI secara eksklusif untuk menggabungkan JVM dengan Python menggunakan perintah "startJVM()". Padahal versi berikutnya (2.0) juga akan memberikan kemampuan untuk melakukan hal sebaliknya dimana Python dapat dimulai dari dalam Java. Ini dilakukan melalui pendekatan lapisan. Ada lapisan Python yang menampung semua kelas tingkat tinggi yang berfungsi sebagai ujung depan, modul pribadi CPython dengan kelas dasar yang memegang titik masuk, lapisan pendukung C++ yang menangani semua jenis konversi dan pencocokan serta bertindak sebagai modul asli untuk perpustakaan Java, dan perpustakaan Java yang semua tugas utilitasnya (menampung masa hidup objek, membuat kelas pendukung untuk irisan dan pengecualian, dan ekstraktor/rendering javadoc).

8 tahun yang lalu JPype sedikit berantakan. Itu mencoba untuk mendukung Ruby dan Python sehingga lapisan C++ adalah jalinan pembungkus untuk digunakan dan ujung depan semuanya dalam Python sehingga sangat lambat. Itu juga bercabang pada tipe pengembalian karena dukungan numpy dapat dikompilasi sehingga menghasilkan objek yang berbeda yang dikembalikan. Dibutuhkan banyak kelas adaptor seperti JException untuk bertindak sebagai proxy di mana objek Python dan Java asli berbeda. Tetapi semua masalah itu telah teratasi dalam 3 tahun sejak saya bergabung dengan proyek ini. Dua tujuan utama (bagi saya) di JPype adalah untuk menyediakan sintaks yang cukup sederhana bagi fisikawan yang akrab dengan pemrograman untuk dapat menggunakan Java dan memiliki tingkat integrasi yang tinggi dengan kode Python ilmiah. Kami melakukan ini dengan membuat objek-objek yang didukung oleh Java memiliki pembungkus objek CPython "semua embel-embel". Daripada mengonversi array primitif Java, kami membuat array primitif Java menjadi tipe Python asli baru yang mengimplementasikan semua titik masuk yang sama dengan numpy dan semua ini didukung oleh transfer buffer memori. Jadi kami memiliki konversi cepat dengan memanggil list(jarray) atau np.array(jarray) .

Untuk mengadaptasinya ke Android, urutan start up mungkin perlu dikerjakan ulang dan kode thunk yang digunakan untuk memuat pustaka internalnya perlu diganti dengan model JNI yang lebih tradisional. Saya sudah menghapus kode thunk di versi berikutnya sehingga nanti sudah terpenuhi. Hanya yang pertama akan diperlukan.

Perbedaan utama dalam pendekatan yang dapat saya lihat adalah bahwa PyJnius mengonversi array ke dan dari. Ini tampaknya sangat menghalangi pengkodean ilmiah di mana meneruskan array besar bolak-balik (seringkali tidak pernah dikonversi) adalah gaya JPype yang disukai. Keputusan untuk meminta konversi kemudian memaksa opsi seperti nilai pass by value dan pass by reference, tetapi itu berpotensi mengarah ke masalah yang lebih besar seolah-olah Anda memiliki panggilan multi argumen, Anda memilih satu kebijakan untuk semua argumen. Itu juga akan membuat berurusan dengan array multidimensi menjadi sulit. (Saya pikir kelas adaptor yang digunakan seperti obj.method(1, jnius.byref(list1), list2) akan memberikan kontrol yang lebih baik jika dapat dicapai). Selain itu, ada banyak masalah yang telah diselesaikan JPype seperti tautan GC, metode sensitif pemanggil, dan semacamnya. Jika tidak ada yang lain, silakan lihat kode JPype dan lihat apakah ada ide bagus yang dapat Anda gunakan.

Semua 27 komentar

Terima kasih telah menghubungi kami!

Saya mungkin salah ingat karena sudah bertahun-tahun saya tidak melihat jpype, tetapi saya meskipun menggunakan pendekatan yang berbeda, berbicara dengan JVM melalui server (jadi IPC) daripada memori bersama (tetapi readme Anda mengisyaratkan sebaliknya, dan saya tidak melihat petunjuk yang menyangkal hal itu dari pandangan sekilas pada kode, jadi saya mungkin sepenuhnya salah), dan pendekatan ini membuatnya tidak dapat dijalankan di Android, misalnya (yang merupakan alasan utama pengembangan pyjnius, meskipun beberapa orang menggunakannya pada platform desktop). Di sisi lain, saya tidak melihat banyak petunjuk tentang dukungan Android di basis kode JPype, kecuali itu tentang direktori native , karena jni.h-nya tampaknya diambil dari AOSP?

Tapi sejujurnya, kami tidak mengetahui JPype sama sekali ketika proyek dimulai, itu hanya langkah maju dari keharusan mengkodekan kode jni secara manual ke antarmuka ke kelas Android tertentu untuk dukungan kivy. Saya percaya @tito melakukan penggalian untuk membandingkan nanti, ketika kami mengetahuinya, tetapi saya tidak ingat apakah dia melihat alasan khusus untuk tidak mencoba beralih.

Hai. Saya ingat nama JPype, tetapi pada saat mencari apa yang bisa kami gunakan, saya tidak ingat mengapa itu tidak digunakan sama sekali, 8 tahun yang lalu :) Satu-satunya tujuan di awal adalah untuk dapat berkomunikasi dengan Android API, tetapi tidak menggunakan server RPC menengah seperti yang dilakukan proyek P4A saat itu.

Saya menjangkau semua kode jembatan Java sekitar 2 tahun yang lalu. Sayangnya, kode PyJnius tampaknya terlewatkan karena tidak pernah muncul dalam pencarian saya. Saya akan melewatkannya di babak ini juga kecuali saya sedang mencari halaman yang melakukan siaran pers teknologi yang bagus terakhir kali dan menemukan sebuah blog yang membahas dua proyek. Tidak yakin bagaimana saya melewatkan proyek aktif lain di area yang sama selama dua tahun, tetapi itu jelas salah saya.

Sepertinya Anda membuat JPype bingung dengan Py4J yang merupakan kode jembatan utama lainnya. Mereka melakukan segalanya menggunakan soket dengan manfaat dan kerugian yang menyertainya. Saya juga menemukan bahwa proyek tidak memenuhi persyaratan saya.

Saya belum melakukan penelitian tentang apa yang diperlukan untuk mendukung android. Meskipun jika saya memiliki beberapa spesifikasi teknis dari itu seharusnya mungkin. Tidak ada yang kami lakukan di luar JNI asli dan panggilan lama biasa ke C API dari Python.

Sejauh pendekatannya, JPype menggunakan JNI secara eksklusif untuk menggabungkan JVM dengan Python menggunakan perintah "startJVM()". Padahal versi berikutnya (2.0) juga akan memberikan kemampuan untuk melakukan hal sebaliknya dimana Python dapat dimulai dari dalam Java. Ini dilakukan melalui pendekatan lapisan. Ada lapisan Python yang menampung semua kelas tingkat tinggi yang berfungsi sebagai ujung depan, modul pribadi CPython dengan kelas dasar yang memegang titik masuk, lapisan pendukung C++ yang menangani semua jenis konversi dan pencocokan serta bertindak sebagai modul asli untuk perpustakaan Java, dan perpustakaan Java yang semua tugas utilitasnya (menampung masa hidup objek, membuat kelas pendukung untuk irisan dan pengecualian, dan ekstraktor/rendering javadoc).

8 tahun yang lalu JPype sedikit berantakan. Itu mencoba untuk mendukung Ruby dan Python sehingga lapisan C++ adalah jalinan pembungkus untuk digunakan dan ujung depan semuanya dalam Python sehingga sangat lambat. Itu juga bercabang pada tipe pengembalian karena dukungan numpy dapat dikompilasi sehingga menghasilkan objek yang berbeda yang dikembalikan. Dibutuhkan banyak kelas adaptor seperti JException untuk bertindak sebagai proxy di mana objek Python dan Java asli berbeda. Tetapi semua masalah itu telah teratasi dalam 3 tahun sejak saya bergabung dengan proyek ini. Dua tujuan utama (bagi saya) di JPype adalah untuk menyediakan sintaks yang cukup sederhana bagi fisikawan yang akrab dengan pemrograman untuk dapat menggunakan Java dan memiliki tingkat integrasi yang tinggi dengan kode Python ilmiah. Kami melakukan ini dengan membuat objek-objek yang didukung oleh Java memiliki pembungkus objek CPython "semua embel-embel". Daripada mengonversi array primitif Java, kami membuat array primitif Java menjadi tipe Python asli baru yang mengimplementasikan semua titik masuk yang sama dengan numpy dan semua ini didukung oleh transfer buffer memori. Jadi kami memiliki konversi cepat dengan memanggil list(jarray) atau np.array(jarray) .

Untuk mengadaptasinya ke Android, urutan start up mungkin perlu dikerjakan ulang dan kode thunk yang digunakan untuk memuat pustaka internalnya perlu diganti dengan model JNI yang lebih tradisional. Saya sudah menghapus kode thunk di versi berikutnya sehingga nanti sudah terpenuhi. Hanya yang pertama akan diperlukan.

Perbedaan utama dalam pendekatan yang dapat saya lihat adalah bahwa PyJnius mengonversi array ke dan dari. Ini tampaknya sangat menghalangi pengkodean ilmiah di mana meneruskan array besar bolak-balik (seringkali tidak pernah dikonversi) adalah gaya JPype yang disukai. Keputusan untuk meminta konversi kemudian memaksa opsi seperti nilai pass by value dan pass by reference, tetapi itu berpotensi mengarah ke masalah yang lebih besar seolah-olah Anda memiliki panggilan multi argumen, Anda memilih satu kebijakan untuk semua argumen. Itu juga akan membuat berurusan dengan array multidimensi menjadi sulit. (Saya pikir kelas adaptor yang digunakan seperti obj.method(1, jnius.byref(list1), list2) akan memberikan kontrol yang lebih baik jika dapat dicapai). Selain itu, ada banyak masalah yang telah diselesaikan JPype seperti tautan GC, metode sensitif pemanggil, dan semacamnya. Jika tidak ada yang lain, silakan lihat kode JPype dan lihat apakah ada ide bagus yang dapat Anda gunakan.

@Thrameos satu hal baru-baru ini yang ingin kami gabungkan adalah penggunaan lambdas Python untuk antarmuka Fungsional Java. Lihat https://github.com/kivy/pyjnius/pull/515 ; Saya tidak melihatnya di JPype?

JPype memiliki dukungan untuk lambda dari antarmuka Fungsional mulai dari 1.0.0. Itu adalah bagian dari 30 tarikan dalam 30 hari dorongan Maret ke 1,0.

JPype memiliki masa inkubasi yang panjang. Awalnya dimulai pada tahun 2004 berjalan hingga 2007. Kemudian mendapat dorongan besar ketika sekelompok pengguna menghidupkannya kembali untuk porting ke Python 3 sekitar tahun 2015. Kemudian pada tahun 2017, diambil untuk digunakan di laboratorium nasional yang membawanya dari 0,6. 3 hingga 0,7.2. Selama periode itu semua upaya difokuskan pada peningkatan dan pengerasan teknologi inti yang menyediakan antarmuka. Tapi itu akhirnya selesai pada bulan Maret setelah penulisan ulang inti kedua sehingga kami akhirnya bisa mendorong untuk 1.0.0. Sejak itu kami telah menambahkan semua yang "hilang" selama kampanye daftar keinginan "30 tarikan dalam 30 malam" saya. Tumpukan semua yang tidak dapat saya terapkan karena akan terlalu banyak pekerjaan akhirnya diselesaikan (masalah turun dari 50 menjadi 20, minta pengguna untuk menanyakan apa yang mereka butuhkan, dll). Jadi mungkin ada beberapa fitur yang tidak Anda temukan di versi sebelumnya yang sekarang tersedia. Saya telah mengubah sebagian besar permintaan fitur selama kurang dari seminggu sehingga yang tersisa hanyalah 3 besar (jembatan terbalik, memperpanjang kelas dengan Python, kemampuan untuk memulai JVM kedua).

Proyek ini akan kembali tertidur karena saya 2 bulan dalam upaya 6 bulan untuk menyelesaikan kode jembatan terbalik yang akan memungkinkan Java untuk memanggil Python dan menghasilkan rintisan untuk pustaka Python sehingga mereka dapat digunakan sebagai pustaka asli Java. Ini menggunakan ASM untuk membangun kelas Java dengan cepat sehingga dukungan asli untuk Python dapat dicapai. Masih belum sepenuhnya terintegrasi seperti Jython tetapi mungkin cukup dekat sehingga tidak akan ada banyak perbedaan.

Terima kasih banyak atas penjelasan rincinya, dan memang, jika ada, pasti ada ide yang bisa kita gunakan, dan ada baiknya mempelajari kodenya, melihat kode sebelumnya, apa yang saya lihat tampaknya cukup bersih baik dalam kualitas kode maupun struktur proyek, jadi selamat atas semua pekerjaan. Anda benar tentang mixup saya dengan Py4J, saya kira ketika saya melihat JPype, itu pasti dalam keadaan berantakan yang Anda gambarkan, dan menggunakannya pasti jauh lebih rumit daripada PyJNIus pada saat ini.

Poin Anda tentang meneruskan nilai/konversi sangat benar, dan memicu beberapa diskusi baru-baru ini di sini juga, karena @ hx2A melihat cara meningkatkan kinerja, dan menjadikan konversi kembali ke tipe python opsional (seperti meneruskan daftar sekali pakai ke Java, mendapatkannya dikonversi ke daftar java, dimodifikasi oleh java atau tidak, dan dikonversi kembali ke python, hanya untuk mengumpulkan sampah, tentu saja kurang optimal, sekarang kita setidaknya dapat menghindari bagian kedua, dengan biaya menggunakan argumen kata kunci, yang aman karena Java tidak mendukungnya, jadi tidak ada bentrokan tanda tangan, tetapi tentu saja sintaksnya sedikit lebih berisik).

Adapun perbedaan yang mungkin antara JPype dan PyJNIus, kita dapat mengimplementasikan antarmuka java menggunakan kelas python dan meneruskannya ke java untuk digunakan sebagai panggilan balik, di sisi lain, kita memang perlu menghasilkan bytecode java, jika kita ingin memperluas kelas java dari python, karena merupakan persyaratan untuk menggunakan beberapa api Android, yang tidak dapat kami bahas sekarang, saya tidak yakin apakah saya menyimpulkan dengan benar dari komentar Anda, tetapi mungkin Anda tidak memiliki kemampuan untuk membuat kelas Java memanggil Anda kode python seperti ini (menggunakan antarmuka).

JPype dapat mengimplementasikan antarmuka dengan Python. Cukup tambahkan dekorator ke kelas Python biasa.

from java.util.function import Consumer

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

Gunakan string di @JImplements jika kelas didefinisikan sebelum JVM dimulai, beberapa antarmuka dapat diimplementasikan sekaligus tetapi semua metode diperiksa untuk melihat apakah mereka diimplementasikan. Perbedaan utama adalah bahwa JPype menggunakan metode pengiriman (semua kelebihan beban pergi ke metode yang sama) daripada metode kelebihan beban. Ini karena kami ingin dapat memanggil metode dari Java dan Python. Saya dapat menambahkan kelebihan individu jika itu adalah fitur yang diinginkan, tetapi tidak ada yang memintanya.

(Sunting: Alasan kami tidak menggunakan warisan Python adalah bahwa pada saat ini ditambahkan kami masih mendukung Python 2 yang menyebabkan banyak masalah kelas meta, kami akan membersihkan lebih lanjut setelah ekstensi kelas ada. Jadi anotasi yang evaluasi sekali pada waktu deklarasi lebih bersih.)

Kami menggunakan sistem yang sama untuk mengimplementasikan penyesuai (dunder?) untuk kelas

@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.
        ...

Kami juga menggunakan anotasi untuk mendefinisikan konverter implisit seperti "Semua objek Python Path dikonversi ke 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))

Jelas menggunakan logika semacam ini sedikit lebih lambat daripada metode C khusus, tetapi itu membuat keterbacaan dan fleksibilitas tetap tinggi. Saya telah mendorong jalur kritis yang telah terbukti menjadi hambatan kembali ke C sesuai kebutuhan.

Kami juga menyediakan beberapa gula sintaks untuk membuat semuanya bersih MyJavaClass@obj => cast ke MyJavaClass (Java setara (MyJavaClass)obj ) atau cls=JInt[:] => buat tipe array ( cls=int[].class ) atau a=JDouble[10][5] => buat array multidim ( double[][] a = new double[10][5] ).

Saya telah mengerjakan prototipe untuk memperluas kelas dari JPype. Kami memiliki masalah yang sama seperti beberapa kelas Swing dan API lain yang memerlukan perluasan kelas. Solusi yang saya masak sejauh ini untuk membuat kelas yang diperluas dengan HashMap untuk masing-masing metode yang diganti. Jika tidak ada apa pun di hashmap untuk titik masuk itu, maka berikan ke super, jika tidak, ia akan memanggil penangan metode proxy. Tetapi saya memutuskan bahwa ini akan lebih mudah diterapkan setelah jembatan terbalik selesai sehingga Java benar-benar dapat menangani metode Python daripada melalui metode proxy Java. Jadi masih sekitar 6 bulan sebelum prototipe saya bisa berfungsi. Anda dapat melihat cabang epypj (nama saya untuk jembatan terbalik) untuk melihat bagaimana memanggil kembali dari Java ke Python bekerja serta pola untuk menghasilkan sebuah panggilan menggunakan ASM untuk membuat kelas Java dengan cepat.

Untuk mengelola pengumpulan sampah, ada beberapa bagian JPype yang melakukan pekerjaan itu. Pertama adalah JPypeReferenceQueue (native/Java/org/jpype/ref/JPypeReferenceQueue) yang mengikat kehidupan objek Python ke objek Java. Ini digunakan untuk membuat buffer dan hal-hal lain di mana Java perlu mengakses konsep Python untuk jangka waktu tertentu. Yang kedua adalah menggunakan referensi global sehingga Python dapat menampung objek Java dalam ruang lingkup. Ini memerlukan tautan pengumpul sampah (native/common/jp_gc.cpp) yang mendengarkan salah satu sistem untuk memicu GC dan melakukan ping ke yang lain ketika kondisi tertentu (ukuran kumpulan, pertumbuhan relatif). Proxy Terakhir perlu menggunakan referensi yang lemah karena mereka akan membentuk loop (karena proxy memegang referensi ke setengah Java dan setengah Java menunjuk kembali ke implementasi Python). Akhirnya saya bermaksud menggunakan agen untuk memungkinkan Python melintasi Java tetapi itu masih jalan.

Saya salah satu orang yang menggunakan pyjnius di desktop dan bukan di Android. Saya tidak tahu tentang JPype ketika saya mulai membangun proyek saya, tetapi saya melakukan penyelidikan untuk melihat apa perbedaannya.

Salah satu fitur unik di pyjnius adalah pemanggil dapat memutuskan apakah akan menyertakan metode dan bidang yang dilindungi dan pribadi atau tidak. Preferensi saya hanya untuk publik, tetapi saya memahami argumen bahwa membuat bidang dan metode non-publik tersedia berguna.

Kinerja sangat penting untuk proyek saya. Saya melakukan beberapa tes dengan kelas di bawah ini:

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;
  }
}

Di JPype:

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)

Dalam 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 sedikit lebih cepat, kecuali untuk metode kelebihan beban. Kita harus membandingkan catatan tentang cara memutuskan metode mana yang akan dipanggil saat metode kelebihan beban. Pyjnius memiliki mekanisme penilaian ini yang tampaknya menambah banyak overhead. JPype membuat keputusan itu lebih cepat.

Akhirnya, untuk tujuan 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)

Tentu saja, perbedaan beberapa itu sepele, tetapi itu akan bertambah jika seseorang membuat ribuan panggilan kecil dengan sangat cepat, yang harus saya lakukan.

Integrasi JPype dengan numpy cukup bagus dan mudah digunakan. Saya dapat melihat bagaimana peneliti dapat menggunakan ini untuk menulis skrip yang meneruskan array besar ke perpustakaan Java tanpa sintaks yang rumit. Saya juga perlu melewati array besar dan saya melakukannya dengan tobytes() dan kode Java khusus yang dapat menerima byte, tetapi jelas itu tidak rapi atau nyaman.

Sayangnya, kecepatan JPype adalah semacam prospek apa adanya. JPype sangat defensif mencoba menjaga dari hal-hal buruk yang berarti ada banyak overhead non-sepele. Itu berarti misalnya setiap kali panggilan Java dibuat, itu memastikan utas terhubung sehingga kami tidak segfault jika dipanggil dari utas luar seperti IDE. Grup pengguna lokal saya semuanya adalah ilmuwan sehingga titik masuk semuanya sangat dijaga dari beberapa kabel silang yang cukup mengerikan. Jika ada sesuatu yang salah pada mereka (tidak peduli betapa gilanya itu), maka saya telah gagal. (Yang akan menjelaskan 1500 tes termasuk dengan sengaja membangun objek yang buruk.)

Kedua, kecepatan tindakan individu kemungkinan besar sangat bervariasi dalam perbedaan yang sangat kecil dalam pekerjaan yang dilakukan. Contoh sepele yang Anda berikan adalah salah satu kasus terburuk. Tergantung pada jenis bidang apa yang sedang diakses sebenarnya dapat mengubah kecepatan akses dalam beberapa kasus.

Untuk contoh kecepatan Anda, Anda meminta bidang int.

  • Di PyJnius itu membuat deskriptor untuk pencarian objek, mengakses bidang, membuat panjang Python baru dan kemudian mengembalikannya.
  • Di JPype, ia membuat deskriptor untuk pencarian objek, mengakses bidang, membuat panjang Python baru, lalu membuat tipe pembungkus untuk int Java, menyalin memori panjang Python ke JInt (karena Python tidak memiliki cara untuk membuat turunan kelas integer secara langsung), kemudian mengikat slot dengan nilai Java, dan akhirnya mengembalikan JInt yang dihasilkan.

Jadi, bahkan sesuatu yang sepele seperti melakukan pembandingan kecepatan dalam mengakses suatu bidang, sebenarnya tidak terlalu sepele.
Perbedaannya adalah satu mengembalikan panjang Python dan yang lainnya mengembalikan integer Java yang sebenarnya (yang mengikuti aturan konversi Java) yang dapat diteruskan ke metode kelebihan beban lainnya dan mengikat dengan benar. Pekerjaan untuk mengembalikan tipe pembungkus lebih dari sekadar mengembalikan tipe Python, oleh karena itu perbedaan besar dalam kecepatan.

Saya mencoba mendemonstrasikan ini dengan menguji beberapa jenis bidang yang berbeda. Sayangnya, ketika saya menguji bidang Objek, jnius melakukan kesalahan pada kode "harness.objectField = harness". Saya tidak yakin mengapa bagian tertentu dari kabel silang menyebabkan masalah, tetapi gagal untuk saya. Saya belum terlalu tertarik pada kecepatan untuk JPype selain menghapus pelanggar kotor yang memberikan faktor kecepatan 3-5 untuk panggilan dan 300 kali untuk akses array tertentu. Tapi mungkin saya harus meninjau dan melihat area apa yang bisa diperbaiki. Saya ragu bahwa saya dapat menguranginya menjadi tulang telanjang seperti PyJnius tanpa menghapus keamanan atau menghapus kontrak pengembalian (yang tidak dapat saya lakukan). Paling-paling ada 10-30% percepatan masih mungkin,

Sedangkan untuk fitur akses private dan protected field tentu bisa dilakukan. Saya lebih suka pengguna menggunakan refleksi atau metode akses internal lainnya daripada mengekspos objek secara langsung. Jika saya harus memberikan sesuatu seperti itu, saya kemungkinan akan membuat bidang bernama _private yang akan berisi bidang yang tidak diekspos secara publik. JPype hanya menyediakan satu pembungkus kelas untuk setiap jenis, jadi saya tidak memiliki banyak kontrol butiran halus. Jadi Anda tidak dapat memilih untuk membuat kelas yang memiliki akses pribadi dan kemudian membuat objek kedua dari jenis yang sama dan tidak berakhir dengan pribadi yang terbuka. Saya menempuh jalan itu dengan konversi string, dan itu adalah bencana dengan beberapa perpustakaan memilih satu kebijakan dan yang lain yang berbeda yang mengakibatkan ketidakcocokan.

Saya menjalankan beberapa tes menggunakan daftar array.

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))

JPype

Daftar susunan paket 2.768904e-06
Ulangi daftar array 5.208071e-06
Akses (dapatkan) daftar array 4.037985e-06
Akses([]) daftar array 4.690264e-06

Jnius

Daftar susunan paket 3.322248e-06
Iterasi daftar array 4.099314e-06
Akses (dapatkan) daftar array 5.653444e-06
Access([]) daftar array 7.762727e-06

Ini bukan cerita yang sangat konsisten selain untuk mengatakan bahwa mereka mungkin sangat berbeda kecuali di mana mereka menyediakan fungsionalitas yang sangat berbeda. Jika semua yang Anda lakukan adalah mengakses metode, maka kemungkinan besar metode tersebut sangat mirip. Melewati array yang merupakan JPype pra-konversi 100 kali lebih cepat, mengonversi daftar dan tupel JPype 2 kali lebih lambat (kami tidak menggunakan akses vektor atau memiliki bypass khusus untuk tupel saat ini). Jadi intinya tergantung pada gaya pengkodean Anda, mungkin lebih cepat dengan satu atau yang lain. Tapi kemudian pengguna saya biasanya memilih JPype untuk kemudahan penggunaan dan konstruksi anti peluru daripada kecepatan. (Ah siapa aku bercanda! Mereka kemungkinan besar tersandung di internet karena JPype adalah tautan pertama yang mereka temukan di Google.)

Adapun bagaimana JPype melakukan metode yang mengikat detail itu harus ada di panduan pengembang/pengguna. Pengikatan metode dimulai dengan mengurutkan daftar metode untuk pengiriman sesuai dengan aturan yang diberikan dalam spesifikasi Java sehingga jika ada sesuatu yang menyembunyikan sesuatu yang lain, itu akan muncul pertama kali dalam daftar (kode dapat ditemukan di native/Java/org/jpype seperti yang kita gunakan kelas utilitas Java untuk melakukan penyortiran saat pengiriman dibuat pertama kali). Selain itu setiap metode diberikan daftar preferensi metode mana yang menyembunyikan metode lain. Penyelesaian dimulai dengan terlebih dahulu memeriksa setiap argumen untuk melihat apakah ada "java slot" untuk argumen tersebut. Slot Java menunjuk ke objek yang sudah ada yang tidak memerlukan konversi sehingga menyingkirkannya sebelum mencocokkan berarti kita dapat menggunakan aturan langsung daripada aturan implisit. Kemudian mencocokkan argumen berdasarkan tipe menjadi 4 level (tepat, implisit, eksplisit, tidak ada). Ini jalan pintas pada eksplisit dan tidak ada untuk melompat ke metode berikutnya. Jika pernah mendapat yang tepat maka itu memotong seluruh proses untuk melompat ke panggilan. Jika ada kecocokan, itu akan menyembunyikan metode yang memiliki ikatan yang kurang spesifik. Jika dua kecocokan implisit ditemukan yang tidak disembunyikan, ia melanjutkan ke TypeError. Setelah semua kecocokan habis, rutinitas konversi dijalankan. Itu kemudian membebaskan kunci global Python dan membuat panggilan dan memperoleh kembali kunci global. Jenis pengembalian dicari dan pembungkus Python baru dibuat berdasarkan jenis yang dikembalikan (menggunakan pengembalian kovarian sehingga jenis yang dikembalikan adalah yang paling diturunkan daripada jenis dari metode). Ini sebagian besar linier dengan jumlah kelebihan meskipun ada beberapa kompleksitas untuk argumen variadik, tetapi tabel yang dibuat sebelumnya berarti akan mencoba foo(panjang, panjang) sebelum mencoba foo(ganda, ganda) dan hit (panjang , long) akan mencegah double, double dari setiap kecocokan karena aturan resolusi metode Java. Masih ada beberapa percepatan yang dapat saya terapkan tetapi itu akan membutuhkan tabel caching tambahan.

Saya mewarisi sistem pemesanan dengan jalan pintas ketika saya memulai proyek pada tahun 2017. Saya menambahkan metode menyembunyikan cache dan slot java untuk mendorong keluar sebagian besar overhead kami.

Saya mengoptimalkan jalur eksekusi untuk metode. Angka yang direvisi untuk JPype adalah

Daftar susunan paket 2.226081e-06
Iterasi daftar array 4.082152e-06
Akses (dapatkan) daftar array 2.962606e-06
Akses([]) daftar array 3.644642e-06

Grup pengguna lokal saya semuanya adalah ilmuwan sehingga titik masuk semuanya sangat dijaga dari beberapa kabel silang yang cukup mengerikan. Jika ada sesuatu yang salah pada mereka (tidak peduli betapa gilanya itu), maka saya telah gagal.

Ya, segfault itu mengerikan dan saya mendapatkannya ratusan ketika saya mulai menggunakan pyjnius. Saya belum mendapatkannya dalam waktu yang lama karena mungkin saya mengatasi masalah keamanan dan memasukkannya ke dalam kode saya. Sekarang semuanya bekerja dengan andal. Saya mengerti kasus penggunaan Anda. Jika pengguna Anda adalah ilmuwan yang bekerja dengan objek Java secara langsung untuk melakukan analisis data dengan berbagai pustaka Java, segfault akan menyebabkan mereka kehilangan semua pekerjaannya. JPype tampaknya dirancang lebih baik untuk melakukan pekerjaan ilmiah di mana pengguna akhir bekerja secara langsung dengan objek Java melalui Python. Kasus penggunaan utama untuk pyjnius berbeda, yaitu berkomunikasi dengan Android. Dalam hal ini, masalah keamanan adalah masalah pengembang, jadi mungkin membuat pilihan yang berbeda tentang keselamatan vs kecepatan adalah tepat.

Saya akui saya bukan penggemar berat "aman selama Anda menginjak kotak ini dalam urutan ini". Ketika saya mulai mengerjakan JPype, saya membutuhkan waktu hampir satu tahun untuk membuktikan semua titik masuk dengan cukup sehingga saya dapat meneruskan kode ke grup lokal saya. Dan saya telah menambahkan dua tahun tambahan armor API sejak itu. Selain beberapa orang langka yang JVMnya gagal dimuat (yang sangat sulit diselesaikan) ada beberapa masalah yang tersisa karena JPype telah dibawa ke standar kode produksi.

Adapun sebagai trade off dengan kecepatan dan keamanan, kecepatan itu bagus tetapi jika Anda mendapatkan kecepatan dengan berhemat pada operasi yang aman, biasanya itu adalah perdagangan yang buruk bagi sebagian besar pengguna. Baik Anda membalik kode prototyping atau menulis sistem produksi, harus berhenti bekerja dan mencoba mengatasi segfault adalah gangguan yang seharusnya tidak dihadapi pengguna.

Jika seseorang bersedia memberi saya beberapa contoh cara menguji JPype pada emulator Android, saya dapat melihat tentang membuat modifikasi yang diperlukan.

Untuk digunakan di android, kami mengemas pyjnius sebagai seni distribusi python yang dibangun oleh python-untuk-android, (sering menggunakan buildozer sebagai antarmuka yang lebih mudah, tetapi sama saja), dan kemudian membangun aplikasi python yang mengirimkan distribusi ini, maka kode python Anda dapat mengimpor pyjnius atau pustaka python lainnya yang dibangun dalam distribusi python saat pengguna menjalankan aplikasi.

Dengan demikian, langkah pertama adalah membuat jpype dikompilasi dalam distribusi, yang dilakukan oleh p4a (https://github.com/kivy/python-for-android/) ketika Anda memberi tahu itu bagian dari persyaratan Anda, biasanya ( tetapi tidak selalu) "resep" diperlukan untuk menjelaskan kepada p4a cara membangun perpustakaan yang bukan python murni, yang untuk pyjnius dapat ditemukan di sini https://github.com/kivy/python-for-Android/blob/ develop/pythonforandroid/recipes/pyjnius/__init__.py sebagai contoh . Jika Anda menggunakan buildozer, Anda dapat menggunakan pengaturan p4a.local_recipes di buildozer.spec untuk mendeklarasikan direktori tempat resep untuk persyaratan dapat ditemukan, jadi Anda tidak perlu fork python-for-android ke memiliki resep Anda digunakan.

Saya akan menyarankan menggunakan buildozer, karena mengotomatiskan lebih banyak hal https://buildozer.readthedocs.io/en/latest/installation.html#targeting -android dan Anda masih dapat mengatur resep lokal Anda untuk mencoba berbagai hal. Pembangunan pertama akan memakan waktu, karena perlu membangun python dan sejumlah dependensi untuk lengan, dan perlu mengunduh Android ndk dan SDK untuk itu. Anda mungkin dapat menggunakan bootstrap kivy default untuk aplikasi, dan membuat aplikasi seperti "hello world", yang akan mengimpor jpype dan hanya menampilkan hasil beberapa kode dalam label, atau bahkan di logcat menggunakan print, saya tidak ingat seberapa baik kivy berjalan di emulator Android, saya tidak pernah menggunakannya, tetapi saya pikir beberapa pengguna melakukannya, dan dengan pengaturan akselerasi itu akan berfungsi, afaik, jika tidak, Anda dapat menggunakan pembungkus sdl2, atau tampilan web, dan menggunakan labu atau server botol untuk menampilkan sesuatu, pertama-tama saya akan mencoba dengan yang kivy karena sejauh ini yang paling teruji.

Anda memerlukan mesin linux atau osx (VM baik-baik saja, dan WSL di windows 10 baik-baik saja), untuk membangun untuk Android.

Jika Anda mulai mengerjakan resep jpype untuk python-untuk-android, akan dipersilakan untuk membuka PR yang sedang berlangsung tentangnya untuk diskusi apa pun yang mungkin muncul. Akan sangat bagus jika itu akan berhasil, terutama jika itu memang dapat menyelesaikan beberapa batasan pyjnius yang sudah lama ada. Seperti yang dibahas sebelumnya di utas, pyjnius pada dasarnya mencakup persyaratan inti kami untuk penggunaan kivy, tetapi tidak memiliki keuletan pengembangan yang cukup untuk melampaui ini secara signifikan.

@inclement Saya menyiapkan PR untuk port Android di jpype-project/jpype#799. Sayangnya, saya tidak begitu yakin ke mana harus pergi dari sini. Tampaknya mencoba menjalankan gradle yang sebenarnya bukan jalur build yang benar.

Tindakan yang perlu dilakukan adalah sebagai berikut:

  • [x] Sertakan semua file jpype/*.py dalam build (atau versi yang telah dikompilasi sebelumnya).
  • [x] Jalankan Apache ant pada native/build.xml dan letakkan file jar yang dihasilkan di suatu tempat yang dapat diakses.
  • [x] Sertakan file jar (atau yang setara) dalam file build.
  • [x] Kompilasi kode C++ dari native/common dan native/python ke dalam modul bernama _jpype untuk disertakan dalam build.
  • [x] Sertakan file main.py yang baru saja meluncurkan shell interaktif sehingga kami dapat mengujinya secara manual untuk saat ini.
  • [ ] Di masa depan saya perlu memasukkan "ASM" atau sesuatu yang berfungsi seperti itu untuk Android sehingga saya dapat memuat kelas yang dibuat secara dinamis.
  • [x] Patch kode C++ sehingga menggunakan bootstrap khusus untuk memuat file jar JVM dan pengiring dan menghubungkan semua metode asli.
  • [ ] Patch jvmfinder dengan sesuatu yang berfungsi di android dan "startJVM" dipanggil secara otomatis daripada di awal main.
  • [ ] Patch org.jpype agar sistem navigasi jar (yang merupakan cara kerja impor) dapat berfungsi di android.

Saya melihat beberapa dokumen tetapi tidak ada yang benar-benar menonjol tentang bagaimana mencapai ini. Tata letak proyek saya agak berbeda dari biasanya karena kami tidak meletakkan semuanya di bawah modul utama (karena kami sebenarnya sedang membangun 3 modul yang membentuk sistem. jpype, _jpype, dan org.jpype.) Saya mungkin memerlukan resep khusus untuk lakukan semua tindakan ini serta nonaktifkan pola yang tidak diinginkan seperti menjalankan gradle (kecuali melakukan sesuatu yang berguna yang tidak dapat saya ketahui.)

Tampaknya mencoba menjalankan gradle yang sebenarnya bukan jalur build yang benar.

Gradle adalah alat build yang digunakan sebagai langkah terakhir untuk mengemas APK, mungkin tidak terkait dengan penyertaan jpype Anda.

Sertakan semua file jpype/*.py dalam build (atau versi yang telah dikompilasi sebelumnya).

Secara umum, jika jpype pada dasarnya berfungsi sebagai modul Python normal maka upaya resep pertama Anda di sana mungkin melakukan sebagian besar pekerjaan berat - CppCompiledComponentsPythonRecipe melakukan sesuatu seperti menjalankan python setup.py build_ext dan python setup.py install menggunakan lingkungan NDK. Ini harus menginstal paket jpype python di dalam lingkungan python yang sedang dibangun untuk dimasukkan ke dalam aplikasi.

Sertakan file jar (atau yang setara) di file build.

Ini mungkin merupakan langkah ekstra yang perlu dilakukan resep, itu akan turun untuk menyalin file jar Anda (atau apa pun yang Anda butuhkan) ke beberapa tempat yang sesuai dalam proyek Android yang sedang dibangun python-for-Android.

Kompilasi kode C++ dari native/common dan native/python ke dalam modul bernama _jpype untuk disertakan dalam build.

Jika ini ditangani oleh setup.py, ini seharusnya sudah berfungsi, tetapi mungkin perlu beberapa penyesuaian. Jika tidak, Anda dapat menyertakan perintah kompilasi Anda dalam resep (dan membuatnya dibangun untuk lingkungan Android dengan menyetel env vars yang sesuai, seperti yang akan Anda lihat selesai menggunakan self.get_env dalam resep lain).

Patch kode C++ sehingga menggunakan bootstrap khusus untuk memuat file jar JVM dan pengiring dan menghubungkan semua metode asli.

Saya harap bagian ini cukup sederhana, hanya tergantung pada penggunaan fungsi antarmuka JNI android yang tepat. Kami melakukan ini dengan cara yang sedikit meretas dengan menambal pyjnius daripada melakukan kompilasi bersyarat yang sesuai, karena pustaka pembantu yang berbeda menyediakan pembungkus yang berbeda, seperti yang ditunjukkan oleh misalnya patch ini . Kompleksitas ini tidak perlu memengaruhi Anda, Anda cukup memanggil fungsi api Android yang tepat.

Patch jvmfinder dengan sesuatu yang berfungsi di Android dan "startJVM" dipanggil secara otomatis daripada di awal main.

Saya tidak cukup akrab dengan JVM untuk benar-benar tahu, tetapi saya pikir Android ingin Anda selalu mengakses jvm yang ada dan Anda tidak dapat memulai instance baru. Apakah itu penting (atau hanya salah?)?

Jalankan Apache ant pada native/build.xml dan letakkan file jar yang dihasilkan di suatu tempat yang dapat diakses.

Saya juga tidak yakin tentang yang ini karena tidak terbiasa, apakah ini hanya langkah membangun jpype internal? Saya tidak yakin bagaimana versi Java berinteraksi, tetapi saya kira menggunakan semut sistem-asli seharusnya baik-baik saja di sini.

Itu memberi peringatan bahwa buildozer tidak menghormati setup.py sehingga dilewati langsung dari atas ke langkah bertahap. Oleh karena itu perlu menambahkan langkah-langkah tengah tersebut secara manual. Setup.py kami melakukan banyak langkah kompilasi khusus seperti membuat file kait untuk menggabungkan file jar dengan dll yang kemungkinan tidak berlaku di sini jadi tidak masalah jika dilewati, tetapi juga gagal menemukan file cpp dan Java yang didefinisikan dalam setup.py. Sebagian dari masalahnya adalah setup.py saya begitu besar sehingga saya harus membaginya menjadi modul setupext.

Saya kira pertama-tama saya perlu mencari tahu bagaimana saya bisa menjalankan perintah bersih sehingga saya bisa menjalankan proses sekali dan menunjukkan log. (Saya menjalankannya sepotong makanan pertama kali saat saya bermain. jadi saya tidak memiliki log yang bersih.) Kita juga harus memindahkan percakapan ini ke PR sehingga kita bisa lebih banyak membahas topik untuk utas.

FWIW Saya dapat mem-port inti proyek non-Android saya ke JPype. Ada dua kesulitan atau perbedaan kecil:

  1. classpath kwarg di jpype.startJVM() tampaknya diabaikan apa pun yang saya lakukan. Fungsi addClassPath() berhasil. Pengembang yang peduli dengan urutan classpath mungkin perlu melakukan beberapa penyesuaian dibandingkan dengan cara mereka menggunakan jnius_config.add_classpath() .

  2. Menerapkan Antarmuka Java berfungsi dengan baik tetapi sedikit berbeda. Dalam kode saya, saya memiliki fungsi yang mengembalikan daftar string Python. Dalam retrospeksi saya mungkin harus mengonversi setiap string menjadi string Java, tetapi saya tidak memikirkannya dan membuat definisi fungsi antarmuka mengembalikan daftar objek Java agar ini berfungsi. Itu berfungsi dengan baik di pyjnius, yang secara efektif menjadikan string python sebagai subkelas objek Java. Itu tidak berfungsi di JPype, tetapi saya dengan mudah memperbaikinya dengan mengonversi string dengan kelas JString JPype.

Menulis @JOverride untuk metode yang diimplementasikan jauh lebih sederhana daripada kode seperti @java_method('(Ljava/lang/String;[Ljava/lang/Object;)V') .

Setelah saya menangani dua masalah itu, semuanya pada dasarnya bekerja dengan baik. Saya memiliki beberapa kode yang melewati array byte yang harus diubah tetapi JPype memiliki dukungan yang baik untuk itu. Juga, konversi tipe implisit JPype sangat nyaman dan dapat menggantikan banyak dekorator yang harus saya taburkan di mana-mana.

@Thrameos Saya menghormati tekad Anda di sini. Semoga berhasil membuat JPype bekerja di Android.

Terima kasih atas komentarnya.

Tidak yakin apa yang salah di classpath meskipun jika saya harus menebak dari mana Anda memulai Python. Terkadang orang memulai JVM dari modul dan karena aturan untuk jalur Java dan jalur Python sehubungan dengan direktori awal berbeda, hal itu dapat menyebabkan guci terlewatkan. (Ada bagian di panduan pengguna mengenai masalah ini).

Saya menggunakan fitur classpath setiap hari dan ini adalah bagian dari pola pengujian kami. Jadi, jika classpath tidak dihormati, itu akan menyebabkan beberapa kegagalan yang cukup besar. Yang mengatakan Anda tidak akan menjadi orang pertama yang mengalami kesulitan dalam menemukan lokasi ajaib dan jalur absolut yang diperlukan untuk membuat pola kompleks bekerja.

Berikut adalah contoh di mana saya memulai sistem pengujian saya dari direktori Py relatif terhadap area pengembangan.

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)

Jalur relatif dan absolut memang berhasil, tetapi ini akan sangat bergantung pada dari mana Anda memulai. AddClassPath memiliki keajaiban khusus untuk memastikan bahwa semua jalur sesuai dengan lokasi pemanggil. Saya kira logika yang sama diperlukan dalam argumen kata kunci classpath.

Saya dapat melihat masalah dengan mengembalikan daftar string. Itu akan tergantung pada jenis pengembalian untuk perilaku yang dipicu. Jika metode ini dideklarasikan sebagai mengembalikan String[] saya berharap untuk memaksa setiap string Python menjadi Java saat kembali. Jika metode dideklarasikan sebagai mengembalikan List<String> akan ada masalah karena Java generik menghapusnya sehingga menjadi List<Object> . Bergantung pada bagaimana JPype melihat daftar, mungkin atau mungkin tidak mencoba mengonversi tetapi itu harus didefinisikan perilaku jadi saya akan memeriksanya. Solusi aman adalah pemahaman daftar yang harus dapat mengonversi semua item untuk pengembalian.

Tidak yakin apa yang salah di classpath meskipun jika saya harus menebak dari mana Anda memulai Python. Terkadang orang memulai JVM dari modul

Itulah yang saya lakukan!

Saya menggunakan fitur classpath setiap hari dan ini adalah bagian dari pola pengujian kami. Jadi, jika classpath tidak dihormati, itu akan menyebabkan beberapa kegagalan yang cukup besar. Yang mengatakan Anda tidak akan menjadi orang pertama yang mengalami kesulitan dalam menemukan lokasi ajaib dan jalur absolut yang diperlukan untuk membuat pola kompleks bekerja.

Ya, saya pikir ini adalah sesuatu yang akan Anda perhatikan segera dan bahwa saya bukan orang pertama yang memiliki masalah ini. Terima kasih atas contoh kodenya, sangat membantu. Saya membuat beberapa perbaikan pada kode saya sebagai hasilnya.

Solusi aman adalah pemahaman daftar yang harus dapat mengonversi semua item untuk pengembalian.

Itulah yang saya lakukan dan itu berfungsi dengan benar sekarang. Terima kasih!

Beberapa tahun yang lalu saya telah melakukan perbandingan antara py4j dan jnius dan menemukan jnius jauh lebih cepat.

Ketika saya melihat jpype disebutkan di beberapa tempat, saya selalu berasumsi itu merujuk ke - http://jpype.sourceforge.net/ dan menjauh darinya karena sepertinya tidak terawat (Tidak melihat proyek baru dengan aneh)

Saat membaca utas ini, saya mencoba tolok ukur saya lagi (testcase utama saya untuk kecepatan adalah membaca dan menilai file PMML) - dan menemukan jpype cukup berkinerja.
Beberapa hasil:
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

Agar adil, JPype memiliki API ujung depan yang ditulis dengan Python (sebagai lawan dari CPython) hingga Maret 2020. Jadi ada banyak tolok ukur lama yang menunjukkan bahwa itu agak lambat untuk apa yang disediakannya. Ada begitu banyak masalah backend yang harus diselesaikan dengan manajemen memori, menghapus seluruh pembungkus multilayer untuk mendukung Ruby, multithreading, dan sejenisnya, sehingga kecepatan adalah hal terakhir yang harus dikerjakan.

Pada titik ini seharusnya cukup cepat. Tetapi jika Anda menemukan sesuatu yang membutuhkan pekerjaan tambahan relatif terhadap paket lain, cukup berikan catatan tentang masalah. Karena kontrak pengembalian ada beberapa batasan, tetapi jalur utama untuk panggilan, transfer array, dan buffering memori cukup berhasil pada saat ini.

Saya juga mendapatkan hasil yang baik dengan JPype. Secara khusus, saya mendapatkan nilai bagus dari integrasi dengan array numpy. Saya dapat menambahkan fitur baru yang sebelumnya tidak dapat saya lakukan yang memungkinkan saya meningkatkan kinerja dengan cara yang berarti.

Jika ada yang bisa berbaik hati untuk mencoba memperbarui alat kivy-remote-Shell dengan untuk membangunnya dengan Python3 dan buildozer saat ini dan meningkatkan instruksi menjadi lebih langkah demi langkah, itu akan membantu dengan upaya porting JPype sangat. Saya telah menyelesaikan semua langkah yang diperlukan hingga tahap boot dan pengujian. Tetapi tanpa lingkungan untuk menyelesaikan proses bootstrap, akan sulit untuk membuat kemajuan. Saya dapat mencoba lagi untuk memperbarui alat shell jarak jauh akhir pekan ini (dan mungkin berhasil) atau pihak yang berkepentingan dengan pengetahuan yang lebih baik tentang kivy dapat menyelesaikan tugas prasyarat ini dan kemudian saya dapat menghabiskan akhir pekan menyelesaikan pekerjaan teknis yang paling memenuhi syarat untuk saya selesaikan . Meskipun saya dengan bebas menawarkan waktu saya untuk membantu orang lain, ini adalah sumber daya yang terbatas dan pekerjaan apa pun yang saya lakukan pada upaya porting Android adalah penundaan pada jembatan Python dari Java yang juga diminati oleh sejumlah orang lain.

Saya harap upaya porting Android dapat menghindari cara upaya porting PyPy di ​​mana saya menghabiskan beberapa minggu mengerjakan ulang kode inti untuk dapat menangani perbedaan tetapi kemudian mengalami masalah teknis di mana perbedaan sepele dalam sistem objek menghasilkan kesalahan, dan saya tidak dapat menemukan siapa pun yang dapat membantu saya melacak cara men-debug laporan kesalahan yang dihasilkan dalam kode yang dihasilkan. Meskipun saya tidak menangis tentang susu yang tumpah dan semua upaya itu dilakukan untuk meningkatkan kode JPype ke cara lain yang bermakna, pada akhirnya para pengguna yang ingin menggunakan JPype dibiarkan tinggi dan kering. Jika upaya ini gagal tidak semuanya hilang karena saya akan kembali ke sana, tetapi begitu ada sesuatu di ujung antrian, saya sulit sekali untuk kembali ke sana selama 6 bulan kecuali seseorang dapat meluangkan waktu untuk membantu saya .

Pembaruan kemajuan untuk pihak yang berkepentingan.

Saya berhasil membuat JPype untuk boot dari dalam pythonforandroid dan dapat menguji fungsionalitas dasarnya. Meskipun beberapa fitur lanjutan mungkin tidak dapat dilakukan karena perbedaan dalam JVM, saya percaya bahwa sebagian besar JPype akan tersedia untuk digunakan pada platform Android. Port memakan waktu cukup lama karena memerlukan beberapa peningkatan ke proyek buildozer dan pythonforandroid (jadi banyak membaca sumber dan meminta bantuan). Banyak terima kasih kepada para pengembang di sini karena responsif sehingga saya dapat menyelesaikan bagian tersulit dari proses dalam satu akhir pekan. Itu tidak akan mungkin terjadi tanpa masukan Anda. Saya telah memasukkan perubahan terkait sebagai PR tetapi melihat tumpukan PR, mungkin perlu beberapa saat sebelum muncul untuk dipertimbangkan. Sekarang saya memiliki spesifikasi teknis utama yang saya butuhkan, saya harus dapat mengintegrasikannya dan memiliki kode rilis yang berfungsi di suatu tempat di sekitar JPype ,1.2 secara nominal di kalender untuk akhir musim gugur. Saya dapat mendorongnya ke depan jika ada minat pengguna yang besar tetapi bersaing dengan Python dari Java yang merupakan fitur penting untuk proyek lain.

Jika seseorang ingin membantu mempercepat upaya, langkah sulit berikutnya adalah mencari tahu cara membuat gambar buruh pelabuhan dengan semua yang ada dengan sistem pembangunan sebagian sehingga kita dapat menjalankan pembangunan, memuat emulator, dan menjalankan bangku pengujian dari android di saluran pipa Azure (atau sistem CI lainnya). Setelah kami memiliki CI yang berfungsi yang dapat mendeteksi apa yang berhasil dan apa yang tidak, kami akan lebih dekat untuk dapat digunakan sebagai perangkat lunak yang stabil. Tidak yakin apakah ini harus ditempatkan di proyek JPype atau jika kita harus memiliki proyek pengujian Android yang terpisah.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat