blob: faae421d1d9f0350291b1fdc8b52935f4545399b [file] [log] [blame] [view]
---
title: GraalVM Guide
sidebar_position: 6
id: graalvm_guide
license: |
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---
## GraalVM Native Image
GraalVM `native image` can compile java code into native code ahead to build faster, smaller, leaner applications.
The native image doesn't have a JIT compiler to compile bytecode into machine code, and doesn't support
reflection unless configure reflection file.
Fory runs on GraalVM native image pretty well. Fory generates all serializer code for `Fory JIT framework` and `MethodHandle/LambdaMetafactory` at graalvm build time. Then use those generated code for serialization at runtime without
any extra cost, the performance is great.
In order to use Fory on graalvm native image, you must create Fory as an **static** field of a class, and **register** all classes at
the enclosing class initialize time. Then configure `native-image.properties` under
`resources/META-INF/native-image/$xxx/native-image.propertie` to tell graalvm to init the class at native image
build time. For example, here we configure `org.apache.fory.graalvm.Example` class be init at build time:
```properties
Args = --initialize-at-build-time=org.apache.fory.graalvm.Example
```
Another benefit using fory is that you don't have to configure [reflection json](https://www.graalvm.org/latest/reference-manual/native-image/metadata/#specifying-reflection-metadata-in-json) and
[serialization json](https://www.graalvm.org/latest/reference-manual/native-image/metadata/#serialization), which is
very tedious, cumbersome and inconvenient. When using fory, you just need to invoke
`org.apache.fory.Fory.register(Class<?>, boolean)` for every type you want to serialize.
Note that Fory `asyncCompilationEnabled` option will be disabled automatically for graalvm native image since graalvm
native image doesn't support JIT at the image run time.
## Not thread-safe Fory
Example:
```java
import org.apache.fory.Fory;
import org.apache.fory.util.Preconditions;
import java.util.List;
import java.util.Map;
public class Example {
public record Record (
int f1,
String f2,
List<String> f3,
Map<String, Long> f4) {
}
static Fory fory;
static {
fory = Fory.builder().build();
// register and generate serializer code.
fory.register(Record.class, true);
}
public static void main(String[] args) {
Record record = new Record(10, "abc", List.of("str1", "str2"), Map.of("k1", 10L, "k2", 20L));
System.out.println(record);
byte[] bytes = fory.serialize(record);
Object o = fory.deserialize(bytes);
System.out.println(o);
Preconditions.checkArgument(record.equals(o));
}
}
```
Then add `org.apache.fory.graalvm.Example` build time init to `native-image.properties` configuration:
```properties
Args = --initialize-at-build-time=org.apache.fory.graalvm.Example
```
## Thread-safe Fory
```java
import org.apache.fory.Fory;
import org.apache.fory.ThreadLocalFory;
import org.apache.fory.ThreadSafeFory;
import org.apache.fory.util.Preconditions;
import java.util.List;
import java.util.Map;
public class ThreadSafeExample {
public record Foo (
int f1,
String f2,
List<String> f3,
Map<String, Long> f4) {
}
static ThreadSafeFory fory;
static {
fory = new ThreadLocalFory(classLoader -> {
Fory f = Fory.builder().build();
// register and generate serializer code.
f.register(Foo.class, true);
return f;
});
}
public static void main(String[] args) {
System.out.println(fory.deserialize(fory.serialize("abc")));
System.out.println(fory.deserialize(fory.serialize(List.of(1,2,3))));
System.out.println(fory.deserialize(fory.serialize(Map.of("k1", 1, "k2", 2))));
Foo foo = new Foo(10, "abc", List.of("str1", "str2"), Map.of("k1", 10L, "k2", 20L));
System.out.println(foo);
byte[] bytes = fory.serialize(foo);
Object o = fory.deserialize(bytes);
System.out.println(o);
}
}
```
Then add `org.apache.fory.graalvm.ThreadSafeExample` build time init to `native-image.properties` configuration:
```properties
Args = --initialize-at-build-time=org.apache.fory.graalvm.ThreadSafeExample
```
## Framework Integration
For framework developers, if you want to integrate fory for serialization, you can provided a configuration file to let
the users to list all the classes they want to serialize, then you can load those classes and invoke
`org.apache.fory.Fory.register(Class<?>, boolean)` to register those classes in your Fory integration class, and configure that
class be initialized at graalvm native image build time.
## Benchmark
Here we give two class benchmarks between Fory and Graalvm Serialization.
When Fory compression is disabled:
- Struct: Fory is `46x speed, 43% size` compared to JDK.
- Pojo: Fory is `12x speed, 56% size` compared to JDK.
When Fory compression is enabled:
- Struct: Fory is `24x speed, 31% size` compared to JDK.
- Pojo: Fory is `12x speed, 48% size` compared to JDK.
See [[Benchmark.java](https://github.com/apache/fory/blob/main/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java)] for benchmark code.
### Struct Benchmark
#### Class Fields
```java
public class Struct implements Serializable {
public int f1;
public long f2;
public float f3;
public double f4;
public int f5;
public long f6;
public float f7;
public double f8;
public int f9;
public long f10;
public float f11;
public double f12;
}
```
#### Benchmark Results
No compression:
```
Benchmark repeat number: 400000
Object type: class org.apache.fory.graalvm.Struct
Compress number: false
Fory size: 76.0
JDK size: 178.0
Fory serialization took mills: 49
JDK serialization took mills: 2254
Compare speed: Fory is 45.70x speed of JDK
Compare size: Fory is 0.43x size of JDK
```
Compress number:
```
Benchmark repeat number: 400000
Object type: class org.apache.fory.graalvm.Struct
Compress number: true
Fory size: 55.0
JDK size: 178.0
Fory serialization took mills: 130
JDK serialization took mills: 3161
Compare speed: Fory is 24.16x speed of JDK
Compare size: Fory is 0.31x size of JDK
```
### Pojo Benchmark
#### Class Fields
```java
public class Foo implements Serializable {
int f1;
String f2;
List<String> f3;
Map<String, Long> f4;
}
```
#### Benchmark Results
No compression:
```
Benchmark repeat number: 400000
Object type: class org.apache.fory.graalvm.Foo
Compress number: false
Fory size: 541.0
JDK size: 964.0
Fory serialization took mills: 1663
JDK serialization took mills: 16266
Compare speed: Fory is 12.19x speed of JDK
Compare size: Fory is 0.56x size of JDK
```
Compress number:
```
Benchmark repeat number: 400000
Object type: class org.apache.fory.graalvm.Foo
Compress number: true
Fory size: 459.0
JDK size: 964.0
Fory serialization took mills: 1289
JDK serialization took mills: 15069
Compare speed: Fory is 12.11x speed of JDK
Compare size: Fory is 0.48x size of JDK
```