Apache Fury (en incubation) est un framework de sérialisation multilingue incroyablement rapide alimenté par JIT (compilation juste à temps) et zéro copie , offrant des performances jusqu'à 170x et une facilité d'utilisation ultime.
https://fury.apache.org
Important
Apache Fury (incubation) est un effort en cours d'incubation à l'Apache Software Foundation (ASF), parrainé par Apache Incubator PMC.
Veuillez lire l'AVIS DE NON-RESPONSABILITÉ et une explication complète de « l'incubation ».
En plus de la sérialisation multilingue, Fury propose également :
writeObject
/ readObject
/ writeReplace
/ readResolve
/ readObjectNoData
/ Externalizable
.record
Java 17+ est également pris en charge.Fury a conçu et implémenté plusieurs protocoles binaires pour différents scénarios :
De nouveaux protocoles peuvent être facilement ajoutés en fonction du tampon, de l'encodage, de la méta, du codegen et d'autres capacités existants de Fury. Tous partagent la même base de code et l’optimisation d’un protocole peut être réutilisée par un autre protocole.
Différents cadres de sérialisation conviennent à différents scénarios, et les résultats de référence ici sont uniquement à titre de référence.
Si vous devez effectuer une analyse comparative pour votre scénario spécifique, assurez-vous que tous les cadres de sérialisation sont correctement configurés pour ce scénario.
Les frameworks de sérialisation dynamique prennent en charge le polymorphisme et les références, mais ils ont souvent un coût plus élevé que les frameworks de sérialisation statique, à moins qu'ils n'utilisent des techniques JIT comme le fait Fury. Pour garantir des statistiques de référence précises, il est conseillé de réchauffer le système avant de collecter des données en raison de la génération de code d'exécution de Fury.
Dans les tableaux ci-dessous, les titres contenant « compatible » représentent le mode compatible avec le schéma : la compatibilité ascendante/ascendante du type est activée ; tandis que les titres sans « compatible » représentent le mode de cohérence du schéma : le schéma de classe doit être le même entre la sérialisation et la désérialisation.
Où Struct
est une classe avec 100 champs primitifs, MediaContent
est une classe de jvm-serializers et Sample
est une classe du benchmark kryo.
Consultez les tests de référence pour plus de tests sur la compatibilité ascendante/rétroverse des types, la prise en charge hors tas et la sérialisation sans copie.
Instantané nocturne :
< 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> -->
Version de sortie :
< 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> -->
Scala2 :
libraryDependencies += " org.apache.fury " % " fury-scala_2.13 " % " 0.9.0 "
Scala3 :
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
Nous donnons ici un aperçu rapide de l'utilisation de Fury. Consultez le guide de l'utilisateur pour plus de détails sur Java, le multilingue et le format de ligne.
Si vous n'avez pas d'exigences multilingues, l'utilisation de ce mode entraînera de meilleures performances.
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 ));
}
}
}
Java
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 ));
}
}
Python
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 ))
Golang
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 ])
La sérialisation du graphique d'objet Java Fury prend en charge la compatibilité ascendante/rétroverse des schémas de classe. L'homologue de sérialisation et l'homologue de désérialisation peuvent ajouter/supprimer des champs indépendamment.
Nous prévoyons d'ajouter la prise en charge de la compatibilité des schémas pour la sérialisation multilingue une fois la méta-compression terminée.
Nous améliorons toujours nos protocoles, la compatibilité binaire n'est donc pas garantie entre les versions majeures de Fury pour le moment. Cependant, il est garanti entre versions mineures. Veuillez versioning
vos données par version majeure de Fury si vous souhaitez mettre à niveau Fury à l'avenir, consultez comment mettre à niveau Fury pour plus de détails.
La compatibilité binaire sera garantie lors de la sortie de Fury 1.0.
La sérialisation statique est relativement sécurisée. Mais la sérialisation dynamique telle que la sérialisation native Fury java/python prend en charge la désérialisation des types non enregistrés, ce qui offre plus de dynamique et de flexibilité, mais introduit également des risques de sécurité.
Par exemple, la désérialisation peut invoquer le constructeur init
ou la méthode equals
/ hashCode
, si le corps de la méthode contient du code malveillant, le système sera en danger.
Fury fournit une option d'enregistrement de classe qui est activée par défaut pour ces protocoles, permettant uniquement la désérialisation des types enregistrés de confiance ou des types intégrés. Ne désactivez pas l'inscription aux cours à moins que vous puissiez vous assurer que votre environnement est sécurisé .
Si cette option est désactivée, vous êtes responsable de la sécurité de la sérialisation. Vous pouvez configurer org.apache.fury.resolver.ClassChecker
par ClassResolver#setClassChecker
pour contrôler quelles classes sont autorisées pour la sérialisation.
Pour signaler les vulnérabilités de sécurité trouvées dans Fury, veuillez suivre le processus de rapport de vulnérabilité ASF.
Veuillez lire le guide BUILD pour obtenir des instructions sur la façon de construire.
Veuillez lire le guide CONTRIBUTION pour savoir comment contribuer.
Sous licence Apache, version 2.0