feat(serializer): add class aot serialization support
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { Reader, Writer, deserialize, serialize } from '../../plugin/index.ts';
|
||||
import { Reader, Writer, router } from '../../plugin/index.ts';
|
||||
import {
|
||||
Book,
|
||||
Order,
|
||||
Ticker,
|
||||
buildBook,
|
||||
buildOrder,
|
||||
buildTicker,
|
||||
registerAll,
|
||||
} from './payloads.ts';
|
||||
|
||||
const codecs = registerAll();
|
||||
|
||||
const ticker = buildTicker();
|
||||
const order = buildOrder();
|
||||
const book = buildBook(1000);
|
||||
@@ -23,11 +23,11 @@ const tickerJSON = JSON.stringify(ticker);
|
||||
const orderJSON = JSON.stringify(order);
|
||||
const bookJSON = JSON.stringify(book);
|
||||
|
||||
const tickerBin = serialize(ticker, codecs.ticker);
|
||||
const orderBin = serialize(order, codecs.order);
|
||||
const bookBin = serialize(book, codecs.book);
|
||||
const tickerBin = Ticker.encode(ticker);
|
||||
const orderBin = Order.encode(order);
|
||||
const bookBin = Book.encode(book);
|
||||
|
||||
// One-time payload-size print on module load so it appears once in bench output.
|
||||
// One-time payload-size print on module load.
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
'\n--- payload sizes ---\n' +
|
||||
@@ -40,9 +40,9 @@ describe('encode ticker (5 fields)', () => {
|
||||
bench('JSON.stringify', () => {
|
||||
JSON.stringify(ticker);
|
||||
});
|
||||
bench('codec.encode (pooled)', () => {
|
||||
bench('codec.encodeInto (pooled)', () => {
|
||||
wTicker.reset();
|
||||
codecs.ticker.encode(wTicker, ticker);
|
||||
Ticker.encodeInto(ticker, wTicker);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,9 +50,9 @@ describe('encode order (10 fields + bitset)', () => {
|
||||
bench('JSON.stringify', () => {
|
||||
JSON.stringify(order);
|
||||
});
|
||||
bench('codec.encode (pooled)', () => {
|
||||
bench('codec.encodeInto (pooled)', () => {
|
||||
wOrder.reset();
|
||||
codecs.order.encode(wOrder, order);
|
||||
Order.encodeInto(order, wOrder);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,9 +60,9 @@ describe('encode book (1000 levels)', () => {
|
||||
bench('JSON.stringify', () => {
|
||||
JSON.stringify(book);
|
||||
});
|
||||
bench('codec.encode (pooled)', () => {
|
||||
bench('codec.encodeInto (pooled)', () => {
|
||||
wBook.reset();
|
||||
codecs.book.encode(wBook, book);
|
||||
Book.encodeInto(book, wBook);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,10 +70,9 @@ describe('decode ticker', () => {
|
||||
bench('JSON.parse', () => {
|
||||
JSON.parse(tickerJSON);
|
||||
});
|
||||
bench('codec.decode', () => {
|
||||
bench('codec.decodeFrom', () => {
|
||||
const r = new Reader(tickerBin);
|
||||
r.pos = 2;
|
||||
codecs.ticker.decode(r);
|
||||
Ticker.decodeFrom(r);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,10 +80,9 @@ describe('decode order', () => {
|
||||
bench('JSON.parse', () => {
|
||||
JSON.parse(orderJSON);
|
||||
});
|
||||
bench('codec.decode', () => {
|
||||
bench('codec.decodeFrom', () => {
|
||||
const r = new Reader(orderBin);
|
||||
r.pos = 2;
|
||||
codecs.order.decode(r);
|
||||
Order.decodeFrom(r);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -92,10 +90,9 @@ describe('decode book (1000 levels)', () => {
|
||||
bench('JSON.parse', () => {
|
||||
JSON.parse(bookJSON);
|
||||
});
|
||||
bench('codec.decode', () => {
|
||||
bench('codec.decodeFrom', () => {
|
||||
const r = new Reader(bookBin);
|
||||
r.pos = 2;
|
||||
codecs.book.decode(r);
|
||||
Book.decodeFrom(r);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,17 +102,18 @@ describe('roundtrip ticker', () => {
|
||||
});
|
||||
bench('codec (pooled)', () => {
|
||||
wTicker.reset();
|
||||
codecs.ticker.encode(wTicker, ticker);
|
||||
Ticker.encodeInto(ticker, wTicker);
|
||||
const r = new Reader(wTicker.bytes());
|
||||
codecs.ticker.decode(r);
|
||||
Ticker.decodeFrom(r);
|
||||
});
|
||||
});
|
||||
|
||||
describe('serialize+deserialize ticker (with frame)', () => {
|
||||
describe('framed ticker via router', () => {
|
||||
const proto = router(Ticker, Order, Book);
|
||||
bench('JSON', () => {
|
||||
JSON.parse(JSON.stringify(ticker));
|
||||
});
|
||||
bench('serialize/deserialize (framed)', () => {
|
||||
deserialize(serialize(ticker, codecs.ticker));
|
||||
bench('router encode + decode (framed)', () => {
|
||||
proto.decode(proto.encode(ticker, Ticker));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,73 +1,50 @@
|
||||
import { defineSchema, register, s } from '../../plugin/index.ts';
|
||||
import type { Codec } from '../../plugin/index.ts';
|
||||
import {
|
||||
type,
|
||||
enumOf,
|
||||
f64,
|
||||
flags,
|
||||
list,
|
||||
str,
|
||||
u53,
|
||||
type TypeCodec,
|
||||
} from '../../plugin/index.ts';
|
||||
|
||||
export const TickerSchema = defineSchema('BenchTicker', (s) => ({
|
||||
symbol: s.str,
|
||||
last: s.f64,
|
||||
bid: s.f64,
|
||||
ask: s.f64,
|
||||
volume: s.f64,
|
||||
}));
|
||||
export const Ticker = type('BenchTicker', {
|
||||
symbol: str,
|
||||
last: f64,
|
||||
bid: f64,
|
||||
ask: f64,
|
||||
volume: f64,
|
||||
});
|
||||
|
||||
export const OrderSchema = defineSchema('BenchOrder', (s) => ({
|
||||
id: s.u53,
|
||||
account: s.u53,
|
||||
symbol: s.str,
|
||||
side: s.enum(['buy', 'sell'] as const),
|
||||
type: s.enum(['limit', 'market', 'stop', 'stop_limit'] as const),
|
||||
price: s.f64,
|
||||
qty: s.f64,
|
||||
filledQty: s.f64,
|
||||
ts: s.u53,
|
||||
flags: s.bitset(['ioc', 'post_only', 'reduce_only'] as const),
|
||||
}));
|
||||
export const Order = type('BenchOrder', {
|
||||
id: u53,
|
||||
account: u53,
|
||||
symbol: str,
|
||||
side: enumOf(['buy', 'sell'] as const),
|
||||
type: enumOf(['limit', 'market', 'stop', 'stop_limit'] as const),
|
||||
price: f64,
|
||||
qty: f64,
|
||||
filledQty: f64,
|
||||
ts: u53,
|
||||
flags: flags(['ioc', 'post_only', 'reduce_only'] as const),
|
||||
});
|
||||
|
||||
export const LevelSchema = defineSchema('BenchLevel', (s) => ({
|
||||
p: s.f64,
|
||||
q: s.f64,
|
||||
}));
|
||||
export const Level = type('BenchLevel', { p: f64, q: f64 });
|
||||
|
||||
export const BookSchema = defineSchema('BenchBook', (s) => ({
|
||||
symbol: s.str,
|
||||
ts: s.u53,
|
||||
bids: s.array(LevelSchema),
|
||||
asks: s.array(LevelSchema),
|
||||
}));
|
||||
export const Book = type('BenchBook', {
|
||||
symbol: str,
|
||||
ts: u53,
|
||||
bids: list(Level),
|
||||
asks: list(Level),
|
||||
});
|
||||
|
||||
export interface Ticker {
|
||||
symbol: string;
|
||||
last: number;
|
||||
bid: number;
|
||||
ask: number;
|
||||
volume: number;
|
||||
}
|
||||
export type TickerT = typeof Ticker.$infer;
|
||||
export type OrderT = typeof Order.$infer;
|
||||
export type LevelT = typeof Level.$infer;
|
||||
export type BookT = typeof Book.$infer;
|
||||
|
||||
export interface Order {
|
||||
id: number;
|
||||
account: number;
|
||||
symbol: string;
|
||||
side: 'buy' | 'sell';
|
||||
type: 'limit' | 'market' | 'stop' | 'stop_limit';
|
||||
price: number;
|
||||
qty: number;
|
||||
filledQty: number;
|
||||
ts: number;
|
||||
flags: { ioc: boolean; post_only: boolean; reduce_only: boolean };
|
||||
}
|
||||
|
||||
export interface Level {
|
||||
p: number;
|
||||
q: number;
|
||||
}
|
||||
|
||||
export interface Book {
|
||||
symbol: string;
|
||||
ts: number;
|
||||
bids: Level[];
|
||||
asks: Level[];
|
||||
}
|
||||
|
||||
export function buildTicker(): Ticker {
|
||||
export function buildTicker(): TickerT {
|
||||
return {
|
||||
symbol: 'BTC-USD',
|
||||
last: 67891.23,
|
||||
@@ -77,7 +54,7 @@ export function buildTicker(): Ticker {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildOrder(): Order {
|
||||
export function buildOrder(): OrderT {
|
||||
return {
|
||||
id: 9876543210,
|
||||
account: 12345678,
|
||||
@@ -92,9 +69,9 @@ export function buildOrder(): Order {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildBook(depth: number): Book {
|
||||
const bids: Level[] = new Array(depth);
|
||||
const asks: Level[] = new Array(depth);
|
||||
export function buildBook(depth: number): BookT {
|
||||
const bids: LevelT[] = new Array(depth);
|
||||
const asks: LevelT[] = new Array(depth);
|
||||
for (let i = 0; i < depth; i++) {
|
||||
bids[i] = { p: 67890 - i * 0.5, q: 0.1 + (i % 100) * 0.01 };
|
||||
asks[i] = { p: 67891 + i * 0.5, q: 0.1 + (i % 100) * 0.01 };
|
||||
@@ -102,17 +79,9 @@ export function buildBook(depth: number): Book {
|
||||
return { symbol: 'BTC-USD', ts: 1716100000123, bids, asks };
|
||||
}
|
||||
|
||||
export interface Codecs {
|
||||
ticker: Codec<Ticker>;
|
||||
order: Codec<Order>;
|
||||
level: Codec<Level>;
|
||||
book: Codec<Book>;
|
||||
}
|
||||
|
||||
export function registerAll(): Codecs {
|
||||
const ticker = register<Ticker>(TickerSchema);
|
||||
const order = register<Order>(OrderSchema);
|
||||
const level = register<Level>(LevelSchema);
|
||||
const book = register<Book>(BookSchema);
|
||||
return { ticker, order, level, book };
|
||||
}
|
||||
export type AllCodecs = {
|
||||
ticker: TypeCodec<TickerT>;
|
||||
order: TypeCodec<OrderT>;
|
||||
level: TypeCodec<LevelT>;
|
||||
book: TypeCodec<BookT>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user