Fury:一个基于JIT动态编译的高性能多语言原生序列化框架

引言

过去十多年大数据和分布式系统蓬勃发展,序列化是其频繁使用的技术。当对象需要跨进程、跨语言、跨节点传输、持久化、状态读写时,都需要进行序列化,其性能和易用性影响着系统的运行效率和开发效率

成都创新互联公司网站建设公司,提供成都网站制作、成都网站设计,网页设计,建网站,PHP网站建设等专业做网站服务;可快速的进行网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,是专业的做网站团队,希望更多企业前来合作!

对于Java序列化,尽管Kryo[1]等框架提供了相比JDK序列化数倍的性能,对于高吞吐、低延迟、大规模数据传输场景,序列化仍然是整个系统的性能瓶颈。为了优化序列化的性能,分布式系统如Spark[2]、Flink[3]使用了专有行列存二进制格式如tungsten[4]和arrow[5]。这些格式减少了序列化开销,但增加了系统的复杂性,牺牲了编程的灵活性,同时也只覆盖了SQL等关系代数计算专有场景。对于通用分布式编程和跨进程通信,序列化性能始终是一个绕不过去的关键问题。

同时随着计算和应用场景的日益复杂化,系统已经从单一语言的编程范式发展到多语言融合编程,对象在语言之间传输的易用性影响着系统开发效率,进而影响业务的迭代效率。而已有的跨语言序列化框架protobuf/flatbuffer/msgpack等由于无法支持引用、不支持Zero-Copy、大量手写代码以及生成的类不符合面向对象设计[6]无法给类添加行为,导致在易用性、灵活性、动态性和性能上的不足,并不能满足通用跨语言编程需求。

基于此,我们开发了Fury,通过一套支持引用、类型嵌入的语言无关协议,以及JIT动态编译加速、缓存优化和Zero-Copy等技术,实现了任意对象像动态语言自动序列化一样跨语言自动序列化,消除了语言之间的编程边界,并提供相比于业界别的框架最高20~200倍的性能

Fury是什么

Fury是一个基于JIT的高性能多语言原生序列化框架,专注于提供极致的序列化性能和易用性

  • 支持主流编程语言如Java/Python/C++/Golang,其它语言可轻易扩展;
  • 多语言/跨语言自动序列化任意对象,无需创建IDL文件、手动编译schema生成代码以及将对象转换为中间格式;
  • 多语言/跨语言自动序列化共享引用和循环引用,用户只需要关心对象,不需要关心数据重复或者递归错误;
  • 基于JIT动态编译技术在运行时自动生成序列化代码优化性能,增加方法内联、代码缓存和死代码消除,减少虚方法调用/条件分支/Hash查找/元数据写入等,提供相比其它序列化框架20~200倍以上的性能
  • Zero-Copy序列化支持,支持Out of band序列化协议,支持堆外内存读写;
  • 提供缓存友好的二进制随机访问行存格式,支持跳过序列化和部分序列化,并能和列存自动互转;

除了跨语言能力,Fury还具备以下能力:

  • 无缝替代JDK/Kryo/Hessian等Java序列化框架,无需修改任何代码,同时提供相比Kryo 20倍以上的性能,相比Hessian100倍以上的性能,相比JDK自带序列化200倍以上的性能,可以大幅提升高性能场景RPC调用和对象持久化效率
  • 支持共享引用和循环引用Golang序列化框架;
  • 支持对象自动序列化的Golang序列化框架;

目前Fury已经支持Java、Python、Golang以及C++。本文将首先简单介绍如何使用Fury,然后将Fury跟别的序列化框架进行功能、性能和易用性比较,Fury的实现原理将在后续文章里面详细介绍。

如何使用Fury

这里给出跨语言序列化、纯Java序列化以及避免序列化的示例:

  • 跨语言序列化自定义类型
  • 跨语言序列化包含循环引用的自定义类型
  • 跨语言零拷贝序列化
  • Drop-in替代Kryo/Hession/JDK序列化

通过Fury Format避免序列化

序列化自定义类型

下面是序列化用户自定义类型的一个示例,该类型里面包含多个基本类型以及嵌套类型的字段,在业务应用里面相当常见。需要注意自定义类型跨语言序列化之前需要调用`register`API注册自定义类型,建立类型在不同语言之间的映射关系,同时保证GoLang等静态语言编译器编译代码时不裁剪掉这部分类型的符号

Java序列化示例

ap f2;
}
public static class SomeClass2 {
Object f1;
String f2;
List f3;
Map f4;
Byte f5;
Short f6;
Integer f7;
Long f8;
Float f9;
Double f10;
short[] f11;
List f12;
}
public static Object createObject() {
SomeClass1 obj1 = new SomeClass1();
obj1.f1 = true;
obj1.f2 = ImmutableMap.of((byte) -1, 2);
SomeClass2 obj = new SomeClass2();
obj.f1 = obj1;
obj.f2 = "abc";
obj.f3 = Arrays.asList("abc", "abc");
obj.f4 = ImmutableMap.of((byte) 1, 2);
obj.f5 = Byte.MAX_VALUE;
obj.f6 = Short.MAX_VALUE;
obj.f7 = Integer.MAX_VALUE;
obj.f8 = Long.MAX_VALUE;
obj.f9 = 1.0f / 2;
obj.f10 = 1 / 3.0;
obj.f11 = new short[] {(short) 1, (short) 2};
obj.f12 = ImmutableList.of((short) -1, (short) 4);
return obj;
}
}

纯Java序列化:

public class CustomObjectExample {
// mvn exec:java -Dexec.mainClass="io.fury.examples.CustomObjectExample"
public static void main(String[] args) {
// Fury应该在多个对象序列化之间复用,不要每次创建新的Fury实例
Fury fury = Fury.builder().withLanguage(Language.JAVA)
.withReferenceTracking(false)
.withClassRegistrationRequired(false)
.build();
byte[] bytes = fury.serialize(createObject());
System.out.println(fury.deserialize(bytes));;
}
}

跨语言序列化:

public class CustomObjectExample {
// mvn exec:java -Dexec.mainClass="io.fury.examples.CustomObjectExample"
public static void main(String[] args) {
// Fury应该在多个对象序列化之间复用,不要每次创建新的Fury实例
Fury fury = Fury.builder().withLanguage(Language.XLANG)
.withReferenceTracking(false).build();
fury.register(SomeClass1.class, "example.SomeClass1");
fury.register(SomeClass2.class, "example.SomeClass2");
byte[] bytes = fury.serialize(createObject());
// bytes can be data serialized by other languages.
System.out.println(fury.deserialize(bytes));;
}
}

Python序列化示例

from dataclasses import dataclass
from typing import List, Dict
import pyfury


@dataclass
class SomeClass2:
f1: Any = None
f2: str = None
f3: List[str] = None
f4: Dict[pyfury.Int8Type, pyfury.Int32Type] = None
f5: pyfury.Int8Type = None
f6: pyfury.Int16Type = None
f7: pyfury.Int32Type = None
# int类型默认会按照long类型进行序列化,如果对端是更加narrow的类型,
# 需要使用pyfury.Int32Type等进行标注
f8: int = None # 也可以使用pyfury.Int64Type进行标注
f9: pyfury.Float32Type = None
f10: float = None # 也可以使用pyfury.Float64Type进行标注
f11: pyfury.Int16ArrayType = None
f12: List[pyfury.Int16Type] = None


@dataclass
class SomeClass1:
f1: Any
f2: Dict[pyfury.Int8Type, pyfury.Int32Type]


if __name__ == "__main__":
fury_ = pyfury.Fury(reference_tracking=False)
fury_.register_class(SomeClass1, "example.SomeClass1")
fury_.register_class(SomeClass2, "example.SomeClass2")
obj2 = SomeClass2(f1=True, f2={-1: 2})
obj1 = SomeClass1(
f1=obj2,
f2="abc",
f3=["abc", "abc"],
f4={1: 2},
f5=2 ** 7 - 1,
f6=2 ** 15 - 1,
f7=2 ** 31 - 1,
f8=2 ** 63 - 1,
f9=1.0 / 2,
f10=1 / 3.0,
f11=array.array("h", [1, 2]),
f12=[-1, 4],
)
data = fury_.serialize(obj)
# bytes can be data serialized by other languages.
print(fury_.deserialize(data))

GoLang序列化示例

package main


import "code.alipay.com/ray-project/fury/go/fury"
import "fmt"


func main() {
type SomeClass1 struct {
F1 interface{}
F2 string
F3 []interface{}
F4 map[int8]int32
F5 int8
F6 int16
F7 int32
F8 int64
F9 float32
F10 float64
F11 []int16
F12 fury.Int16Slice
}
type SomeClas2 struct {
F1 interface{}
F2 map[int8]int32
}
fury_ := fury.NewFury(false)
if err := fury_.RegisterTagType("example.SomeClass1", SomeClass1{}); err != nil {
panic(err)
}
if err := fury_.RegisterTagType("example.SomeClass2", SomeClass2{}); err != nil {
panic(err)
}
obj2 := &SomeClass2{}
obj2.F1 = true
obj2.F2 = map[int8]int32{-1: 2}
obj := &SomeClass1{}
obj.F1 = obj2
obj.F2 = "abc"
obj.F3 = []interface{}{"abc", "abc"}
f4 := map[int8]int32{1: 2}
obj.F4 = f4
obj.F5 = fury.MaxInt8
obj.F6 = fury.MaxInt16
obj.F7 = fury.MaxInt32
obj.F8 = fury.MaxInt64
obj.F9 = 1.0 / 2
obj.F10 = 1 / 3.0
obj.F11 = []int16{1, 2}
obj.F12 = []int16{-1, 4}
bytes, err := fury_.Marshal(value)
if err != nil {
}
var newValue interface{}
// bytes can be data serialized by other languages.
if err := fury_.Unmarshal(bytes, &newValue); err != nil {
panic(err)
}
fmt.Println(newValue)
}

序列化共享&循环引用

共享引用和循环引用是程序里面常见的构造,很多数据结构如图都包含大量的循环引用,而手动实现这些包含共享引用和循环引用的对象,需要大量冗长复杂易出错的代码。跨语言序列化框架支持循环引用可以极大简化这些复杂场景的序列化,加速业务迭代效率。下面是一个包含循环引用的自定义类型跨语言序列化示例。

Java序列化示例

import com.google.common.collect.ImmutableMap;
import io.fury.*;
import java.util.Map;


public class ReferenceExample {
public static class SomeClass {
SomeClass f1;
Map f2;
Map f3;
}
public static Object createObject() {
SomeClass obj = new SomeClass();
obj.f1 = obj;
obj.f2 = ImmutableMap.of("k1", "v1", "k2", "v2");
obj.f3 = obj.f2;
return obj;
}
}

Java序列化:

public class ReferenceExample {    
// mvn exec:java -Dexec.mainClass="io.fury.examples.ReferenceExample"
public static void main(String[] args) {
// Fury应该在多个对象序列化之间复用,不要每次创建新的Fury实例
Fury fury = Fury.builder().withLanguage(Language.JAVA)
.withReferenceTracking(true)
.withClassRegistrationRequired(false)
.build();
byte[] bytes = fury.serialize(createObject());
System.out.println(fury.deserialize(bytes));;
}
}

跨语言序列化:

public class ReferenceExample {
// mvn exec:java -Dexec.mainClass="io.fury.examples.ReferenceExample"
public static void main(String[] args) {
// Fury应该在多个对象序列化之间复用,不要每次创建新的Fury实例
Fury fury = Fury.builder().withLanguage(Language.XLANG)
.withReferenceTracking(true).build();
fury.register(SomeClass.class, "example.SomeClass");
byte[] bytes = fury.serialize(createObject());
// bytes can be data serialized by other languages.
System.out.println(fury.deserialize(bytes));;
}
}

Python序列化示例

from typing import Dict
import pyfury


class SomeClass:
f1: "SomeClass"
f2: Dict[str, str]
f3: Dict[str, str]




if __name__ == "__main__":
fury_ = pyfury.Fury(reference_tracking=True)
fury_.register_class(SomeClass, "example.SomeClass")
obj = SomeClass()
obj.f2 = {"k1": "v1", "k2": "v2"}
obj.f1, obj.f3 = obj, obj.f2
data = fury_.serialize(obj)
# bytes can be data serialized by other languages.
print(fury_.deserialize(data))

Golang序列化示例

package main


import "code.alipay.com/ray-project/fury/go/fury"
import "fmt"


func main() {
type SomeClass struct {
F1 *SomeClass
F2 map[string]string
F3 map[string]string
}
fury_ := fury.NewFury(true)
if err := fury_.RegisterTagType("example.SomeClass", SomeClass{}); err != nil {
panic(err)
}
value := &SomeClass{F2: map[string]string{"k1": "v1", "k2": "v2"}}
value.F3 = value.F2
value.F1 = value
bytes, err := fury_.Marshal(value)
if err != nil {
}
var newValue interface{}
// bytes can be data serialized by other languages.
if err := fury_.Unmarshal(bytes, &newValue); err != nil {
panic(err)
}
fmt.Println(newValue)
}

Zero-Copy序列化

对于大规模数据传输场景,内存拷贝有时会成为整个系统的瓶颈。为此各种语言和框架做了大量优化,比如Java提供了NIO能力,避免了内存在用户态和内核态之间的来回拷贝;Kafka使用Java的NIO来实现零拷贝;Python Pickle5提供了Out-Of-Band Buffer[7]序列化能力来避免额外拷贝。对于高性能跨语言数据传输,序列化框架也需要能够支持Zero-Copy,避免数据Buffer的额外拷贝。下面是一个Fury序列化多个基本类型数组组成的对象树的示例,分别对应到Java基本类型数组、Python Numpy数组、Golang 基本类型slice。对于ByteBuffer零拷贝,在本文的性能测试部分也给出了部分介绍。

Java序列化示例

Java序列化

import io.fury.*;
import io.fury.serializers.BufferObject;
import io.fury.memory.MemoryBuffer;
import java.util.*;
import java.util.stream.Collectors;


public class ZeroCopyExample {
// mvn exec:java -Dexec.mainClass="io.fury.examples.ZeroCopyExample"
public static void main(String[] args) {
// Fury应该在多个对象序列化之间复用,不要每次创建新的Fury实例
Fury fury = Fury.builder()
.withLanguage(Language.JAVA)
.withClassRegistrationRequired(false)
.build();
List list = Arrays.asList("str", new byte[1000], new int[100], new double[100]);
Collection bufferObjects = new ArrayListFury:一个基于JIT动态编译的高性能多语言原生序列化框架
文章起源:http://www.csdahua.cn/qtweb/news11/501461.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网