From 003197f0050b8ef3d52d2c291401991a562c773c Mon Sep 17 00:00:00 2001 From: Alan Paxton Date: Tue, 20 Feb 2024 13:44:35 -0800 Subject: [PATCH] Foreign function interface (Panama) blog (#11760) Summary: We did some experimental work with FFI and native memory as a potential improvement to the Java API. The work lives (unmerged) in https://github.com/facebook/rocksdb/pull/11095 This is the report text from that branch, extract as a blog post. Along with some supporting files (png, pdf of graphs). Pull Request resolved: https://github.com/facebook/rocksdb/pull/11760 Reviewed By: hx235 Differential Revision: D53943442 Pulled By: ajkr fbshipit-source-id: 7c9f800e25be22c10e736cdd3b0d65422ecfc826 --- ...-02-20-foreign-function-interface.markdown | 271 ++++++++++++++++++ docs/_posts/jmh/plot/jmh-result-fixed.png | Bin 0 -> 12059 bytes .../jmh-result-pinnable-vs-output-plot.pdf | Bin 0 -> 18048 bytes docs/_posts/jmh/plot/jmh-result-select.png | Bin 0 -> 15106 bytes 4 files changed, 271 insertions(+) create mode 100644 docs/_posts/2024-02-20-foreign-function-interface.markdown create mode 100644 docs/_posts/jmh/plot/jmh-result-fixed.png create mode 100644 docs/_posts/jmh/plot/jmh-result-pinnable-vs-output-plot.pdf create mode 100644 docs/_posts/jmh/plot/jmh-result-select.png diff --git a/docs/_posts/2024-02-20-foreign-function-interface.markdown b/docs/_posts/2024-02-20-foreign-function-interface.markdown new file mode 100644 index 00000000000..1cdacc0feb0 --- /dev/null +++ b/docs/_posts/2024-02-20-foreign-function-interface.markdown @@ -0,0 +1,271 @@ +--- +title: Java Foreign Function Interface +layout: post +author: alanpaxton +category: blog +--- +# Java Foreign Function Interface (FFI) + +Java19 introduces a new [FFI Preview](https://openjdk.org/jeps/424) which is described as *an API by which Java programs can interoperate with code and data outside of the Java runtime. By efficiently invoking foreign functions (i.e., code outside the JVM), and by safely accessing foreign memory (i.e., memory not managed by the JVM), the API enables Java programs to call native libraries and process native data without the brittleness and danger of JNI*. + +If the twin promises of efficiency and safety are realised, then using FFI as a mechanism to support a future RocksDB API may be of significant benefit. + + - Remove the complexity of `JNI` access to `C++ RocksDB` + - Improve RocksDB Java API performance + - Reduce the opportunity for coding errors in the RocksDB Java API + + ## Process + + We have + + - created a prototype FFI branch + - updated the RocksDB Java build to use Java 19 + - implemented an `FFI Preview API` version of core RocksDB feature (`get()`) + - Extended the current JMH benchmarks to also benchmark the new FFI methods. Usefully, JNI and FFI can co-exist peacefully, so we use the existing RocksDB Java to do support work around the FFI-based `get()` implementation. + +## Implementation + +### How JNI Works + +`JNI` requires a preprocessing step during build/compilation to generate header files which are linked into by Pure Java code. `C++` implementations of the methods in the headers are implemented. Corresponding `native` methods are declared in Java and the whole is linked together. + +Code in the `C++` methods uses what amounts to a `JNI` library to access Java values and objects and to create Java objects in response. + +### How FFI Works + +`FFI` provides the facility for Java to call existing native (in our case C++) code from Pure Java without having to generate support files during compilation steps. `FFI` does support an external tool (`jextract`) which makes generating common boilerplate easier and less error prone, but we choose to start prototyping without it, in part better to understand how things really work. + +`FFI` does its job by providing 2 things +1. A model for allocating, reading and writing native memory and native structures within that memory +2. A model for discovering and calling native methods with parameters consisting of native memory references and/or values + +The called `C++` is invoked entirely natively. It does not have to access any Java objects to retrieve data it needs. Therefore existing packages in `C++` and other sufficiently low level languages can be called from `Java` without having to implement stubs in the `C++`. + +### Our Approach + +While we could in principle avoid writing any C++, C++ objects and classes are not easily defined in the FFI model, so to begin with it is easier to write some very simple `C`-like methods/stubs in C++ which can immediately call into the object-oriented core of RocksDB. We define structures with which to pass parameters to and receive results from the `C`-like method(s) we implement. + +#### `C++` Side + +The first method we implement is +```C +extern "C" int rocksdb_ffi_get_pinnable( + ROCKSDB_NAMESPACE::DB* db, ROCKSDB_NAMESPACE::ReadOptions* read_options, + ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf, rocksdb_input_slice_t* key, + rocksdb_pinnable_slice_t* value); +``` +our input structure is +```C +typedef struct rocksdb_input_slice { + const char* data; + size_t size; +} rocksdb_input_slice_t; +``` +and our output structure is a pinnable slice (of which more later) +```C +typedef struct rocksdb_pinnable_slice { + const char* data; + size_t size; + ROCKSDB_NAMESPACE::PinnableSlice* pinnable_slice; + bool is_pinned; +} rocksdb_pinnable_slice_t; +``` + +#### `Java` Side + + We implement an `FFIMethod` class to advertise a `java.lang.invoke.MethodHandle` for each of our helper stubs + + ```java + public static MethodHandle GetPinnable; // handle which refers to the rocksdb_ffi_get_pinnable method in C++ + public static MethodHandle ResetPinnable; // handle which refers to the rocksdb_ffi_reset_pinnable method in C++ +``` +We also implement an `FFILayout` class to describe each of the passed structures (`rocksdb_input_slice` , `rocksdb_pinnable_slice` and `rocksdb_output_slice`) in `Java` terms + + ```java + public static class InputSlice { + static final GroupLayout Layout = ... + static final VarHandle Data = ... + static final VarHandle Size = ... + }; + + public static class PinnableSlice { + static final GroupLayout Layout = ... + static final VarHandle Data = ... + static final VarHandle Size = ... + static final VarHandle IsPinned = ... + }; + + public static class OutputSlice { + static final GroupLayout Layout = ... + static final VarHandle Data = ... + static final VarHandle Size = ... + }; + ``` + + The `FFIDB` class, which implements the public Java FFI API methods, makes use of `FFIMethod` and `FFILayout` to make the code for each individual method as idiomatic and efficient as possible. This class also contains `java.lang.foreign.MemorySession` and `java.lang.foreign.SegmentAllocator` objects which control the lifetime of native memory sessions and allow us to allocate lifetime-limited native memory which can be written and read by Java, and passed to native methods. + + At the user level, we then present a method which wraps the details of use of `FFIMethod` and `FFILayout` to implement our single, core Java API `get()` method + + ```java + public GetPinnableSlice getPinnableSlice(final ReadOptions readOptions, + final ColumnFamilyHandle columnFamilyHandle, final MemorySegment keySegment, + final GetParams getParams) + ``` + +The flow of implementation of `getPinnableSlice()`, in common with any other core RocksDB FFI API method becomes: + 1. Allocate `MemorySegment`s for `C++` structures using `Layout`s from `FFILayout` + 2. Write to the allocated structures using `VarHandle`s from `FFILayout` + 3. Invoke the native method using the `MethodHandle` from `FFIMethod` and addresses of instantiated `MemorySegment`s, or value types, as parameters + 4. Read the call result and the output parameter(s), again using `VarHandle`s from `FFILayout` to perform the mapping. + +For the `getPinnableSlice()` method, on successful return from an invocation of `rocksdb_ffi_get()`, the `PinnableSlice` object will contain the `data` and `size` fields of a pinnable slice (see below) containing the requested value. A `MemorySegment` referring to the native memory of the pinnable slice is then constructed, and used by the client to retrieve the value in whatever fashion they choose. + +### Pinnable Slices + +RocksDB offers core (C++) API methods using the concept of a [`PinnableSlice`](http://rocksdb.org/blog/2017/08/24/pinnableslice.html) to return fetched data values while reducing copies to a minimum. We take advantage of this to base our central `get()` method(s) on `PinnableSlice`s. Methods mirroring the existing `JNI`-based API can then be implemented in pure Java by wrapping the core `getPinnableSlice()`. + +So we implement +```java +public record GetPinnableSlice(Status.Code code, Optional pinnableSlice) {} + +public GetPinnableSlice getPinnableSlice( + final ColumnFamilyHandle columnFamilyHandle, final byte[] key) +``` +and we wrap that to provide +```java +public record GetBytes(Status.Code code, byte[] value, long size) {} + +public GetBytes get(final ColumnFamilyHandle columnFamilyHandle, final byte[] key) +``` + +## Benchmark Results + +We extended existing RocksDB Java JNI benchmarks with new benchmarks based on FFI. Full benchmark run on Ubuntu, including new benchmarks. + +```bash +java --enable-preview --enable-native-access=ALL-UNNAMED -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=100000 -p keySize=128 -p valueSize=4096,65536 -p columnFamilyTestType="no_column_family" -rf csv org.rocksdb.jmh.GetBenchmarks +``` + +[Results](./jmh/plot/jmh-result.csv, ./jmh/plot/jmh-result-fixed2.csv) +![Plot](./jmh/plot/jmh-result-select.png) +![Plot](./jmh/plot/jmh-result-fixed.png) + +### Discussion + +We have plotted the performance (more operations is better) of a selection of benchmarks, +```bash +q "select Benchmark,Score from ./plot/jmh-result-fixed.csv where \"Param: keyCount\"=100000 and \"Param: valueSize\"=65536 -d, -H +``` + + - JNI versions of benchmarks are previously implemented `jmh` benchmarks for measuring the performance of the current RocksDB Java interface. + - FFI versions of benchmarks are equivalent benchmarks (as far as possible) implemented using the FFI mechanisms. + + We can see that for all benchmarks which have equivalent FFI and JNI pairs, the JNI version is only very marginally faster. FFI has successfully optimized away most of the extra safety-checking of the new invocation mechanism. + + Our initial implementation of FFI benchmarks lagged the JNI benchmarks quite significantly, but we have received extremely helpful support from Maurizio Cimadamore of the Panama Dev team, to help us optimize the performance of our FFI implementation. We consider that the small remaining performance gap is a feature of the remaining extra bounds checking of FFI. + + For basic `get()` the result buffer is allocated by the method, so that there is a cost of allocation associated with each request. + - `ffiGet` vs `get` + - The JNI version is very marginally faster than FFI + + For preallocated `get()` where the result buffer is supplied to the method, we avoid an allocation of a fresh result buffer on each call, and the test recycles its result buffers. Then the same small difference persists + - JNI is very marginally faster than FFI + - `preallocatedGet()` is a lot faster than basic `get()` + + We implemented some methods where the key for the `get()` is randomized, so that any ordering effects can be accounted for. The same differences persisted. + + The FFI interface gives us a natural way to expose RocksDB's [pinnable slice](http://rocksdb.org/blog/2017/08/24/pinnableslice.html) mechanism. When we provide a benchmark which accesses the raw `PinnableSlice` API, as expected this is the fastest method of any; however we are not comparing like with like: + - `ffiGetPinnableSlice()` returns a handle to the RocksDB memory containing the slice, and presents that as an FFI `MemorySegment`. No copying of the memory in the segment occurs. + + As noted above, we implement the new FFI-based `get()` methods using the new FFI-based `getPinnableSlice()` method, and copying out the result. So the `ffiGet` and `ffiPreallocatedGet` benchmarks use this mechanism underneath. + + In an effort to discover whether using the Java APIs to copy from the pinnable slice backed `MemorySegment` was a problem, we implemented a separate `ffiGetOutputSlice()` benchmark which copies the result into a (Java allocated native memory) segment at the C++ side. + - `ffiGetOutputSlice()` is faster than `ffiPreallocatedGet()` and is in fact at least as fast as `preallocatedGet()`, which is an almost exact analogue in the JNI world. + + So it appears that we can build an FFI-based API with equal performance to the JNI-based one. + + Thinking about the (very small, but probably statistically significant) difference between our `ffiGetPinnableSlice()`-based FFI calls and the JNI-based calls, it is reasonable to expect that some of the cost is the extra FFI call to C++ to release the pinned slice as a separate operation. A null FFI method call is extremely fast, but it does take some time. + + - We would recommend looking again the performance of the FFI-based implementation when Panama is release post-Preview in Java 21. It seems that at least with Java 20 the performance is of our FFI benchmarks is not significantly different from that of the Java 19 version. + +### Copies versus Calls + +The second method call over the FFI boundary to release a pinnable slice has a cost. We compared the `ffiGetOutputSlice()` and `ffiGetPinnableSlice()` benchmarks in order to examine this cost. We ran it with a fixed ky size (128 bytes); the key size is likely to be pretty much irrelevant anyway; we varied the value size read from 16 bytes to 16k, and we found a crossover point between 1k and 4k for performance: + +![Plot](./jmh/plot/jmh-result-pinnable-vs-output-plot.pdf) + +- `ffiGetOutputSlice()` is faster when values read are 1k in size or smaller. The cost of an extra copy in the C++ side from the pinnable slice buffer into the supplied buffer allocated by Java Foreign Memory API is less than the cost of the extra call to release a pinnable slice. +- `ffiGetPinnableSlice()` is faster when values read are 4k in size, or larger. Consistent with intuition, the advantage grows with larger read values. + +The way that the RocksDB API is constructed means that of the 2 methods compared, `ffiGetOutputSlice()` will always make exactly 1 more copy than `ffiGetPinnableSlice()`. The underlying RocksDB C++ API will always copy into its own temporary buffer if it decides that it cannot pin an internal buffer, and that will be returned as the pinnable slice. There is a potential optimization where the temporary buffer could be replaced by an output buffer, such as that supplied by `ffiGetOutputSlice()`; in practice that is a hard fix to hack in. Its effectiveness depends on how often RocksDB fails to pin an internal buffer. + +A solution which either filled a buffer *or* returned a pinnable slice would give us the best of both worlds. + +## Other Conclusions + +### Build Processing + +- It is easier to implement an interface using FFI than JNI. No intermediate build processing or code generation steps were needed to implement this protoype. + +- For a production version, we would urge using `jextract` to automate the process of generating Java API methods from the set of supporting stubs we generate. + +### Safety + +- The use of `jextract` will give a similar level of type security to the use of JNI, when crossing the language boundary. But we do not believe FFI is significantly more type-safe than JNI for method invocation. Neither is it less safe, though. + +### Native Memory + +Panama's *Foreign-Memory Access API* appears to us to be the most significant part of the whole project. At the `Java` side of RocksDB it gives us a clean mechanism (a `MemorySegment`) for holding RocksDB data (e.g. as from the result of a `get()`) call pending its forwarding to client code or network buffers. + +We have taken advantage of this mechanism to provide the core `FFIDB.getPinnableSlice()` method in our Panama-based API. The rest of our prototype `get()` API, duplicating the existing *JNI*-based API, is then a *Pure Java* library on top of `FFIDB.getPinnableSlice()` and `FFIPinnableSlice.reset()`. + +The common standard for foreign memory opens up the possibility of efficient interoperation between RocksDB and Java clients (e.g. Kafka). We think that this is really the key to higher performing, more integrated Java-based systems: +- This could result in data never being copied into Java memory, or a significant reduction in copies, as native `MemorySegment`s are handed off between co-operating Java clients of fundamentally native APIs. This extra potential performance can be extremely useful when 2 or more clients are interoperating; we still need to provide a simplest possible API wrapping these calls (like our prototype `get()`), which operates at a similar level to the current Java API. +- Some thought should be applied to how this architecture would interact with the cache layer(s) in RocksDB, and whether it can be accommodated within the present RocksDB architecture. How long can 3rd-party applications *pin* pages in the RocksDB cache without disrupting RocksDB normal behaviour (e.g. compaction) ? + +## Summary + +1. Panama/FFI (in [Preview](https://openjdk.org/jeps/424)) is a highly capable technology for (re)building the RocksDB Java API, although the supported language level of RocksDB and the planned release schedule for Panama mean that it could not replace JNI in production for some time to come. +2. Panama/FFI would seem to offer comparable performance to JNI; there is no strong performance argument *for* a re-implementation of a standalone RocksDB Java API. But the opportunity to provide a natural pinnable slice-based API gives a lot of flexibility; not least because an efficient API could be built mostly in Java with only a small underlying layer implementing the pinnable slice interface. +3. Panama/FFI can remove some boilerplate (native method declarations) and allow Java programs to access `C` libraries without stub code, but calling a `C++`-based library still requires `C` stubs; a possible approach would be to use the RocksDB `C` API as the basis for a rebuilt Java API. This would allow us to remove all the existing JNI boilerplate, and concentrate support effort on the `C` API. An alternative approach would be to build a robust API based on [Reference Counting](https://github.com/facebook/rocksdb/pull/10736), but using FFI. +4. Panama/FFI really shines as a foreign memory standard for a Java API that can allow efficient interoperation between RocksDB Java clients and other (Java and native) components of a system. Foreign Memory gives us a model for how to efficiently return data from RocksDB; as pinnable slices with their contents presented in `MemorySegment`s. If we focus on designing an API *for native interoperability* we think this can be highly productive in opening RocksDB to new uses and opportunities in future. + +## Appendix + +### Running + +Edit this to give you what you want... +```bash +java --enable-preview --enable-native-access=ALL-UNNAMED -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar -p keyCount=100000 -p keySize=128 -p valueSize=4096,65536 -p columnFamilyTestType="no_column_family" -rf csv org.rocksdb.jmh.GetBenchmarks -wi 1 -to 1m -i 1 +``` + +### Processing + +Use [`q`](http://harelba.github.io/q/) to select the csv output for analysis and graphing. + + - Note that we edited the column headings for easier processing + +```bash +q "select Benchmark,Score,Error from ./plot/jmh-result.csv where keyCount=100000 and valueSize=65536" -d, -H -C readwrite +``` + +### Java 19 installation + +I followed the instructions to install [Azul](https://docs.azul.com/core/zulu-openjdk/install/debian). Then you still need to pick the right java locally: +```bash +sudo update-alternatives --config java +sudo update-alternatives --config javac +``` +And set `JAVA_HOME` appropriately. In my case, `sudo update-alternatives --config java` listed a few JVMs thus: +``` + 0 /usr/lib/jvm/bellsoft-java8-full-amd64/bin/java 20803123 auto mode + 1 /usr/lib/jvm/bellsoft-java8-full-amd64/bin/java 20803123 manual mode + 2 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1111 manual mode +* 3 /usr/lib/jvm/zulu19/bin/java 2193001 manual mode +``` +so I did this: +```bash +export JAVA_HOME=/usr/lib/jvm/zulu19 +``` + +The default maven on ubuntu (3.6.3) is incompatible with Java 19. You will need to install a later [Maven](https://maven.apache.org/install.html), and use it. I used `3.8.7` successfully. \ No newline at end of file diff --git a/docs/_posts/jmh/plot/jmh-result-fixed.png b/docs/_posts/jmh/plot/jmh-result-fixed.png new file mode 100644 index 0000000000000000000000000000000000000000..0d394dd1eb7c4ca4e15508fc661eea5d7be3103d GIT binary patch literal 12059 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 literal 0 HcmV?d00001 diff --git a/docs/_posts/jmh/plot/jmh-result-pinnable-vs-output-plot.pdf b/docs/_posts/jmh/plot/jmh-result-pinnable-vs-output-plot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f958eb08ac262e9c02a84a3e61af931edc119db5 GIT binary patch literal 18048 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 literal 0 HcmV?d00001