From 11fccd22e4f4ed528ff7b7077ce0f7ac7ee86c46 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Wed, 11 Sep 2024 00:30:34 +0500 Subject: [PATCH] chore: add js benchmarks --- Cargo.toml | 1 + js-benchmarks/.gitignore | 3 + js-benchmarks/flake.lock | 458 ++++++++++++++++++ js-benchmarks/flake.nix | 54 +++ js-benchmarks/package.json | 12 + js-benchmarks/pnpm-lock.yaml | 449 +++++++++++++++++ js-benchmarks/readme.md | 22 + js-benchmarks/schema-proto/bench.proto | 32 ++ js-benchmarks/serdegen-bincode/Cargo.lock | 353 ++++++++++++++ js-benchmarks/serdegen-bincode/Cargo.toml | 9 + js-benchmarks/serdegen-bincode/src/main.rs | 56 +++ .../bincode/bincode/bincodeDeserializer.ts | 23 + .../src/bincode/bincode/bincodeSerializer.ts | 20 + js-benchmarks/src/bincode/bincode/mod.ts | 7 + .../src/bincode/registry/registry.ts | 190 ++++++++ .../src/bincode/serde/binaryDeserializer.ts | 142 ++++++ .../src/bincode/serde/binarySerializer.ts | 173 +++++++ .../src/bincode/serde/deserializer.ts | 53 ++ js-benchmarks/src/bincode/serde/mod.ts | 10 + js-benchmarks/src/bincode/serde/serializer.ts | 52 ++ js-benchmarks/src/bincode/serde/types.ts | 27 ++ js-benchmarks/src/index.ts | 72 +++ js-benchmarks/src/proto/bench.ts | 357 ++++++++++++++ 23 files changed, 2575 insertions(+) create mode 100644 js-benchmarks/.gitignore create mode 100644 js-benchmarks/flake.lock create mode 100644 js-benchmarks/flake.nix create mode 100644 js-benchmarks/package.json create mode 100644 js-benchmarks/pnpm-lock.yaml create mode 100644 js-benchmarks/readme.md create mode 100644 js-benchmarks/schema-proto/bench.proto create mode 100644 js-benchmarks/serdegen-bincode/Cargo.lock create mode 100644 js-benchmarks/serdegen-bincode/Cargo.toml create mode 100644 js-benchmarks/serdegen-bincode/src/main.rs create mode 100644 js-benchmarks/src/bincode/bincode/bincodeDeserializer.ts create mode 100644 js-benchmarks/src/bincode/bincode/bincodeSerializer.ts create mode 100644 js-benchmarks/src/bincode/bincode/mod.ts create mode 100644 js-benchmarks/src/bincode/registry/registry.ts create mode 100644 js-benchmarks/src/bincode/serde/binaryDeserializer.ts create mode 100644 js-benchmarks/src/bincode/serde/binarySerializer.ts create mode 100644 js-benchmarks/src/bincode/serde/deserializer.ts create mode 100644 js-benchmarks/src/bincode/serde/mod.ts create mode 100644 js-benchmarks/src/bincode/serde/serializer.ts create mode 100644 js-benchmarks/src/bincode/serde/types.ts create mode 100644 js-benchmarks/src/index.ts create mode 100644 js-benchmarks/src/proto/bench.ts diff --git a/Cargo.toml b/Cargo.toml index 18b6e7cda..dcc0089fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "serde-generate", "serde-generate-bin", ] +exclude = ["js-benchmarks"] resolver = "2" [profile.release] diff --git a/js-benchmarks/.gitignore b/js-benchmarks/.gitignore new file mode 100644 index 000000000..fc0bac6ee --- /dev/null +++ b/js-benchmarks/.gitignore @@ -0,0 +1,3 @@ +node_modules +target +.devenv \ No newline at end of file diff --git a/js-benchmarks/flake.lock b/js-benchmarks/flake.lock new file mode 100644 index 000000000..b7486745a --- /dev/null +++ b/js-benchmarks/flake.lock @@ -0,0 +1,458 @@ +{ + "nodes": { + "cachix": { + "inputs": { + "devenv": "devenv_2", + "flake-compat": [ + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "pre-commit-hooks": [ + "devenv", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1712055811, + "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", + "owner": "cachix", + "repo": "cachix", + "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat_2", + "nix": "nix_2", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1725964132, + "narHash": "sha256-+IW4z7tXTgkEA677hbT+qsfNxaduCZY+1rAKFgVVmLI=", + "owner": "cachix", + "repo": "devenv", + "rev": "98c7c131e3fa30eb00e9bfe44c1a180c7f94102f", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_2": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "flake-compat" + ], + "nix": "nix", + "nixpkgs": "nixpkgs", + "poetry2nix": "poetry2nix", + "pre-commit-hooks": [ + "devenv", + "cachix", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1708704632, + "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", + "owner": "cachix", + "repo": "devenv", + "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "python-rewrite", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1725234343, + "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nix": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix_2": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression_2" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1692808169, + "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1725233747, + "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_2": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1725910328, + "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692876271, + "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils_2", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1713775815, + "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/js-benchmarks/flake.nix b/js-benchmarks/flake.nix new file mode 100644 index 000000000..dc6641b27 --- /dev/null +++ b/js-benchmarks/flake.nix @@ -0,0 +1,54 @@ +{ + inputs = { + # + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + flake-parts.url = "github:hercules-ci/flake-parts"; + + devenv.url = "github:cachix/devenv"; + devenv.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = {self, ...} @ inputs: + with builtins; let + lib = inputs.nixpkgs.lib; + in + with lib; + inputs.flake-parts.lib.mkFlake { + inherit inputs; + specialArgs = {inherit lib;}; + } + ({moduleWithSystem, ...}: { + imports = with inputs; [devenv.flakeModule]; + systems = ["x86_64-linux"]; + perSystem = { + config, + system, + self', + inputs', + ... + }: let + pkgs = import inputs.nixpkgs { + inherit system; + config.allowUnfree = true; + }; + in { + _module.args = {inherit pkgs;}; + devenv.shells.default = {config, ...} @ devenvArgs: let + inherit (config.devenv) root state profile; + in { + packages = with pkgs; [nodejs nodePackages.pnpm protobuf_28]; + scripts."gen:proto".exec = concatStringsSep " \\\n" [ + "protoc" + "--plugin ${root}/node_modules/.bin/protoc-gen-ts_proto" + "--ts_proto_out src/proto" + "--ts_proto_opt esModuleInterop=true,snakeToCamel=false,forceLong=number,oneof=unions,outputJsonMethods=false,env=browser" + + "--proto_path schema-proto" + "--proto_path ${pkgs.protobuf}/include/google/protobuf/" + ''$(find schema-proto -iname "*.proto")'' + ]; + }; + }; + }); +} diff --git a/js-benchmarks/package.json b/js-benchmarks/package.json new file mode 100644 index 000000000..3be2c9a98 --- /dev/null +++ b/js-benchmarks/package.json @@ -0,0 +1,12 @@ +{ + "name": "js-bin-benchmark", + "type": "module", + "version": "0.0.0", + "dependencies": { + "long": "^5.2.3", + "protobufjs": "^7.4.0", + "tinybench": "^2.9.0", + "ts-proto": "1", + "tsx": "^4.19.0" + } +} diff --git a/js-benchmarks/pnpm-lock.yaml b/js-benchmarks/pnpm-lock.yaml new file mode 100644 index 000000000..fa95728fb --- /dev/null +++ b/js-benchmarks/pnpm-lock.yaml @@ -0,0 +1,449 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + long: + specifier: ^5.2.3 + version: 5.2.3 + protobufjs: + specifier: ^7.4.0 + version: 7.4.0 + tinybench: + specifier: ^2.9.0 + version: 2.9.0 + ts-proto: + specifier: '1' + version: 1.181.2 + tsx: + specifier: ^4.19.0 + version: 4.19.0 + +packages: + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@types/node@22.5.4': + resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==} + + case-anything@2.1.13: + resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==} + engines: {node: '>=12.13'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + dprint-node@1.0.8: + resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==} + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.8.0: + resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} + + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + + protobufjs@7.4.0: + resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} + engines: {node: '>=12.0.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + ts-poet@6.9.0: + resolution: {integrity: sha512-roe6W6MeZmCjRmppyfOURklO5tQFQ6Sg7swURKkwYJvV7dbGCrK28um5+51iW3twdPRKtwarqFAVMU6G1mvnuQ==} + + ts-proto-descriptors@1.16.0: + resolution: {integrity: sha512-3yKuzMLpltdpcyQji1PJZRfoo4OJjNieKTYkQY8pF7xGKsYz/RHe3aEe4KiRxcinoBmnEhmuI+yJTxLb922ULA==} + + ts-proto@1.181.2: + resolution: {integrity: sha512-knJ8dtjn2Pd0c5ZGZG8z9DMiD4PUY8iGI9T9tb8DvGdWRMkLpf0WcPO7G+7cmbZyxvNTAG6ci3fybEaFgMZIvg==} + hasBin: true + + tsx@4.19.0: + resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} + engines: {node: '>=18.0.0'} + hasBin: true + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + +snapshots: + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@types/node@22.5.4': + dependencies: + undici-types: 6.19.8 + + case-anything@2.1.13: {} + + detect-libc@1.0.3: {} + + dprint-node@1.0.8: + dependencies: + detect-libc: 1.0.3 + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.8.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + long@5.2.3: {} + + protobufjs@7.4.0: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.5.4 + long: 5.2.3 + + resolve-pkg-maps@1.0.0: {} + + tinybench@2.9.0: {} + + ts-poet@6.9.0: + dependencies: + dprint-node: 1.0.8 + + ts-proto-descriptors@1.16.0: + dependencies: + long: 5.2.3 + protobufjs: 7.4.0 + + ts-proto@1.181.2: + dependencies: + case-anything: 2.1.13 + protobufjs: 7.4.0 + ts-poet: 6.9.0 + ts-proto-descriptors: 1.16.0 + + tsx@4.19.0: + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.0 + optionalDependencies: + fsevents: 2.3.3 + + undici-types@6.19.8: {} diff --git a/js-benchmarks/readme.md b/js-benchmarks/readme.md new file mode 100644 index 000000000..7a1837c0c --- /dev/null +++ b/js-benchmarks/readme.md @@ -0,0 +1,22 @@ +## JavaScript benchamrks. + +Run: +```sh +nix develop . --impure +pnpm i +./node_modules/.bin/tsx ./src/index.ts +``` + +## Results +``` +┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ +│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ +├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ +│ 0 │ 'JSON:encode' │ '818,455' │ 1221.8140604397472 │ '±0.08%' │ 4092276 │ +│ 1 │ 'protobuf-js-ts-proto:encode' │ '825,741' │ 1211.0328013030787 │ '±0.18%' │ 4128708 │ +│ 2 │ 'serdegen-bincode:encode' │ '154,729' │ 6462.880889137842 │ '±0.50%' │ 773649 │ +│ 3 │ 'JSON:decode' │ '616,082' │ 1623.1602305653864 │ '±0.09%' │ 3080411 │ +│ 4 │ 'protobuf-js-ts-proto:decode' │ '1,235,026' │ 809.6989756493713 │ '±0.04%' │ 6175135 │ +│ 5 │ 'serdegen-bincode:decode' │ '39,897' │ 25064.009654615096 │ '±0.32%' │ 199490 │ +└─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ +``` \ No newline at end of file diff --git a/js-benchmarks/schema-proto/bench.proto b/js-benchmarks/schema-proto/bench.proto new file mode 100644 index 000000000..44f63d9e6 --- /dev/null +++ b/js-benchmarks/schema-proto/bench.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +message Test { + string string = 1; + uint32 uint32 = 2; + Inner inner = 3; + float float = 4; + + message Inner { + int32 int32 = 1; + InnerInner innerInner = 2; + Outer outer = 3; + message InnerInner { + int64 long = 1; + Enum enum = 2; + sint32 sint32 = 3; + } + } + + enum Enum { + ONE = 0; + TWO = 1; + THREE = 2; + FOUR = 3; + FIVE = 4; + } +} + +message Outer { + repeated bool bool = 1; + double double = 2; +} \ No newline at end of file diff --git a/js-benchmarks/serdegen-bincode/Cargo.lock b/js-benchmarks/serdegen-bincode/Cargo.lock new file mode 100644 index 000000000..16ca749fa --- /dev/null +++ b/js-benchmarks/serdegen-bincode/Cargo.lock @@ -0,0 +1,353 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "include_dir" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b56e147e6187d61e9d0f039f10e070d0c0a887e24fe0bb9ca3f29bfde62cab" +dependencies = [ + "glob", + "include_dir_impl", + "proc-macro-hack", +] + +[[package]] +name = "include_dir_impl" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df" +dependencies = [ + "anyhow", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-generate" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8309fc8d475cf5884e92a463b3a003d433684335be19c3f739af0451b027254b" +dependencies = [ + "heck", + "include_dir", + "phf", + "serde", + "serde-reflection", + "textwrap", +] + +[[package]] +name = "serde-reflection" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b6798a64289ff550d8d79847467789a5fd30b42c9c406a4d6dc0bc9b567e55c" +dependencies = [ + "once_cell", + "serde", + "thiserror", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serdegen-bincode" +version = "0.1.0" +dependencies = [ + "serde", + "serde-generate", + "serde-reflection", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd05616119e612a8041ef58f2b578906cc2531a6069047ae092cfb86a325d835" +dependencies = [ + "smawk", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/js-benchmarks/serdegen-bincode/Cargo.toml b/js-benchmarks/serdegen-bincode/Cargo.toml new file mode 100644 index 000000000..f35adbbd1 --- /dev/null +++ b/js-benchmarks/serdegen-bincode/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "serdegen-bincode" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = "1.0.210" +serde-generate = "0.26.0" +serde-reflection = "0.4.0" diff --git a/js-benchmarks/serdegen-bincode/src/main.rs b/js-benchmarks/serdegen-bincode/src/main.rs new file mode 100644 index 000000000..5615b2249 --- /dev/null +++ b/js-benchmarks/serdegen-bincode/src/main.rs @@ -0,0 +1,56 @@ +fn main() { + use serde_reflection::{Registry, Tracer, TracerConfig}; + let mut tracer = Tracer::new(TracerConfig::default()); + + #[derive(serde::Deserialize)] + pub struct Test { + pub string: String, + pub uint32: u32, + pub inner: Inner, + pub float: f32, + } + #[derive(serde::Deserialize)] + pub struct Inner { + pub int32: i32, + pub inner_inner: InnerInner, + pub outer: Outer, + } + #[derive(serde::Deserialize)] + pub struct InnerInner { + pub long: i64, + pub enum_value: Enum, // enum is a reserved keyword in Rust, use enum_value + pub sint32: i32, + } + #[derive(serde::Deserialize)] + pub struct Outer { + pub bools: Vec, // repeated field is represented as a Vec + pub double: f64, + } + #[derive(serde::Deserialize)] + pub enum Enum { + ONE = 0, + TWO = 1, + THREE = 2, + FOUR = 3, + FIVE = 4, + } + + tracer.trace_simple_type::().unwrap(); + tracer.trace_simple_type::().unwrap(); + tracer.trace_simple_type::().unwrap(); + tracer.trace_simple_type::().unwrap(); + tracer.trace_simple_type::().unwrap(); + + let registry = tracer.registry().unwrap(); + + use serde_generate::{ + typescript::{self, CodeGenerator}, + CodeGeneratorConfig, Encoding, + }; + let mut source = Vec::new(); + let config = + CodeGeneratorConfig::new("bincode".to_string()).with_encodings(vec![Encoding::Bincode]); + let generator = typescript::CodeGenerator::new(&config); + generator.output(&mut source, ®istry).unwrap(); + std::fs::write("../src/bincode/registry/registry.ts", source).unwrap(); +} diff --git a/js-benchmarks/src/bincode/bincode/bincodeDeserializer.ts b/js-benchmarks/src/bincode/bincode/bincodeDeserializer.ts new file mode 100644 index 000000000..f8de97ed8 --- /dev/null +++ b/js-benchmarks/src/bincode/bincode/bincodeDeserializer.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ + +import { BinaryDeserializer } from "../serde/binaryDeserializer.ts"; + +export class BincodeDeserializer extends BinaryDeserializer { + deserializeLen(): number { + return Number(this.deserializeU64()); + } + + public deserializeVariantIndex(): number { + return this.deserializeU32(); + } + + checkThatKeySlicesAreIncreasing( + key1: [number, number], + key2: [number, number], + ): void { + return; + } +} diff --git a/js-benchmarks/src/bincode/bincode/bincodeSerializer.ts b/js-benchmarks/src/bincode/bincode/bincodeSerializer.ts new file mode 100644 index 000000000..dce23a19f --- /dev/null +++ b/js-benchmarks/src/bincode/bincode/bincodeSerializer.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ + +import { BinarySerializer } from "../serde/binarySerializer.ts"; + +export class BincodeSerializer extends BinarySerializer { + serializeLen(value: number): void { + this.serializeU64(value); + } + + public serializeVariantIndex(value: number): void { + this.serializeU32(value); + } + + public sortMapEntries(offsets: number[]): void { + return; + } +} diff --git a/js-benchmarks/src/bincode/bincode/mod.ts b/js-benchmarks/src/bincode/bincode/mod.ts new file mode 100644 index 000000000..302340333 --- /dev/null +++ b/js-benchmarks/src/bincode/bincode/mod.ts @@ -0,0 +1,7 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ + +export { BincodeSerializer } from "./bincodeSerializer.ts"; +export { BincodeDeserializer } from "./bincodeDeserializer.ts"; diff --git a/js-benchmarks/src/bincode/registry/registry.ts b/js-benchmarks/src/bincode/registry/registry.ts new file mode 100644 index 000000000..9a98ed007 --- /dev/null +++ b/js-benchmarks/src/bincode/registry/registry.ts @@ -0,0 +1,190 @@ + +import { Serializer, Deserializer } from '../serde/mod.ts'; +import { Seq, bool, int32, int64, uint32, float32, float64, str } from '../serde/mod.ts'; + +export abstract class Enum { + abstract serialize(serializer: Serializer): void; + + static deserialize(deserializer: Deserializer): Enum { + const index = deserializer.deserializeVariantIndex(); + switch (index) { + case 0: return EnumVariantONE.load(deserializer); + case 1: return EnumVariantTWO.load(deserializer); + case 2: return EnumVariantTHREE.load(deserializer); + case 3: return EnumVariantFOUR.load(deserializer); + case 4: return EnumVariantFIVE.load(deserializer); + default: throw new Error("Unknown variant index for Enum: " + index); + } + } +} + + +export class EnumVariantONE extends Enum { + constructor() { + super(); + } + + public serialize(serializer: Serializer): void { + serializer.serializeVariantIndex(0); + } + + static load(deserializer: Deserializer): EnumVariantONE { + return new EnumVariantONE(); + } + +} + +export class EnumVariantTWO extends Enum { + constructor() { + super(); + } + + public serialize(serializer: Serializer): void { + serializer.serializeVariantIndex(1); + } + + static load(deserializer: Deserializer): EnumVariantTWO { + return new EnumVariantTWO(); + } + +} + +export class EnumVariantTHREE extends Enum { + constructor() { + super(); + } + + public serialize(serializer: Serializer): void { + serializer.serializeVariantIndex(2); + } + + static load(deserializer: Deserializer): EnumVariantTHREE { + return new EnumVariantTHREE(); + } + +} + +export class EnumVariantFOUR extends Enum { + constructor() { + super(); + } + + public serialize(serializer: Serializer): void { + serializer.serializeVariantIndex(3); + } + + static load(deserializer: Deserializer): EnumVariantFOUR { + return new EnumVariantFOUR(); + } + +} + +export class EnumVariantFIVE extends Enum { + constructor() { + super(); + } + + public serialize(serializer: Serializer): void { + serializer.serializeVariantIndex(4); + } + + static load(deserializer: Deserializer): EnumVariantFIVE { + return new EnumVariantFIVE(); + } + +} +export class Inner { + + constructor(public int32: int32, public inner_inner: InnerInner, public outer: Outer) { + } + + public serialize(serializer: Serializer): void { + serializer.serializeI32(this.int32); + this.inner_inner.serialize(serializer); + this.outer.serialize(serializer); + } + + static deserialize(deserializer: Deserializer): Inner { + const int32 = deserializer.deserializeI32(); + const inner_inner = InnerInner.deserialize(deserializer); + const outer = Outer.deserialize(deserializer); + return new Inner(int32, inner_inner, outer); + } + +} +export class InnerInner { + + constructor(public long: int64, public enum_value: Enum, public sint32: int32) { + } + + public serialize(serializer: Serializer): void { + serializer.serializeI64(this.long); + this.enum_value.serialize(serializer); + serializer.serializeI32(this.sint32); + } + + static deserialize(deserializer: Deserializer): InnerInner { + const long = deserializer.deserializeI64(); + const enum_value = Enum.deserialize(deserializer); + const sint32 = deserializer.deserializeI32(); + return new InnerInner(long, enum_value, sint32); + } + +} +export class Outer { + + constructor(public bools: Seq, public double: float64) { + } + + public serialize(serializer: Serializer): void { + Helpers.serializeVectorBool(this.bools, serializer); + serializer.serializeF64(this.double); + } + + static deserialize(deserializer: Deserializer): Outer { + const bools = Helpers.deserializeVectorBool(deserializer); + const double = deserializer.deserializeF64(); + return new Outer(bools, double); + } + +} +export class Test { + + constructor(public string: str, public uint32: uint32, public inner: Inner, public float: float32) { + } + + public serialize(serializer: Serializer): void { + serializer.serializeStr(this.string); + serializer.serializeU32(this.uint32); + this.inner.serialize(serializer); + serializer.serializeF32(this.float); + } + + static deserialize(deserializer: Deserializer): Test { + const string = deserializer.deserializeStr(); + const uint32 = deserializer.deserializeU32(); + const inner = Inner.deserialize(deserializer); + const float = deserializer.deserializeF32(); + return new Test(string, uint32, inner, float); + } + +} +export class Helpers { + static serializeVectorBool(value: Seq, serializer: Serializer): void { + serializer.serializeLen(value.length); + value.forEach((item: bool) => { + serializer.serializeBool(item); + }); + } + + static deserializeVectorBool(deserializer: Deserializer): Seq { + const length = deserializer.deserializeLen(); + const list: Seq = []; + for (let i = 0; i < length; i++) { + list.push(deserializer.deserializeBool()); + } + return list; + } + +} + diff --git a/js-benchmarks/src/bincode/serde/binaryDeserializer.ts b/js-benchmarks/src/bincode/serde/binaryDeserializer.ts new file mode 100644 index 000000000..71ada91a9 --- /dev/null +++ b/js-benchmarks/src/bincode/serde/binaryDeserializer.ts @@ -0,0 +1,142 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ + +import { Deserializer } from "./deserializer.ts"; + +export abstract class BinaryDeserializer implements Deserializer { + private static readonly BIG_32: bigint = BigInt(32); + private static readonly BIG_64: bigint = BigInt(64); + private static readonly textDecoder = new TextDecoder(); + public buffer: ArrayBuffer; + public offset: number; + + constructor(data: Uint8Array) { + // copies data to prevent outside mutation of buffer. + this.buffer = new ArrayBuffer(data.length); + new Uint8Array(this.buffer).set(data, 0); + this.offset = 0; + } + + private read(length: number): ArrayBuffer { + const bytes = this.buffer.slice(this.offset, this.offset + length); + this.offset += length; + return bytes; + } + + abstract deserializeLen(): number; + + abstract deserializeVariantIndex(): number; + + abstract checkThatKeySlicesAreIncreasing( + key1: [number, number], + key2: [number, number], + ): void; + + public deserializeStr(): string { + const value = this.deserializeBytes(); + return BinaryDeserializer.textDecoder.decode(value); + } + + public deserializeBytes(): Uint8Array { + const len = this.deserializeLen(); + if (len < 0) { + throw new Error("Length of a bytes array can't be negative"); + } + return new Uint8Array(this.read(len)); + } + + public deserializeBool(): boolean { + const bool = new Uint8Array(this.read(1))[0]; + return bool == 1; + } + + public deserializeUnit(): null { + return null; + } + + public deserializeU8(): number { + return new DataView(this.read(1)).getUint8(0); + } + + public deserializeU16(): number { + return new DataView(this.read(2)).getUint16(0, true); + } + + public deserializeU32(): number { + return new DataView(this.read(4)).getUint32(0, true); + } + + public deserializeU64(): bigint { + const low = this.deserializeU32(); + const high = this.deserializeU32(); + + // combine the two 32-bit values and return (little endian) + return BigInt( + (BigInt(high.toString()) << BinaryDeserializer.BIG_32) | + BigInt(low.toString()), + ); + } + + public deserializeU128(): bigint { + const low = this.deserializeU64(); + const high = this.deserializeU64(); + + // combine the two 64-bit values and return (little endian) + return BigInt( + (BigInt(high.toString()) << BinaryDeserializer.BIG_64) | + BigInt(low.toString()), + ); + } + + public deserializeI8(): number { + return new DataView(this.read(1)).getInt8(0); + } + + public deserializeI16(): number { + return new DataView(this.read(2)).getInt16(0, true); + } + + public deserializeI32(): number { + return new DataView(this.read(4)).getInt32(0, true); + } + + public deserializeI64(): bigint { + const low = this.deserializeI32(); + const high = this.deserializeI32(); + + // combine the two 32-bit values and return (little endian) + return (BigInt(high.toString()) << BinaryDeserializer.BIG_32) | + BigInt(low.toString()); + } + + public deserializeI128(): bigint { + const low = this.deserializeI64(); + const high = this.deserializeI64(); + + // combine the two 64-bit values and return (little endian) + return (BigInt(high.toString()) << BinaryDeserializer.BIG_64) | + BigInt(low.toString()); + } + + public deserializeOptionTag(): boolean { + return this.deserializeBool(); + } + + public getBufferOffset(): number { + return this.offset; + } + + public deserializeChar(): string { + throw new Error("Method deserializeChar not implemented."); + } + + public deserializeF32(): number { + return new DataView(this.read(4)).getFloat32(0, true); + } + + public deserializeF64(): number { + return new DataView(this.read(8)).getFloat64(0, true); + } +} diff --git a/js-benchmarks/src/bincode/serde/binarySerializer.ts b/js-benchmarks/src/bincode/serde/binarySerializer.ts new file mode 100644 index 000000000..3de27bc71 --- /dev/null +++ b/js-benchmarks/src/bincode/serde/binarySerializer.ts @@ -0,0 +1,173 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ + +import { Serializer } from "./serializer.ts"; + +export abstract class BinarySerializer implements Serializer { + private static readonly BIG_32: bigint = BigInt(32); + private static readonly BIG_64: bigint = BigInt(64); + + private static readonly BIG_32Fs: bigint = BigInt("4294967295"); + private static readonly BIG_64Fs: bigint = BigInt("18446744073709551615"); + + private static readonly textEncoder = new TextEncoder(); + + private buffer: ArrayBuffer; + private offset: number; + + constructor() { + this.buffer = new ArrayBuffer(64); + this.offset = 0; + } + + private ensureBufferWillHandleSize(bytes: number) { + while (this.buffer.byteLength < this.offset + bytes) { + const newBuffer = new ArrayBuffer(this.buffer.byteLength * 2); + new Uint8Array(newBuffer).set(new Uint8Array(this.buffer)); + this.buffer = newBuffer; + } + } + + protected serialize(values: Uint8Array) { + this.ensureBufferWillHandleSize(values.length); + new Uint8Array(this.buffer, this.offset).set(values); + this.offset += values.length; + } + + abstract serializeLen(value: number): void; + + abstract serializeVariantIndex(value: number): void; + + abstract sortMapEntries(offsets: number[]): void; + + public serializeStr(value: string): void { + this.serializeBytes(BinarySerializer.textEncoder.encode(value)); + } + + public serializeBytes(value: Uint8Array): void { + this.serializeLen(value.length); + this.serialize(value); + } + + public serializeBool(value: boolean): void { + const byteValue = value ? 1 : 0; + this.serialize(new Uint8Array([byteValue])); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/explicit-module-boundary-types + public serializeUnit(_value: null): void { + return; + } + + private serializeWithFunction( + fn: (byteOffset: number, value: number, littleEndian: boolean) => void, + bytesLength: number, + value: number, + ) { + this.ensureBufferWillHandleSize(bytesLength); + const dv = new DataView(this.buffer, this.offset); + fn.apply(dv, [0, value, true]); + this.offset += bytesLength; + } + + public serializeU8(value: number): void { + this.serialize(new Uint8Array([value])); + } + + public serializeU16(value: number): void { + this.serializeWithFunction(DataView.prototype.setUint16, 2, value); + } + + public serializeU32(value: number): void { + this.serializeWithFunction(DataView.prototype.setUint32, 4, value); + } + + public serializeU64(value: BigInt | number): void { + const low = BigInt(value.toString()) & BinarySerializer.BIG_32Fs; + const high = BigInt(value.toString()) >> BinarySerializer.BIG_32; + + // write little endian number + this.serializeU32(Number(low)); + this.serializeU32(Number(high)); + } + + public serializeU128(value: BigInt | number): void { + const low = BigInt(value.toString()) & BinarySerializer.BIG_64Fs; + const high = BigInt(value.toString()) >> BinarySerializer.BIG_64; + + // write little endian number + this.serializeU64(low); + this.serializeU64(high); + } + + public serializeI8(value: number): void { + const bytes = 1; + this.ensureBufferWillHandleSize(bytes); + new DataView(this.buffer, this.offset).setInt8(0, value); + this.offset += bytes; + } + + public serializeI16(value: number): void { + const bytes = 2; + this.ensureBufferWillHandleSize(bytes); + new DataView(this.buffer, this.offset).setInt16(0, value, true); + this.offset += bytes; + } + + public serializeI32(value: number): void { + const bytes = 4; + this.ensureBufferWillHandleSize(bytes); + new DataView(this.buffer, this.offset).setInt32(0, value, true); + this.offset += bytes; + } + + public serializeI64(value: bigint | number): void { + const low = BigInt(value) & BinarySerializer.BIG_32Fs; + const high = BigInt(value) >> BinarySerializer.BIG_32; + + // write little endian number + this.serializeI32(Number(low)); + this.serializeI32(Number(high)); + } + + public serializeI128(value: bigint | number): void { + const low = BigInt(value) & BinarySerializer.BIG_64Fs; + const high = BigInt(value) >> BinarySerializer.BIG_64; + + // write little endian number + this.serializeI64(low); + this.serializeI64(high); + } + + public serializeOptionTag(value: boolean): void { + this.serializeBool(value); + } + + public getBufferOffset(): number { + return this.offset; + } + + public getBytes(): Uint8Array { + return new Uint8Array(this.buffer).slice(0, this.offset); + } + + public serializeChar(_value: string): void { + throw new Error("Method serializeChar not implemented."); + } + + public serializeF32(value: number): void { + const bytes = 4; + this.ensureBufferWillHandleSize(bytes); + new DataView(this.buffer, this.offset).setFloat32(0, value, true); + this.offset += bytes; + } + + public serializeF64(value: number): void { + const bytes = 8; + this.ensureBufferWillHandleSize(bytes); + new DataView(this.buffer, this.offset).setFloat64(0, value, true); + this.offset += bytes; + } +} diff --git a/js-benchmarks/src/bincode/serde/deserializer.ts b/js-benchmarks/src/bincode/serde/deserializer.ts new file mode 100644 index 000000000..1cd03d094 --- /dev/null +++ b/js-benchmarks/src/bincode/serde/deserializer.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ + +export interface Deserializer { + deserializeStr(): string; + + deserializeBytes(): Uint8Array; + + deserializeBool(): boolean; + + deserializeUnit(): null; + + deserializeChar(): string; + + deserializeF32(): number; + + deserializeF64(): number; + + deserializeU8(): number; + + deserializeU16(): number; + + deserializeU32(): number; + + deserializeU64(): bigint; + + deserializeU128(): bigint; + + deserializeI8(): number; + + deserializeI16(): number; + + deserializeI32(): number; + + deserializeI64(): bigint; + + deserializeI128(): bigint; + + deserializeLen(): number; + + deserializeVariantIndex(): number; + + deserializeOptionTag(): boolean; + + getBufferOffset(): number; + + checkThatKeySlicesAreIncreasing( + key1: [number, number], + key2: [number, number], + ): void; +} diff --git a/js-benchmarks/src/bincode/serde/mod.ts b/js-benchmarks/src/bincode/serde/mod.ts new file mode 100644 index 000000000..4c8a4cf74 --- /dev/null +++ b/js-benchmarks/src/bincode/serde/mod.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ + +export * from "./types.ts"; +export * from "./serializer.ts"; +export * from "./deserializer.ts"; +export * from "./binarySerializer.ts"; +export * from "./binaryDeserializer.ts"; diff --git a/js-benchmarks/src/bincode/serde/serializer.ts b/js-benchmarks/src/bincode/serde/serializer.ts new file mode 100644 index 000000000..361e337a8 --- /dev/null +++ b/js-benchmarks/src/bincode/serde/serializer.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ + +export interface Serializer { + serializeStr(value: string): void; + + serializeBytes(value: Uint8Array): void; + + serializeBool(value: boolean): void; + + serializeUnit(value: null): void; + + serializeChar(value: string): void; + + serializeF32(value: number): void; + + serializeF64(value: number): void; + + serializeU8(value: number): void; + + serializeU16(value: number): void; + + serializeU32(value: number): void; + + serializeU64(value: bigint | number): void; + + serializeU128(value: bigint | number): void; + + serializeI8(value: number): void; + + serializeI16(value: number): void; + + serializeI32(value: number): void; + + serializeI64(value: bigint | number): void; + + serializeI128(value: bigint | number): void; + + serializeLen(value: number): void; + + serializeVariantIndex(value: number): void; + + serializeOptionTag(value: boolean): void; + + getBufferOffset(): number; + + getBytes(): Uint8Array; + + sortMapEntries(offsets: number[]): void; +} diff --git a/js-benchmarks/src/bincode/serde/types.ts b/js-benchmarks/src/bincode/serde/types.ts new file mode 100644 index 000000000..ea1049b27 --- /dev/null +++ b/js-benchmarks/src/bincode/serde/types.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ + +export type Optional = T | null; +export type Seq = T[]; +export type Tuple = T; +export type ListTuple = Tuple[]; + +export type unit = null; +export type bool = boolean; +export type int8 = number; +export type int16 = number; +export type int32 = number; +export type int64 = bigint; +export type int128 = bigint; +export type uint8 = number; +export type uint16 = number; +export type uint32 = number; +export type uint64 = bigint; +export type uint128 = bigint; +export type float32 = number; +export type float64 = number; +export type char = string; +export type str = string; +export type bytes = Uint8Array; diff --git a/js-benchmarks/src/index.ts b/js-benchmarks/src/index.ts new file mode 100644 index 000000000..7f475197f --- /dev/null +++ b/js-benchmarks/src/index.ts @@ -0,0 +1,72 @@ +import { Bench } from 'tinybench' +import * as ProtobufRegistry from './proto/bench.ts' +import * as BincodeRegistry from './bincode/registry/registry.ts' +import { BincodeSerializer } from './bincode/bincode/bincodeSerializer.ts' +import { BincodeDeserializer } from './bincode/bincode/bincodeDeserializer.ts' + +const skeleton = Object.freeze({ + "string": "Lorem ipsum dolor sit amet.", + "uint32": 9000, + "inner": { + "int32": 20161110, + "innerInner": { + "long": 1051, + "enum": 1, + "sint32": -42 + }, + "outer": { + "bool": [true, false, false, true, false, false, true], + "double": 204.8 + } + }, + "float": 0.25 +}) + +const bench = new Bench({ time: 5_000 }) + +bench.add('JSON:encode', () => { + JSON.stringify(skeleton) +}) +bench.add('protobuf-js-ts-proto:encode', () => { + ProtobufRegistry.Test.encode(skeleton).finish() +}) + +const outer = new BincodeRegistry.Outer(skeleton.inner.outer.bool, skeleton.inner.outer.double) +const enu_m = new BincodeRegistry.EnumVariantONE() +const inner_inner = new BincodeRegistry.InnerInner(BigInt(skeleton.inner.innerInner.long), enu_m, skeleton.inner.innerInner.sint32) +const inner = new BincodeRegistry.Inner(skeleton.inner.int32, inner_inner, outer) +bench.add('serdegen-bincode:encode', () => { + const bincode_serializer = new BincodeSerializer() + new BincodeRegistry.Test(skeleton.string, skeleton.uint32, inner, skeleton.float).serialize(bincode_serializer) + bincode_serializer.getBytes() +}) + +const json_encoded = JSON.stringify(skeleton) +bench.add('JSON:decode', () => { + JSON.parse(json_encoded) +}) +const pb_encoded = ProtobufRegistry.Test.encode(skeleton).finish() +bench.add('protobuf-js-ts-proto:decode', () => { + ProtobufRegistry.Test.decode(pb_encoded) +}) + +const bc_encoded = function () { + const outer = new BincodeRegistry.Outer(skeleton.inner.outer.bool, skeleton.inner.outer.double) + const enu_m = new BincodeRegistry.EnumVariantONE() + const inner_inner = new BincodeRegistry.InnerInner(BigInt(skeleton.inner.innerInner.long), enu_m, skeleton.inner.innerInner.sint32) + const inner = new BincodeRegistry.Inner(skeleton.inner.int32, inner_inner, outer) + + const bincode_serializer = new BincodeSerializer() + new BincodeRegistry.Test(skeleton.string, skeleton.uint32, inner, skeleton.float).serialize(bincode_serializer) + return bincode_serializer.getBytes() +}() +bench.add('serdegen-bincode:decode', () => { + const deserializer = new BincodeDeserializer(bc_encoded) + BincodeRegistry.Test.deserialize(deserializer) +}) + + +await bench.warmup() +await bench.run() + +console.table(bench.table()) \ No newline at end of file diff --git a/js-benchmarks/src/proto/bench.ts b/js-benchmarks/src/proto/bench.ts new file mode 100644 index 000000000..1bbcd22af --- /dev/null +++ b/js-benchmarks/src/proto/bench.ts @@ -0,0 +1,357 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v1.181.2 +// protoc v5.28.0 +// source: bench.proto + +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = ""; + +export interface Test { + string: string; + uint32: number; + inner: Test_Inner | undefined; + float: number; +} + +export enum Test_Enum { + ONE = 0, + TWO = 1, + THREE = 2, + FOUR = 3, + FIVE = 4, + UNRECOGNIZED = -1, +} + +export interface Test_Inner { + int32: number; + innerInner: Test_Inner_InnerInner | undefined; + outer: Outer | undefined; +} + +export interface Test_Inner_InnerInner { + long: number; + enum: Test_Enum; + sint32: number; +} + +export interface Outer { + bool: boolean[]; + /** make sure to set something that's fair to JSON */ + double: number; +} + +function createBaseTest(): Test { + return { string: "", uint32: 0, inner: undefined, float: 0 }; +} + +export const Test = { + encode(message: Test, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.string !== "") { + writer.uint32(10).string(message.string); + } + if (message.uint32 !== 0) { + writer.uint32(16).uint32(message.uint32); + } + if (message.inner !== undefined) { + Test_Inner.encode(message.inner, writer.uint32(26).fork()).ldelim(); + } + if (message.float !== 0) { + writer.uint32(37).float(message.float); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Test { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.string = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.uint32 = reader.uint32(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.inner = Test_Inner.decode(reader, reader.uint32()); + continue; + case 4: + if (tag !== 37) { + break; + } + + message.float = reader.float(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): Test { + return Test.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Test { + const message = createBaseTest(); + message.string = object.string ?? ""; + message.uint32 = object.uint32 ?? 0; + message.inner = (object.inner !== undefined && object.inner !== null) + ? Test_Inner.fromPartial(object.inner) + : undefined; + message.float = object.float ?? 0; + return message; + }, +}; + +function createBaseTest_Inner(): Test_Inner { + return { int32: 0, innerInner: undefined, outer: undefined }; +} + +export const Test_Inner = { + encode(message: Test_Inner, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.int32 !== 0) { + writer.uint32(8).int32(message.int32); + } + if (message.innerInner !== undefined) { + Test_Inner_InnerInner.encode(message.innerInner, writer.uint32(18).fork()).ldelim(); + } + if (message.outer !== undefined) { + Outer.encode(message.outer, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Test_Inner { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTest_Inner(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.int32 = reader.int32(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.innerInner = Test_Inner_InnerInner.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.outer = Outer.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): Test_Inner { + return Test_Inner.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Test_Inner { + const message = createBaseTest_Inner(); + message.int32 = object.int32 ?? 0; + message.innerInner = (object.innerInner !== undefined && object.innerInner !== null) + ? Test_Inner_InnerInner.fromPartial(object.innerInner) + : undefined; + message.outer = (object.outer !== undefined && object.outer !== null) ? Outer.fromPartial(object.outer) : undefined; + return message; + }, +}; + +function createBaseTest_Inner_InnerInner(): Test_Inner_InnerInner { + return { long: 0, enum: 0, sint32: 0 }; +} + +export const Test_Inner_InnerInner = { + encode(message: Test_Inner_InnerInner, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.long !== 0) { + writer.uint32(8).int64(message.long); + } + if (message.enum !== 0) { + writer.uint32(16).int32(message.enum); + } + if (message.sint32 !== 0) { + writer.uint32(24).sint32(message.sint32); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Test_Inner_InnerInner { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTest_Inner_InnerInner(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.long = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.enum = reader.int32() as any; + continue; + case 3: + if (tag !== 24) { + break; + } + + message.sint32 = reader.sint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): Test_Inner_InnerInner { + return Test_Inner_InnerInner.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Test_Inner_InnerInner { + const message = createBaseTest_Inner_InnerInner(); + message.long = object.long ?? 0; + message.enum = object.enum ?? 0; + message.sint32 = object.sint32 ?? 0; + return message; + }, +}; + +function createBaseOuter(): Outer { + return { bool: [], double: 0 }; +} + +export const Outer = { + encode(message: Outer, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.bool) { + writer.bool(v); + } + writer.ldelim(); + if (message.double !== 0) { + writer.uint32(17).double(message.double); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Outer { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOuter(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 8) { + message.bool.push(reader.bool()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.bool.push(reader.bool()); + } + + continue; + } + + break; + case 2: + if (tag !== 17) { + break; + } + + message.double = reader.double(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): Outer { + return Outer.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Outer { + const message = createBaseOuter(); + message.bool = object.bool?.map((e) => e) || []; + message.double = object.double ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends { $case: string } ? { [K in keyof Omit]?: DeepPartial } & { $case: T["$case"] } + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + if (long.lt(globalThis.Number.MIN_SAFE_INTEGER)) { + throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER"); + } + return long.toNumber(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +}