Repository navigation
Replies: 30 comments
|
@marscher @pelson I separate issue that I am tracking for future directions is the impending downgrading of JNI. As anyone should be aware JNI is being phased out in favor for panama, but panama is mostly geared to downcasting. I was on travel all week so I had a fair about of time waiting in airports or stuck in hotels, so I tried building a JNI replacement with panama. Unfortunately, if I make such a substitution it means we will end up stepping up the Java requirements all the way to Java 22. I am thinking the only way to do this properly would be to fork the project and use the fact that our project is named "jpype1" in pypi. We can make post JNI version called JPype2 and avoid the horrible nightmare that would come from having two backend models. The upside of this is as the new protocol is transaction based, we would be able to support shared memory or even remote JVM models rather than pure in process. Any thoughts on project directions? |
|
I put in a solid weekend on the Panama "Java Second Native Interface" project. It is shaping up great because it will actually speed up the array bulk transfers even more than we currently have, but until I get a full version running and benchmarked I don't know if it is viable. I will likely just push it to my personal github next weekend, then we can decide what to do. |
|
I do like the reverse bridge and the new Panama thing sounds interesting as well. If you're going towards the Panama direction, I also recommend a second branch if the models are so fundamentally different. |
|
The reverse bridge would be of interest to Ghidra. Right now users have to launch Ghidra a special way if they want it to be PyGhidra-enabled, which is undesireable. Sounds like with reverse bridge they could just launch Ghidra as a normal Java process like normal, and potentially have native CPython support available. |
|
Okay I will see about switching back to the reverse bridge work. I need to ship the 1.7.1 release. My Panama experiments are going okay so far in that I have a code base with about 90% working in the JNI side, but still too many rough edges to show. Ais have improved greatly on the test generation part so that reverse bridge will likely progress. My hang up there was exception handling (always a rough spot), testing, and then the big one how to pass a PyObject to Java. Currently we just block it entirely so we aren’t likely to break much, but if allow implicit conversion it will have broad implications. The safest will be require ***@***.***)” where PyObject is something one imports from “python.lang” which is the Java entity definitions.
|
|
I completed the first prototype of the J2NI support library late last night and submitted it for Review and Release. Based on the size of the package and its relatively low-risk profile as general-purpose shared software, I expect the review cycle may take on the order of 30 days. I will need to wait for that process to complete before posting it and beginning the next phase of JPype integration work. At this point, I would characterize the project as being in a late prototype phase.
At a high level, the library is built around a POD-oriented transport model, with references represented as int64_t values. It includes an ABI installer that dynamically links Java-provided symbols, and once initialized it provides the core mechanisms for class, field, method, and local/global object references. Traditional pushFrame and popFrame semantics are preserved.
One of the main architectural changes relative to JNI is that array access is no longer based on shared or pinned GC-managed memory. Instead, the design uses explicit view-based transfer operations. Because the transport is POD-oriented, the model works naturally in-process and could potentially support other transport modes in the future.
I also moved a substantial portion of the common runtime support needed for JPype into the base library. That should reduce the amount of JPype-specific bookkeeping required during bootstrap and steady-state operation, particularly around reference handling and other runtime utilities. Proxy and Cleaner support are also included in the core library, which should help keep the JPype-specific layer smaller and more focused.
From an architectural standpoint, the design reduces several categories of cross-boundary bookkeeping by using a shared context, or mailbox model, for status, exceptions, arguments, and results. The main open question is the cost of Java-side marshalling, since this approach shifts a significant amount of bridge behavior into Java. That remains the main area that still needs measurement.
Current test coverage is approximately 90% of branches, with 760 Java tests completed so far, and I expect a comparable amount of C++ testing once that side is exercised more fully. The Java test suite is currently passing on Java 25. The next step is to validate the C++ layer and begin benchmarking.
The main unresolved areas are monitors, launching, and thread management. At present, Panama does not appear to provide enough support in those areas to fully eliminate JNI dependencies for all runtime-critical functions, which remains an important limitation for this type of migration.
I also added structured marshalling and unmarshalling support for larger argument lists and bulk-style transfer patterns, which may be useful for higher-volume operations such as database-oriented unpacking and similar workloads.
I included both C and C++ bindings, and the design may be adaptable to other languages such as Rust.
|
|
I moved back to the reverse bridge. Getting it synced with the current presented little difficulty but JPype still needs a series of core upgrades before this concept can be reviewed or it is going to be another one of those too many things at once. In particular we have the ghost context pointer that is floating around everywhere to cleanup. I was not able to maintain it in the reverse bridge as it was leaking into downfalls everywhere. Second the proxy class is way too heavy currently. It should be doing all is work eagerly on the first proxy request so that the hot path is clean and lean. These two changes can be pulled out of the reverse bridge and applied separately as 1.8 before we get to the reverse wrapper. It should moderately speed stream api calls from Python. |
|
@marscher Do you have any preference how to handle these PR? My thinking is we should make a development branch ( I have the J2NI review in progress but they are being slow so it will likely be several months before we can show that prototype version. My hope with that is just getting it benchmarked and converted first. Because it is transaction based and supports bulk transfer it will be better for database support. The potential is there for improvements with JPype, but we have a lot of strange mechanics (as I was once again reminded) so actual mileage may vary. |
|
I finished the global removal with a week to spare. There key realization was that to get rid of globals required making only a few adjustments:
That then just left all the technical debt where we had failed to pass the frame through and places were the patterns need to shift to support subinterpreters. |
|
I haven't got particularly strong opinions on any, but my order of preference (most important first):
|
|
@pelson did the revised prototype for forward using My priority order right now is reverse bridge (usability) and j2ni (speed) as both if those are free with requiring cpython upstream changes. |
|
@marscher I have a 99% functional copy of the reverse bridge running. It is just chasing down those last downcalls in the unit test. If you can tell me how to arrange the PRs then I can start assembling. |
|
@Thrameos That's exciting news! I think we should prioritize a low integration effort first. When I remember correctly you said earlier, that j2ni needs a modern Java version, right? |
|
I was more looking for how to stage this into the repo. As one of those "Karl had to move many pieces" PR it has 20k lines of differences which is very punishing on you. I tried to stage it into 3 sets with core, global removal, than reverse bridge, but of course best laid plans as I found issues when assembling the pieces that required rework meaning it got all tangled up. (I had to flatten 4 times already) So we just released 1.7.x series and if we want to only do core changes in can pull out a clean globals/core fix from the reverse bridge. That would let us make a development branch for reverse. As a huge API change that we are going to support long term we will need some serious review. With the reverse bridge, most issues I have faced were in practice overloading issues. That is I put a face for Given the complexity i still recommend the original ordering. I should branch the repo to a development copy with a linter, flatten to core only PR1, global on core PR2, then reverse bridge PR3. That would make the review process somewhat tolerable for you and means we can decide to pull in PR2 as 1.8.x. But once I run the linter it will break all outstanding PR, hence we need to decide if anything else is salvagable and close the rest or they will all end up as orphans. J2NI is about 4 times more complex. Because to replaces JNI, it guts the whole common area with type manager, JPContext, and JPJavaFrame taking the brunt of the damage (>80%), but the type faces also don't leave unscathed (>20%) . The key change is without JNI binding to do the work, you instead must push C proxies into Java. It is actually a helpful change as rather than 20 random entry points you get a single point. I hit first light last week on J2NI stand alone internal testing, discovered that to support multiple connections I needed a rework, completely it, then started working on networking, but discovered I need to rework my rework to support versioned ABI. I don't think the JPype given it's type system will be able to run multiple remotes at a time, but the reverse direction with Java spinning many remote copies of Python (each running JPype) appears doable. But even best case I don't see a working JPype2 prototype appearing until December. I am also not quite sure what people will do with it. Over socket will run about 40% slower and lose some features, but it gain complete isolation. Java benefits much more than Python as you can spin up Python instance then close the connection and all the resources flush. Then spin another. So it's value is mostly depending on people actually using the reverse bridge which is as yet unproven. My side quest this weekend was adding in inline encryption so that the connection is not a free remote execution exploit for any MiTM. But I am sure any cryptographic expert would find by mutating crypto state horrifying as I built the most fragile encryption protocol ever where a single bit out of place causes the remote connection to unravel and go back to reauthirization/reconnect. |
|
Status report on this task. I have switched back to the J2Ni task for two weeks as that is a larger project and I will lose momentum if I don't keep it in active view. I have complete round test Java to Java feeding back to locally. But the rubber hits the road when I try to run over network because that stress tests if I made a bad pattern in the API. Unfortunately, I found multiple that need to be resolved as well as requirements for the encryption to avoid remote exploits. The initial software release is still stalled, so I need find a why to kick it. I made decent progress and will try to press for a java to java execution over remote in 3 weeks. Once that works I will have enough design parameters to finish the C++ side. That would finally be enough to plan out the changes need to jpype native side. Meanwhile I wrapped Llama.cpp in panama wrappers for direct orchestration without any middleman. |
|
@ryanmkurtz Regarding the J2NI, are you likely to be able to use it? Under J2NI we should be able to support.
The only configuration we can't support easily is mixed (remote and local) or Python launched multiple connections (unless somehow we can rearrange our API to have a "jvm" concept for limited access). Network conductivity will be Unix pipes or TCP sockets. The establishment connection will be TLS/SSL and the execution/data bus will be TCP with encrypted state machine. I will be working locally to get a security review obviously as that is a lot of surface area for remote exploits. |
|
@pelson Given you are currently using gRPC I think that J2NI will also fit your use case. Under J2NI Java or Python can talk over a shared multiplexer bus line with up to 64 threads. So not quite the same as a full local process, but should cover most use cases. |
|
Sorry what exactly is J2NI? |
|
I am replacing the foundation of JPype from JNI to Panama. That means creating a new JVM substrate protocol similar to JDWP and CapTP. It allows for in process message sharing and over wire using the same execution model. That allows the same JPype API to run both forward and reverse over many types of connections. Thus you can run in process (direct memory sharing) or remote (thread and memory mirroring) natively. Create an isolated JVM Create an isolated JVM with a fixed port Link to an existing JVM Possibly one day? Is that clear? Would it be useful? |
|
I think that is clear. So will there always be separate processes for the JVM and Python, as opposed to how the JVM currently resides in the Python process? Will there be a mode where Python can live within the JVM processes? |
|
Yes on both counts. You get to select local execution (shared buffers to numpy work) or remote execution (all features except shared buffers work). You may select only one option though as having both a local and remote means that the tables that support will get trampled.
If you are running locally you get
1) Python starts JVM (no Python subinterpreters, with buffer support, no isolation just like current JPype)
OR
2) JVM starts Python ( Python subinterpreters and buffer support, no isolation)
If you are running with remote then it may spawn a copy or use a specified copy. You get either
1) Python starts JVM (no Python subinterpreters, no buffer support, isolation)
OR
2) Java starts JVM (multiple remote Python interpreters, no Python subinterpreters, no buffer support, isolation)
Either way Java starts will support multiple interpreters, but there is a big difference. With remote interpreters you can use any module including those that do no support subinterpreters. With local you get a Main interpreter (any module) and then subinterpreters (only special modules with subinterpreter support.)
Python starts are more limited as they are mainly to run one JVM.
|
|
I think that would be useful indeed. I think Ghidra's first use of it would be to get its Java-driven Python interpreter running from the main Java process, rather than needing to start up the Ghidra JVM in a new Python process. It's not ideal for the user since they have to decide ahead of time if they want to use the python interpreter, so they can start Ghidra with the special launch scripts that support it. Python is not normally a runtime requirement of Ghidra. Out of curiousity, have you tried Ghidra/PyGhidra? |
|
@ryanmkurtz Sorry I haven't had time to test PyGhidra yet. Though it is on my list. |
|
I have first light on the J2NI C++ interface testing. The results show J2NI's design philosophy pays off: Bulk operations (the real use case):
Individual operations: The architectural wins (not measured in benchmarks):
J2NI vs JNI Performance Benchmark ┌───────────────────────────┬───────────┬──────────┬─────────┬──────────┐ |
|
That's exiting news and very impressive benchmark results! I'm looking forward to see this in action. |
|
I am guessing 80 to 160 hrs remain before I will have jpype2 prototype. |
|
Another long weekend on future projects. This time on my webfx now renamed due to conflicts. Should be hitting the net in next two weeks so I had to spend the time catching up on release tasks for the office. Hopefully it will be useful to others. |
|
I have almost gotten through the list of all the TODOs. Highlights:
Key issues going forward:
@marscher Again sorry for the massive stream of PRs. I have been working to review them before they get to you, but they are coming in fast because I set up an automatic system using agents to analyze and propose solutions. |
|
I completed the benchmarking between JPype, JPY, and JEP. (which matches my previous checks against Pyjnius from a few years back.) The basic conclusion for general method/field dispatch we are heavier. We have much richer objects and thus pay a far higher price. In some cases it is as little as 20%, often in the x2 range, while others like the int path where CPython lacks a "create int with meta class" are x5 worse. That is not unexpected as full features has a price. The int torture test we did against Graalvm is our absolutely worst possible path in the whole code. So if looping integers one at a time is you measure of success, sadly we lose hands down. Where the story is more interesting is bulk transfers which is what those other codes generally claim to be good at, JPype wins by substantial margins and often shockingly so. We only lose on small arrays where our method overhead is important. Once the array size grows large or the dimensions get deep, JPype is in a class of its own. We scale! Upload rates to Java up to 28 times faster. Download rates between 1.6 and 3 times faster. JPype remains the Scientific tool of choice for moving between numpy and Java. |
|
J2NI cleared review so I can progress with demonstration and benchmarking of it. Estimate time 4 weeks to completion. |
Uh oh!
There was an error while loading. Please reload this page.
I would like to check which enhancement is most valuable to the current user base now that I am back online.
Here are the options:
Technically I could pull all off in a year, but that would require assistance from a cpython team so I likely will be restricted to choose two. Webfx is the only one with internal funding.
All reactions