Skip to content

Commit

Permalink
speed up BookSide decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
mschneider committed May 16, 2024
1 parent b855221 commit dd88b29
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 14 deletions.
2 changes: 1 addition & 1 deletion 3rdparty/anchor
Submodule anchor updated 302 files
74 changes: 62 additions & 12 deletions ts/client/src/accounts/bookSide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ import {
LeafNode,
InnerNode,
U64_MAX_BN,
AnyNode,
} from '..';
import { BN } from '@coral-xyz/anchor';
import { Order } from '../structs/order';

function decodeOrderTreeRootStruct(data: Buffer) {
const maybeNode = data.readUInt32LE(0);
const leafCount = data.readUInt32LE(4);
return { maybeNode, leafCount };
}

export class BookSide {
public clusterTime: BN;

Expand All @@ -24,6 +31,40 @@ export class BookSide {
this.clusterTime = new BN(0);
}

public static decodeAccountfromBuffer(data: Buffer): BookSideAccount {
// TODO: add discriminator parsing & check
const roots = [
decodeOrderTreeRootStruct(data.subarray(8)),
decodeOrderTreeRootStruct(data.subarray(16))];

// skip reserved
let offset = 56+256;

const orderTreeType = data.readUInt8(offset);
const bumpIndex = data.readUInt32LE(offset+4);
const freeListLen = data.readUInt32LE(offset+8);
const freeListHead = data.readUInt32LE(offset+12);

// skip more reserved data
offset += 16 + 512;

const nodes: any[] = [];
for (let i = 0; i < 1024; ++i) {
const tag = data.readUInt8(offset);
const nodeData = data.subarray(offset, offset+88);
nodes.push({tag, nodeData});
offset += 88;
}

// this result has a slightly different layout than the regular account
// it doesn't include reserved data and it's AnyNodes don't have the field
// data: number[] (excluding the tag prefix byte)
// but nodeData: Buffer (including the tag prefix byte)
const result = { roots, nodes: { orderTreeType, bumpIndex, freeListLen, freeListHead, nodes }};

return result as any;
}

public *items(): Generator<Order> {
const fGen = this.fixedItems();
const oPegGen = this.oraclePeggedItems();
Expand Down Expand Up @@ -71,10 +112,10 @@ export class BookSide {
const index = stack.pop()!;
const node = this.account.nodes.nodes[index];
if (node.tag === BookSide.INNER_NODE_TAG) {
const innerNode = this.toInnerNode(node.data);
const innerNode = this.toInnerNode(node);
stack.push(innerNode.children[right], innerNode.children[left]);
} else if (node.tag === BookSide.LEAF_NODE_TAG) {
const leafNode = this.toLeafNode(node.data);
const leafNode = this.toLeafNode(node);
const expiryTimestamp = leafNode.timeInForce
? leafNode.timestamp.add(new BN(leafNode.timeInForce))
: U64_MAX_BN;
Expand All @@ -100,10 +141,10 @@ export class BookSide {
const index = stack.pop()!;
const node = this.account.nodes.nodes[index];
if (node.tag === BookSide.INNER_NODE_TAG) {
const innerNode = this.toInnerNode(node.data);
const innerNode = this.toInnerNode(node);
stack.push(innerNode.children[right], innerNode.children[left]);
} else if (node.tag === BookSide.LEAF_NODE_TAG) {
const leafNode = this.toLeafNode(node.data);
const leafNode = this.toLeafNode(node);
const expiryTimestamp = leafNode.timeInForce
? leafNode.timestamp.add(new BN(leafNode.timeInForce))
: U64_MAX_BN;
Expand Down Expand Up @@ -153,14 +194,23 @@ export class BookSide {
private static INNER_NODE_TAG = 1;
private static LEAF_NODE_TAG = 2;

private toInnerNode(data: number[]): InnerNode {
return (this.market.client.program as any)._coder.types.typeLayouts
.get('InnerNode')
.decode(Buffer.from([BookSide.INNER_NODE_TAG].concat(data)));
private toInnerNode(node: AnyNode): InnerNode {
const layout = (this.market.client.program as any)._coder.types.typeLayouts.get('InnerNode');
// need to differentiate between accounts loaded via anchor and decodeAccountfromBuffer
if ( 'nodeData' in node) {
return layout.decode(node['nodeData']);
} else {
return layout.decode(Buffer.from([BookSide.INNER_NODE_TAG].concat(node.data)));
}
}
private toLeafNode(data: number[]): LeafNode {
return (this.market.client.program as any)._coder.types.typeLayouts
.get('LeafNode')
.decode(Buffer.from([BookSide.LEAF_NODE_TAG].concat(data)));

private toLeafNode(node: AnyNode): LeafNode {
const layout = (this.market.client.program as any)._coder.types.typeLayouts.get('LeafNode');
// need to differentiate between accounts loaded via anchor and decodeAccountfromBuffer
if ( 'nodeData' in node) {
return layout.decode(node['nodeData']);
} else {
return layout.decode(Buffer.from([BookSide.LEAF_NODE_TAG].concat(node.data)));
}
}
}
27 changes: 26 additions & 1 deletion ts/client/src/test/market.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
findAllMarkets,
Watcher,
sleep,
BookSide,
} from '..';
import { initReadOnlyOpenbookClient } from './util';

Expand Down Expand Up @@ -42,6 +43,29 @@ async function testDecodeMarket(): Promise<void> {
console.log(market.toPrettyString());
}


async function benchDecodeMarket(): Promise<void> {
const client = initReadOnlyOpenbookClient();
const marketPk = new PublicKey(
'CFSMrBssNG8Ud1edW59jNLnq2cwrQ9uY5cM3wXmqRJj3',
);
const market = await Market.load(client, marketPk);
await market.loadOrderBook();
await market.loadEventHeap();

const bookSideAccount = await client.connection.getAccountInfo(market.bids!.pubkey);

const start = new Date();
for (let i = 0; i < 10000; ++i) {
const side = client.program.coder.accounts.decode("bookSide", bookSideAccount!.data);
market.bids!.account = side;
market.bids!.getL2(16);
}
const end = new Date();
console.log({start, end, duration: end.valueOf() - start.valueOf()});
console.log();
};

async function testWatchMarket(): Promise<void> {
const client = initReadOnlyOpenbookClient();
const marketPk = new PublicKey(
Expand Down Expand Up @@ -103,4 +127,5 @@ async function testMarketLots(): Promise<void> {
// void testFindAllMarkets();
// void testDecodeMarket();
// void testWatchMarket();
void testMarketLots();
// void testMarketLots();
void benchDecodeMarket();

0 comments on commit dd88b29

Please sign in to comment.