C轮融资|Fury:一个基于JIT动态编译的高性能多语言原生序列化框架( 五 )


interface{ var buffers [
*fury.ByteBuffer for _ o := range serializedObjects { buffers = append(buffers o.ToBuffer())err := fury.Deserialize(bufnewList buffers) fmt.Println(newList) Drop-in替换Kryo/Hession
除了多语言原生序列化以外 , Fury还是一个高性能的通用Java序列化框架 , 可以序列化任意Java Object , 完全兼容JDK序列化 , 包括支持序列化自定义writeObject/readObject/writeReplace/readResolve的对象 , 支持堆内/堆外内存 。 可以Drop-in替换jdk/kryo/hession等序列化框架 , 性能最高是Kryo 20倍以上 , Hession100倍以上 , JDK自带序列化200倍 。
下面是一个序列化自定义类型的示例:
import io.fury.Fury;import java.util.List;import java.util.Arrays;public class Example { public static void main(String[
args) { SomeClass object = new SomeClass(); // Fury实例应该在序列化多个对象之间复用 , 不要每次创建新的实例 { Fury fury = Fury.builder() .withLanguage(Language.JAVA) // 设置为true可以避免反序列化未注册的非内置类型 ,// 避免安全漏洞 .withClassRegistrationRequired(false) .withReferenceTracking(true).build(); // 注册类型可以减少classname的序列化 , 不是强制要求 // fury.register(SomeClass.class); byte[
bytes = fury.serialize(object); System.out.println(fury.deserialize(bytes));{ ThreadSafeFury fury = Fury.builder().withLanguage(Language.JAVA) .withReferenceTracking(true) .withClassRegistrationRequired(false) .buildThreadSafeFury(); byte[
bytes = fury.serialize(object); System.out.println(fury.deserialize(bytes));{ ThreadSafeFury fury = new ThreadSafeFury(() -{ Fury fury = Fury.builder() .withLanguage(Language.JAVA) .withClassRegistrationRequired(false) .withReferenceTracking(true).build(); // 注册类型可以减少classname的序列化 fury.register(SomeClass.class); return fury; ); byte[
bytes = fury.serialize(object); System.out.println(fury.deserialize(bytes));通过Fury Format避免序列化
对于有极致性能要求的场景 , 如果用户只需要读取部分数据 , 或者在Serving场景根据对象树某个字段进行过滤和转发 , 可以使用Fury Format来避免其它字段的序列化 。 Fury Row Format是参考SQL行存和Arrow列存实现的一套可以随机访问的二进制行存结构 。 目前实现了Java/Python/C++版本 , Python版本通过Cython绑定到C++实现 。
由于该格式是自包含的 , 可以根据schema直接计算出任意字段的offset 。 因此通过使用该格式 , 可以避免掉序列化 , 直接在二进制数据buffer上面进行所有读写操作 , 这样做有三个优势:
减少Java GC overhead 。 由于避免了反序列化 , 因此不会创建对象 , 从而避免了GC问题 。避免Python反序列化 。 Python性能一直很慢 , 因此在跨语言序列化时 , 可以在Java/C++侧把对象序列化成Row-Format , 然后Python侧直接使用该数据计算 , 这样就避免了Python反序列化的昂贵开销 。 同时由于Python的动态性 , Fury的BinaryRow/BinaryArrays实现了_getattr__/__getitem__/slice/和其它special methods , 保证了行为跟python pojo/list/object的一致性 , 用户没有任何感知 。缓存友好 , 数据密集存储 。Python示例
这里给出一个读取部分数据的样例以及性能测试结果 。 在下面这个序列化场景中 , 需要读取第二个数组字段的第10万个元素 , Fury耗时几乎为0 , 而pickler需要8秒 。
@dataclassclass Bar: f1: str f2: List[pa.int64
@dataclassclass Foo: f1: pa.int32 f2: List[pa.int32
f3: Dict[str pa.int32
f4: List[Bar
encoder = pyfury.encoder(Foo)foo = Foo(f1=10 f2=list(range(1000_000)) f3={f\"k{i\": i for i in range(1000_000) f4=[Bar(f1=f\"s{i\" f2=list(range(10))) for i in range(1000_000)
)binary: bytes = encoder.to_row(foo).to_bytes()print(f\"start: {datetime.datetime.now()\")foo_row = pyfury.RowData(encoder.schema binary)print(foo_row.f2[100000
foo_row.f4[100000