Karmem adalah format serialisasi biner cepat. Prioritas karmem adalah mudah digunakan sementara mungkin secepat mungkin. Dioptimalkan untuk mengambil kinerja maksimum Golang dan Tinygo dan efisien untuk bacaan yang dapat diulang, membaca konten yang berbeda dari jenis yang sama. Karmem menunjukkan sepuluh kali lebih cepat dari Google Flatbuffer, dengan tambahan overhead dari pemeriksaan batas termasuk.
️ Karmem masih dalam pengembangan, API tidak stabil. Namun, format serialisasi itu sendiri tidak seperti berubah dan harus tetap kompatibel dengan versi yang lebih lama.
Karmem dibuat untuk memecahkan satu masalah: membuat data yang mudah ditransfer antara host WebAssembly dan tamu. Sementara masih portabel untuk bahasa non-Webassembly. Kami sedang bereksperimen dengan "Pola Peristiwa-Peristiwa" antara Wasm-Host dan Wasm-Guest dalam satu proyek, tetapi berbagi data sangat mahal, dan panggilan FFI juga tidak murah. Karmem mengkodekan sekali dan berbagi konten yang sama dengan banyak tamu, terlepas dari bahasanya, membuatnya sangat efisien. Juga, bahkan menggunakan Object-API untuk memecahkan kode, cukup cepat, dan Karmem dirancang untuk memanfaatkan pola itu, menghindari alokasi, dan menggunakan kembali struct yang sama untuk beberapa data.
Mengapa tidak menggunakan WitX? Ini adalah proyek yang baik dan bertujuan untuk WASM, namun tampaknya lebih kompleks dan mendefinisikan tidak hanya struktur data, tetapi fungsi, yang saya coba hindari. Juga, itu tidak dimaksudkan untuk menjadi portabel bagi non-WASM. Mengapa tidak menggunakan flatbuffer? Kami mencoba, tetapi itu tidak cukup cepat dan juga menyebabkan kepanikan karena kurangnya periksa terikat. Mengapa tidak menggunakan cap'n'proto? Ini adalah alternatif yang baik tetapi tidak memiliki implementasi untuk zig dan perakitan, yang merupakan prioritas tertinggi, ia juga memiliki lebih banyak alokasi dan API yang dihasilkan lebih sulit digunakan, dibandingkan dengan Karmem.
Itu adalah contoh kecil tentang bagaimana penggunaan karmem.
karmem app @ packed ( true ) @ golang . package ( `app` );
enum SocialNetwork uint8 { Unknown ; Facebook ; Instagram ; Twitter ; TikTok ; }
struct ProfileData table {
Network SocialNetwork ;
Username [] char ;
ID uint64 ;
}
struct Profile inline {
Data ProfileData ;
}
struct AccountData table {
ID uint64 ;
Email [] char ;
Profiles [] Profile ;
} Hasilkan kode menggunakan go run karmem.org/cmd/karmem build --golang -o "km" app.km
Untuk menyandikan, penggunaan harus membuat struct asli dan kemudian menyandikannya.
var writerPool = sync. Pool { New : func () any { return karmem . NewWriter ( 1024 ) }}
func main () {
writer := writerPool . Get ().( * karmem. Writer )
content := app. AccountData {
ID : 42 ,
Email : "[email protected]" ,
Profiles : []app. Profile {
{ Data : app. ProfileData {
Network : app . SocialNetworkFacebook ,
Username : "inkeliz" ,
ID : 123 ,
}},
{ Data : app. ProfileData {
Network : app . SocialNetworkFacebook ,
Username : "karmem" ,
ID : 231 ,
}},
{ Data : app. ProfileData {
Network : app . SocialNetworkInstagram ,
Username : "inkeliz" ,
ID : 312 ,
}},
},
}
if _ , err := content . WriteAsRoot ( writer ); err != nil {
panic ( err )
}
encoded := writer . Bytes ()
_ = encoded // Do something with encoded data
writer . Reset ()
writerPool . Put ( writer )
}Alih -alih mendecoding ke struct lain, Anda dapat membaca beberapa bidang secara langsung, tanpa decoding tambahan. Dalam contoh ini, kita hanya membutuhkan nama pengguna setiap profil.
func decodes ( encoded [] byte ) {
reader := karmem . NewReader ( encoded )
account := app . NewAccountDataViewer ( reader , 0 )
profiles := account . Profiles ( reader )
for i := range profiles {
fmt . Println ( profiles [ i ]. Data ( reader ). Username ( reader ))
}
} Perhatikan: Kami menggunakan NewAccountDataViewer , Viewer apa pun hanyalah penampil, dan tidak menyalin data backend. Beberapa bahasa (C#, Assemblyscript) menggunakan UTF-16, sedangkan Karmem menggunakan UTF-8, dalam kasus tersebut Anda memiliki penalti kinerja.
Anda juga dapat memecahkan kode ke struct yang ada. Dalam beberapa kasus, lebih baik jika Anda menggunakan kembali struct yang sama untuk pembacaan kelipatan.
var accountPool = sync. Pool { New : func () any { return new (app. AccountData ) }}
func decodes ( encoded [] byte ) {
account := accountPool . Get ().( * app. AccountData )
account . ReadAsRoot ( karmem . NewReader ( encoded ))
profiles := account . Profiles
for i := range profiles {
fmt . Println ( profiles [ i ]. Data . Username )
}
accountPool . Put ( account )
}Menggunakan skema serupa dengan flatbuffer dan karmem. Karmem hampir 10 kali lebih cepat dari Google Flatbuffer.
Asli (MacOS/ARM64 - M1):
name old time/op new time/op delta
EncodeObjectAPI-8 2.54ms ± 0% 0.51ms ± 0% -79.85% (p=0.008 n=5+5)
DecodeObjectAPI-8 3.57ms ± 0% 0.20ms ± 0% -94.30% (p=0.008 n=5+5)
DecodeSumVec3-8 1.44ms ± 0% 0.16ms ± 0% -88.86% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
EncodeObjectAPI-8 12.1kB ± 0% 0.0kB -100.00% (p=0.008 n=5+5)
DecodeObjectAPI-8 2.87MB ± 0% 0.00MB -100.00% (p=0.008 n=5+5)
DecodeSumVec3-8 0.00B 0.00B ~ (all equal)
name old allocs/op new allocs/op delta
EncodeObjectAPI-8 1.00k ± 0% 0.00k -100.00% (p=0.008 n=5+5)
DecodeObjectAPI-8 110k ± 0% 0k -100.00% (p=0.008 n=5+5)
DecodeSumVec3-8 0.00 0.00 ~ (all equal)
WebAssembly di Wazero (MacOS/ARM64 - M1):
name old time/op new time/op delta
EncodeObjectAPI-8 17.2ms ± 0% 4.0ms ± 0% -76.51% (p=0.008 n=5+5)
DecodeObjectAPI-8 50.7ms ± 2% 1.9ms ± 0% -96.18% (p=0.008 n=5+5)
DecodeSumVec3-8 5.74ms ± 0% 0.75ms ± 0% -86.87% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
EncodeObjectAPI-8 3.28kB ± 0% 3.02kB ± 0% -7.80% (p=0.008 n=5+5)
DecodeObjectAPI-8 3.47MB ± 2% 0.02MB ± 0% -99.56% (p=0.008 n=5+5)
DecodeSumVec3-8 1.25kB ± 0% 1.25kB ± 0% ~ (all equal)
name old allocs/op new allocs/op delta
EncodeObjectAPI-8 4.00 ± 0% 4.00 ± 0% ~ (all equal)
DecodeObjectAPI-8 5.00 ± 0% 4.00 ± 0% -20.00% (p=0.008 n=5+5)
DecodeSumVec3-8 5.00 ± 0% 5.00 ± 0% ~ (all equal)
Kinerja hampir sama ketika membandingkan membaca data non-serial dari struct asli dan membacanya dari data karmem-serialized.
Asli (MacOS/ARM64 - M1):
name old time/op new time/op delta
DecodeSumVec3-8 154µs ± 0% 160µs ± 0% +4.36% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
DecodeSumVec3-8 0.00B 0.00B ~ (all equal)
name old allocs/op new allocs/op delta
DecodeSumVec3-8 0.00 0.00 ~ (all equal)
Itu adalah perbandingan dengan semua bahasa yang didukung.
WebAssembly di Wazero (MacOS/ARM64 - M1):
name time/op result/wasi-go-km.out result/wasi-as-km.out result/wasi-zig-km.out result/wasi-swift-km.out result/wasi-c-km.out result/wasi-odin-km.out result/wasi-dotnet-km.out
DecodeSumVec3-8 757µs ± 0% 1651µs ± 0% 369µs ± 0% 9145µs ± 6% 368µs ± 0% 1330µs ± 0% 75671µs ± 0%
DecodeObjectAPI-8 1.59ms ± 0% 6.13ms ± 0% 1.04ms ± 0% 30.59ms ±34% 0.90ms ± 1% 4.06ms ± 0% 231.72ms ± 0%
EncodeObjectAPI-8 3.96ms ± 0% 4.51ms ± 1% 1.20ms ± 0% 8.26ms ± 0% 1.03ms ± 0% 5.19ms ± 0% 237.99ms ± 0%
name alloc/op result/wasi-go-km.out result/wasi-as-km.out result/wasi-zig-km.out result/wasi-swift-km.out result/wasi-c-km.out result/wasi-odin-km.out result/wasi-dotnet-km.out
DecodeSumVec3-8 1.25kB ± 0% 21.75kB ± 0% 1.25kB ± 0% 1.82kB ± 0% 1.25kB ± 0% 5.34kB ± 0% 321.65kB ± 0%
DecodeObjectAPI-8 15.0kB ± 0% 122.3kB ± 1% 280.8kB ± 1% 108.6kB ± 3% 1.2kB ± 0% 23.8kB ± 0% 386.5kB ± 0%
EncodeObjectAPI-8 3.02kB ± 0% 58.00kB ± 1% 1.23kB ± 0% 1.82kB ± 0% 1.23kB ± 0% 8.91kB ± 0% 375.82kB ± 0%
name allocs/op result/wasi-go-km.out result/wasi-as-km.out result/wasi-zig-km.out result/wasi-swift-km.out result/wasi-c-km.out result/wasi-odin-km.out result/wasi-dotnet-km.out
DecodeSumVec3-8 5.00 ± 0% 5.00 ± 0% 5.00 ± 0% 32.00 ± 0% 5.00 ± 0% 6.00 ± 0% 11.00 ± 0%
DecodeObjectAPI-8 5.00 ± 0% 4.00 ± 0% 4.00 ± 0% 32.00 ± 0% 4.00 ± 0% 6.00 ± 0% 340.00 ± 0%
EncodeObjectAPI-8 4.00 ± 0% 3.00 ± 0% 3.00 ± 0% 30.00 ± 0% 3.00 ± 0% 5.00 ± 0% 40.00 ± 0%
Saat ini, kami memiliki fokus pada WebAssembly, dan karena itu adalah bahasa yang didukung:
Beberapa bahasa masih dalam pengembangan, dan tidak memiliki janji kompatibilitas terbelakang. Kami akan mencoba mengikuti versi terbaru. Saat ini, API yang dihasilkan dan perpustakaan tidak boleh dianggap stabil.
| Fitur | Go/Tinygo | Zig | Assemblyscript | Cepat | C | C#/. Net | Odin |
|---|---|---|---|---|---|---|---|
| Pertunjukan | Bagus | Bagus sekali | Bagus | Miskin | Bagus sekali | Mengerikan | Bagus |
| Prioritas | Tinggi | Tinggi | Tinggi | Rendah | Tinggi | Sedang | Rendah |
| Pengkodean | |||||||
| Pengkodean objek | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| Pengkodean mentah | |||||||
| Nol-salinan | |||||||
| Decoding | |||||||
| Decoding objek | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| Penggunaan kembali objek | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | |
| Akses acak | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| Nol-salinan | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | |
| Zero-copy-string | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ||
| Array asli | ✔️ | ✔️ | ✔️ | ️ | ✔️ |
Karmem menggunakan bahasa skema khusus, yang mendefinisikan struct, enum dan tipe.
Skema ini sangat mudah dipahami dan ditentukan:
karmem game @ packed ( true ) @golang. package ( `km` ) @ assemblyscript . import ( `../../assemblyscript/karmem` );
enum Team uint8 { Humans ; Orcs ; Zombies ; Robots ; Aliens ;}
struct Vec3 inline {
X float32 ;
Y float32 ;
Z float32 ;
}
struct MonsterData table {
Pos Vec3 ;
Mana int16 ;
Health int16 ;
Name [] char ;
Team Team ;
Inventory [ < 128 ] byte ;
Hitbox [ 4 ] float64 ;
Status [] int32 ;
Path [ < 128 ] Vec3 ;
}
struct Monster inline {
Data MonsterData ;
}
struct State table {
Monsters [ < 2000 ] Monster ;
} Setiap file harus dimulai dengan: karmem {name} [@tag()]; . Tag opsional lainnya dapat didefinisikan, seperti yang ditunjukkan di atas, disarankan untuk menggunakan opsi @packed(true) .
Primitif :
uint8 , uint16 , uint32 , uint64int8 , int16 , int32, int64float32 , float64boolbyte , charTidak mungkin untuk mendefinisikan tipe opsional atau tidak dapat dibatalkan.
Array :
[{Length}]{Type} (contoh: [123]uint16 , [3]float32 )[]{Type} (contoh: []char , []uint64 )[<{Length}]{Type} (Contoh: [<512]float64 , [<42]byte )Tidak mungkin memiliki irisan tabel atau irisan enum atau irisan irisan. Namun, dimungkinkan untuk membungkus jenis-jenis itu di dalam satu inline-struktur.
Saat ini, Karmem memiliki dua jenis struct: inline dan tabel.
Inline: Struct inline, seperti namanya, diinline saat digunakan. Itu mengurangi ukuran dan dapat meningkatkan kinerja. Namun, itu tidak dapat mengubah definisi mereka. Dalam urutan kata -kata: Anda tidak dapat mengedit bidang satu struct inline tanpa melanggar kompatibilitas.
struct Vec3 inline {
X float32 ;
Y float32 ;
Z float32 ;
} Struct itu persis sama dari [3]float32 dan akan memiliki hasil serialisasi yang sama. Karena itu, setiap perubahan struct ini (misalnya, ubah menjadi float64 atau menambahkan bidang baru) akan merusak kompatibilitas.
Tabel: Tabel dapat digunakan saat kompatibilitas ke belakang penting. Misalnya, tabel dapat memiliki bidang baru ditambahkan di bagian bawah tanpa melanggar kompatibilitas.
struct User table {
Name [] char ;
Email [] char ;
Password [] char ;
}Mari kita pertimbangkan bahwa Anda membutuhkan bidang lain ... untuk tabel, ini bukan masalah:
struct User table {
Name [] char ;
Email [] char ;
Password [] char ;
Telephone [] char ;
}Karena ini adalah tabel, Anda dapat menambahkan bidang baru di bagian bawah struct, dan kedua versi tersebut kompatibel di antara mereka. Jika pesan dikirim ke klien yang tidak memahami bidang baru, itu akan diabaikan. Jika satu klien yang sudah ketinggalan zaman mengirim pesan ke klien yang lebih baru, bidang yang baru akan memiliki nilai default (0, false, string kosong, dll).
Enum dapat digunakan sebagai jenis alias untuk bilangan bulat, seperti uint8 .
enum Team uint8 {
Unknown ;
Humans ;
Orcs ;
Zombies = 255 ;
}ENUMS harus dimulai dengan nilai nol, nilai default dalam semua kasus. Jika nilai enum apa pun dihilangkan, itu akan menggunakan urutan enum sebagai nilai.
Setelah Anda memiliki skema yang ditentukan, Anda dapat menghasilkan kode. Pertama, Anda perlu diinstal karmem , mendapatkannya dari halaman rilis atau menjalankannya dengan Go.
karmem build --assemblyscript -o "output-folder" your-schema.km
Jika Anda sudah menginstal Golang, Anda dapat menggunakan go karmem.org/cmd/karmem build --zig -o "output-folder" your-schema.km sebagai gantinya.
Perintah:
build
--zig : Aktifkan generasi untuk zig--swift : Aktifkan generasi untuk Swift/Swiftwasm--odin : Aktifkan generasi untuk Odin--golang : Aktifkan Generasi untuk Golang/Tinygo--dotnet : Aktifkan generasi untuk .net--c : Mengaktifkan generasi untuk c--assemblyscript : Aktifkan pembuatan untuk assemblyscript-o <dir> : Menentukan folder output<input-file> : Menentukan skema inputKarmem cepat dan juga bertujuan untuk aman dan stabil untuk penggunaan umum.
Di luar batas
Karmem termasuk pemeriksaan batas untuk mencegah pembacaan di luar batas dan menghindari kecelakaan dan kepanikan. Itu adalah sesuatu yang tidak dimiliki Google Flatbuffer, dan konten cacat akan menyebabkan panik. Namun, itu tidak memperbaiki semua kemungkinan kerentanan.
Kelelahan Sumber Daya
Karmem memungkinkan satu pointer/offset dapat digunakan kembali beberapa kali dalam pesan yang sama. Sayangnya, perilaku itu memungkinkan pesan singkat untuk menghasilkan array yang lebih luas daripada ukuran pesan. Saat ini, satu-satunya mitigasi untuk masalah itu adalah menggunakan array terbatas alih-alih array dan menghindari decode objek-API.
Bocor data
Karmem tidak menghapus memori sebelum pengkodean, yang dapat membocorkan informasi dari pesan sebelumnya atau dari memori sistem itu sendiri. Itu dapat diselesaikan menggunakan tag @packed(true) , seperti yang dijelaskan sebelumnya. Tag packed akan menghapus bantalan dari pesan, yang akan mencegah kebocoran. Atau, Anda dapat menghapus memori sebelum pengkodean, secara manual.
Karma memiliki beberapa keterbatasan dibandingkan dengan perpustakaan serialisasi lainnya, seperti:
Ukuran maksimum
Mirip dengan Google Protobuf dan Google Flatbuffer, Karmem memiliki ukuran maksimum 2GB. Itu adalah ukuran maksimum dari seluruh pesan, bukan ukuran maksimum setiap array. Keterbatasan ini disebabkan oleh fakta bahwa WASM dirancang untuk menjadi 32-bit, dan ukuran maksimum 2GB tampaknya memadai untuk kebutuhan saat ini. Penulis saat ini tidak menegakkan batasan ini, tetapi membaca pesan yang lebih besar dari 2GB akan menyebabkan perilaku yang tidak ditentukan.
Array array/tabel
Karmem tidak mendukung array array atau array tabel. Namun, dimungkinkan untuk membungkus jenis-jenis itu di dalam satu inline-struktur, seperti yang disebutkan di atas. Keterbatasan itu diberlakukan untuk mengambil keuntungan dari array asli/irisan dari bahasa. Sebagian besar bahasa merangkum pointer dan ukuran array di dalam struct-like, yang membutuhkan ukuran setiap elemen yang diketahui, akibatnya mencegah array item dengan ukuran/langkah variabel.
UTF-8
Karmem hanya mendukung UTF-8 dan tidak mendukung penyandian lainnya.