The Overhead of JNI

Almost six years ago I wrote chess4j + Prophet4 POC , where I laid out an idea for a proof-of-concept to marry up my Java engine chess4j and my C engine Prophet. The idea was to use native C code, which typically executes much faster than Java code, for the core engine, but to use the higher level language for all the ancillary features. Of course, if using a higher level language was the only goal, C++ would have been the obvious choice. This is a hobby though, and one of the goals for chess4j was to explore Java based technologies. So, I wrote a Java Native Interface (JNI) layer as the bridge between the two codebases.

The learning curve for JNI is fairly steep, and it’s cumbersome to work with, but in the end it came together and works well. As predicted it gave chess4j a nice speed boost, and I haven’t had to write some of the features in chess4j into Prophet. Prophet has no opening book, no pondering support, no test suite support, and no HCE auto-tuner since all I have to do is run chess4j with the Prophet engine to leverage those features.

As I mentioned, running Prophet as a native library within chess4j does give chess4j a nice speed boost. But now I am asking the inverse question: how much slower is chess4j + Prophet than just plain Prophet? So, I put together a small test and was unpleasantly surprised by the results. Below are the results of doing a 30 second search.

PositionProphet knpschess4j knpschess4j + Prophet knps
initial position542810062058
r2qr1k1/p4ppp/5n2/2PpN3/8/2N1P1P1/1PP3BP/R4RK1 b – –523910262086
2r1r1k1/p4ppp/8/2Pp4/N7/4qNP1/1PP2RBP/4R2K b – –501310662127
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq –555711672115
rn1qk2r/ppp2ppp/3bpn2/3p4/6b1/1P2P1Q1/PBPP1PPP/RN2KBNR w KQkq –47749981800
rnbqkbnr/pp2pppp/8/3p4/8/2N2N2/PPPP1PPP/R1BQKB1R b KQkq –517610491984
r2qkb1r/1p1b1ppp/p3pn2/3p2B1/3P4/2N2N2/PPP2PPP/R2Q1RK1 b kq –531610852052
2kr1b1r/1pqb1p2/p3p2p/3pNp2/3P4/2NQ4/PPP2PPP/R3R1K1 b – –478310131941
2k3rr/1pqb4/p2bpp1p/3p1p2/NP1P4/P2Q1N1P/2P2PP1/R3R1K1 b – –44409441924
2k4r/1pqN4/p2b3p/3Q1p2/1P6/P4p1P/2P2Pr1/R3RK2 b – –573612402555

On average, loading Prophet as a native library into chess4j is 1.95x the speed of chess4j by itself. That’s great! However, chess4j + Prophet is about 0.4x the speed of Prophet alone. That’s no so great.

So, why does this matter? It goes back to the goals for each project. chess4j was started as a test bed to learn about Java based technologies. I’ve never meant for it to be a competitive program, so from that perspective it really doesn’t matter. That said, I would like Prophet to be a competitive engine.

Prophet has been on the Computer Chess Ratings List for some time. Prophet 5.0 is rated 2648 in Blitz as I write this. They test Prophet as a standalone engine, so this issue has absolutely no bearing on that. The CCRL team uses a generic opening book, not the engine’s opening book, and they disable pondering, so it’s not an issue for Prophet to be missing those features. Sometimes I enter programmer tournaments though, where programmers run their programs on their own machines in whatever configuration they’d like. Opening books do matter here. Pondering matters. So, in these tournaments I run chess4j + Prophet to take advantage of those features. But, apparently I’m taking a massive penalty in terms of speed to do so. What to do?

If I want to compete in programmer tournaments with Prophet, I’m going to have to find a way to significantly reduce that overhead, or add opening book and pondering support directly into Prophet. I’m currently migrating away from JNI to Java’s Foreign Function and Memory API (FFM). FFM is Java’s new approach to interacting with native code. At the very least, it will significantly simplify the codebase and the build process. I’m hopeful it will also be more performant. I’m guesstimating this migration will be complete towards the end of Sept. or early Oct. Once it is, I’ll repeat the test above and make some decisions.

UPDATE 9/16/25: SEE FOLLOW UP IN Goodbye JNI, Hello FFM . (TL;DR : JNI was not the issue. shared vs static compilation of the Prophet library was.)