Apache Fury (インキュベート)は、 JIT (ジャストインタイム コンパイル) とゼロコピーを利用した超高速の多言語シリアル化フレームワークで、最大 170 倍のパフォーマンスと究極の使いやすさを提供します。
https://fury.apache.org
重要
Apache Fury (インキュベート) は、Apache Incubator PMC の後援により、Apache Software Foundation (ASF) でインキュベーション中の取り組みです。
免責事項と「インキュベート」の詳細な説明をお読みください。
Fury は、言語を越えたシリアル化に加えて、次の機能も備えています。
writeObject
/ readObject
/ writeReplace
/ readResolve
/ readObjectNoData
/ Externalizable
API をサポートします。record
もサポートされます。Fury は、さまざまなシナリオ向けに複数のバイナリ プロトコルを設計および実装しました。
Fury の既存のバッファ、エンコーディング、メタ、codegen などの機能に基づいて、新しいプロトコルを簡単に追加できます。これらはすべて同じコードベースを共有しており、あるプロトコルの最適化を別のプロトコルで再利用できます。
さまざまなシリアル化フレームワークがさまざまなシナリオに適しており、ここでのベンチマーク結果は参照のみを目的としています。
特定のシナリオのベンチマークを行う必要がある場合は、すべてのシリアル化フレームワークがそのシナリオに合わせて適切に構成されていることを確認してください。
動的シリアル化フレームワークはポリモーフィズムと参照をサポートしますが、Fury のように JIT 技術を利用しない限り、静的シリアル化フレームワークと比較してコストが高くなることがよくあります。正確なベンチマーク統計を確保するには、Fury のランタイム コード生成によるデータを収集する前にシステムをウォームアップすることをお勧めします。
以下の表で、「互換性」を含むタイトルはスキーマ互換モードを表します。タイプの上位/下位互換性が有効です。 「互換性」のないタイトルはスキーマ一貫性モードを表します。クラス スキーマはシリアル化と逆シリアル化の間で同じである必要があります。
Struct
は 100 個のプリミティブ フィールドを持つクラス、 MediaContent
は jvm-serializers のクラス、 Sample
kryo ベンチマークのクラスです。
型の上位/下位互換性、オフヒープ サポート、ゼロコピー シリアル化に関するベンチマークの詳細については、ベンチマークを参照してください。
夜間のスナップショット:
< repositories >
< repository >
< id >apache</ id >
< url >https://repository.apache.org/snapshots/</ url >
< releases >
< enabled >false</ enabled >
</ releases >
< snapshots >
< enabled >true</ enabled >
</ snapshots >
</ repository >
</ repositories >
< dependency >
< groupId >org.apache.fury</ groupId >
< artifactId >fury-core</ artifactId >
< version >0.10.0-SNAPSHOT</ version >
</ dependency >
<!-- row/arrow format support -->
<!-- <dependency>
<groupId>org.apache.fury</groupId>
<artifactId>fury-format</artifactId>
<version>0.10.0-SNAPSHOT</version>
</dependency> -->
リリースバージョン:
< dependency >
< groupId >org.apache.fury</ groupId >
< artifactId >fury-core</ artifactId >
< version >0.9.0</ version >
</ dependency >
<!-- row/arrow format support -->
<!-- <dependency>
<groupId>org.apache.fury</groupId>
<artifactId>fury-format</artifactId>
<version>0.9.0</version>
</dependency> -->
スカラ2:
libraryDependencies += " org.apache.fury " % " fury-scala_2.13 " % " 0.9.0 "
スカラ3:
libraryDependencies += " org.apache.fury " % " fury-scala_3 " % " 0.9.0 "
< dependency >
< groupId >org.apache.fury</ groupId >
< artifactId >fury-kotlin</ artifactId >
< version >0.9.0</ version >
</ dependency >
pip install pyfury
npm install @furyjs/fury
go get github.com/apache/fury/go/fury
ここでは、Fury の使用方法について簡単に説明します。Java、クロスランゲージ、および行形式の詳細については、ユーザー ガイドを参照してください。
言語間の要件がない場合は、このモードを使用するとパフォーマンスが向上します。
import org . apache . fury .*;
import org . apache . fury . config .*;
import java . util .*;
public class Example {
public static void main ( String [] args ) {
SomeClass object = new SomeClass ();
// Note that Fury instances should be reused between
// multiple serializations of different objects.
{
Fury fury = Fury . builder (). withLanguage ( Language . JAVA )
. requireClassRegistration ( true )
. build ();
// Registering types can reduce class name serialization overhead, but not mandatory.
// If class registration enabled, all custom types must be registered.
fury . register ( SomeClass . class );
byte [] bytes = fury . serialize ( object );
System . out . println ( fury . deserialize ( bytes ));
}
{
ThreadSafeFury fury = Fury . builder (). withLanguage ( Language . JAVA )
. requireClassRegistration ( true )
. buildThreadSafeFury ();
// Registering types can reduce class name serialization overhead, but not mandatory.
// If class registration enabled, all custom types must be registered.
fury . register ( SomeClass . class );
byte [] bytes = fury . serialize ( object );
System . out . println ( fury . deserialize ( bytes ));
}
{
ThreadSafeFury fury = new ThreadLocalFury ( classLoader -> {
Fury f = Fury . builder (). withLanguage ( Language . JAVA )
. withClassLoader ( classLoader ). build ();
f . register ( SomeClass . class );
return f ;
});
byte [] bytes = fury . serialize ( object );
System . out . println ( fury . deserialize ( bytes ));
}
}
}
ジャワ
import org . apache . fury .*;
import org . apache . fury . config .*;
import java . util .*;
public class ReferenceExample {
public static class SomeClass {
SomeClass f1 ;
Map < String , String > f2 ;
Map < String , String > f3 ;
}
public static Object createObject () {
SomeClass obj = new SomeClass ();
obj . f1 = obj ;
obj . f2 = ofHashMap ( "k1" , "v1" , "k2" , "v2" );
obj . f3 = obj . f2 ;
return obj ;
}
// mvn exec:java -Dexec.mainClass="org.apache.fury.examples.ReferenceExample"
public static void main ( String [] args ) {
Fury fury = Fury . builder (). withLanguage ( Language . XLANG )
. withRefTracking ( 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 ));
}
}
パイソン
from typing import Dict
import pyfury
class SomeClass :
f1 : "SomeClass"
f2 : Dict [ str , str ]
f3 : Dict [ str , str ]
fury = pyfury . Fury ( ref_tracking = True )
fury . register_class ( SomeClass , type_tag = "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 ))
ゴラン
package main
import furygo "github.com/apache/fury/go/fury"
import "fmt"
func main () {
type SomeClass struct {
F1 * SomeClass
F2 map [ string ] string
F3 map [ string ] string
}
fury := furygo . 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 )
}
public class Bar {
String f1 ;
List < Long > f2 ;
}
public class Foo {
int f1 ;
List < Integer > f2 ;
Map < String , Integer > f3 ;
List < Bar > f4 ;
}
RowEncoder < Foo > encoder = Encoders . bean ( Foo . class );
Foo foo = new Foo ();
foo . f1 = 10 ;
foo . f2 = IntStream . range ( 0 , 1000000 ). boxed (). collect ( Collectors . toList ());
foo . f3 = IntStream . range ( 0 , 1000000 ). boxed (). collect ( Collectors . toMap ( i -> "k" + i , i -> i ));
List < Bar > bars = new ArrayList <>( 1000000 );
for ( int i = 0 ; i < 1000000 ; i ++) {
Bar bar = new Bar ();
bar . f1 = "s" + i ;
bar . f2 = LongStream . range ( 0 , 10 ). boxed (). collect ( Collectors . toList ());
bars . add ( bar );
}
foo . f4 = bars ;
// Can be zero-copy read by python
BinaryRow binaryRow = encoder . toRow ( foo );
// can be data from python
Foo newFoo = encoder . fromRow ( binaryRow );
// zero-copy read List<Integer> f2
BinaryArray binaryArray2 = binaryRow . getArray ( 1 );
// zero-copy read List<Bar> f4
BinaryArray binaryArray4 = binaryRow . getArray ( 3 );
// zero-copy read 11th element of `readList<Bar> f4`
BinaryRow barStruct = binaryArray4 . getStruct ( 10 );
// zero-copy read 6th of f2 of 11th element of `readList<Bar> f4`
barStruct . getArray ( 1 ). getInt64 ( 5 );
RowEncoder < Bar > barEncoder = Encoders . bean ( Bar . class );
// deserialize part of data.
Bar newBar = barEncoder . fromRow ( barStruct );
Bar newBar2 = barEncoder . fromRow ( binaryArray4 . getStruct ( 20 ));
@ dataclass
class Bar :
f1 : str
f2 : List [ pa . int64 ]
@ dataclass
class 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 ()
foo_row = pyfury . RowData ( encoder . schema , binary )
print ( foo_row . f2 [ 100000 ], foo_row . f4 [ 100000 ]. f1 , foo_row . f4 [ 200000 ]. f2 [ 5 ])
Fury Java オブジェクト グラフのシリアル化は、クラス スキーマの前方/後方互換性をサポートします。シリアル化ピアと逆シリアル化ピアは、独立してフィールドを追加/削除できます。
メタ圧縮が完了した後、言語間シリアル化のスキーマ互換性サポートを追加する予定です。
私たちはまだプロトコルを改善しているため、現時点では Fury メジャー リリース間のバイナリ互換性は保証されていません。ただし、マイナー バージョン間では保証されます。将来 Fury をアップグレードする場合は、Fury メジャー バージョンごとにデータをversioning
ください。詳細については、Fury をアップグレードする方法を参照してください。
Fury 1.0 がリリースされると、バイナリ互換性が保証されます。
静的シリアル化は比較的安全です。ただし、Fury java/python ネイティブ シリアル化などの動的シリアル化では、未登録型の逆シリアル化がサポートされており、これにより動的性と柔軟性が向上しますが、セキュリティ リスクも生じます。
たとえば、逆シリアル化によってinit
コンストラクターまたはequals
/ hashCode
メソッドが呼び出される可能性があり、メソッド本体に悪意のあるコードが含まれている場合、システムは危険にさらされます。
Fury は、そのようなプロトコルに対してデフォルトで有効になるクラス登録オプションを提供し、信頼できる登録型または組み込み型の逆シリアル化のみを許可します。環境が安全であることを確認できない限り、クラスの登録を無効にしないでください。
このオプションが無効になっている場合、シリアル化のセキュリティについてはユーザーの責任となります。 ClassResolver#setClassChecker
によってorg.apache.fury.resolver.ClassChecker
を設定して、シリアル化を許可するクラスを制御できます。
Fury で見つかったセキュリティ脆弱性を報告するには、ASF 脆弱性報告プロセスに従ってください。
ビルド方法についてはビルドガイドをお読みください。
貢献方法については、貢献ガイドをお読みください。
Apache License、バージョン 2.0 に基づいてライセンス供与されています