diff --git a/packages/store-sync/package.json b/packages/store-sync/package.json index 574edf5360..0e334e92e3 100644 --- a/packages/store-sync/package.json +++ b/packages/store-sync/package.json @@ -48,6 +48,7 @@ } }, "scripts": { + "bench": "vitest bench", "build": "pnpm run build:js", "build:js": "tsup", "clean": "pnpm run clean:js", diff --git a/packages/store-sync/src/getRecord.bench.ts b/packages/store-sync/src/getRecord.bench.ts new file mode 100644 index 0000000000..34ad3a9f38 --- /dev/null +++ b/packages/store-sync/src/getRecord.bench.ts @@ -0,0 +1,26 @@ +import { bench, describe } from "vitest"; +import { getComponentValue } from "@latticexyz/recs"; +import { blocks } from "../test/blocks"; +import { + components, + recsStorageAdapter, + sqliteStorageAdapter, + tables, + useStore, + zustandStorageAdapter, +} from "../test/utils"; +import { singletonEntity } from "./recs"; + +for (const block of blocks) { + await Promise.all([recsStorageAdapter(block), zustandStorageAdapter(block), sqliteStorageAdapter(block)]); +} + +describe("Get single record by key", () => { + bench("recs: `getComponentValue`", async () => { + getComponentValue(components.NumberList, singletonEntity); + }); + + bench("zustand: `getRecord`", async () => { + useStore.getState().getRecord(tables.NumberList, {}); + }); +}); diff --git a/packages/store-sync/src/getRecords.bench.ts b/packages/store-sync/src/getRecords.bench.ts new file mode 100644 index 0000000000..4861208706 --- /dev/null +++ b/packages/store-sync/src/getRecords.bench.ts @@ -0,0 +1,34 @@ +import { bench, describe } from "vitest"; +import { getComponentEntities, getComponentValue } from "@latticexyz/recs"; +import { eq } from "drizzle-orm"; +import { mudStoreTables } from "./sqlite"; +import { blocks } from "../test/blocks"; +import { + components, + db, + recsStorageAdapter, + sqliteStorageAdapter, + tables, + useStore, + zustandStorageAdapter, +} from "../test/utils"; + +for (const block of blocks) { + await Promise.all([recsStorageAdapter(block), zustandStorageAdapter(block), sqliteStorageAdapter(block)]); +} + +describe("Get all records for table", () => { + bench("recs: `getComponentValue`", async () => { + for (const entity of getComponentEntities(components.NumberList)) { + getComponentValue(components.NumberList, entity); + } + }); + + bench("zustand: `getRecords`", async () => { + useStore.getState().getRecords(tables.NumberList); + }); + + bench("sqlite: `select`", async () => { + db.select().from(mudStoreTables).where(eq(mudStoreTables.name, "NumberList")).all(); + }); +}); diff --git a/packages/store-sync/src/recs/recsStorage.test.ts b/packages/store-sync/src/recs/recsStorage.test.ts index 7a19c9cbaf..afead84f6b 100644 --- a/packages/store-sync/src/recs/recsStorage.test.ts +++ b/packages/store-sync/src/recs/recsStorage.test.ts @@ -58,5 +58,21 @@ describe("recsStorage", () => { ], } `); + + expect( + [...getComponentEntities(components.NumberList)].map((entity) => getComponentValue(components.NumberList, entity)) + ).toMatchInlineSnapshot(` + [ + { + "__dynamicData": "0x000001a400000045", + "__encodedLengths": "0x0000000000000000000000000000000000000000000000000800000000000008", + "__staticData": undefined, + "value": [ + 420, + 69, + ], + }, + ] + `); }); }); diff --git a/packages/store-sync/src/storageAdapter.bench.ts b/packages/store-sync/src/storageAdapter.bench.ts new file mode 100644 index 0000000000..b5cd271547 --- /dev/null +++ b/packages/store-sync/src/storageAdapter.bench.ts @@ -0,0 +1,24 @@ +import { bench, describe } from "vitest"; + +import { blocks } from "../test/blocks"; +import { recsStorageAdapter, sqliteStorageAdapter, zustandStorageAdapter } from "../test/utils"; + +describe("Storage Adapter", () => { + bench("recs: `storageAdapter`", async () => { + for (const block of blocks) { + await recsStorageAdapter(block); + } + }); + + bench("zustand: `storageAdapter`", async () => { + for (const block of blocks) { + await zustandStorageAdapter(block); + } + }); + + bench("sqlite: `storageAdapter`", async () => { + for (const block of blocks) { + await sqliteStorageAdapter(block); + } + }); +}); diff --git a/packages/store-sync/test/blocks.ts b/packages/store-sync/test/blocks.ts new file mode 100644 index 0000000000..402985280c --- /dev/null +++ b/packages/store-sync/test/blocks.ts @@ -0,0 +1,18 @@ +import { groupLogsByBlockNumber } from "@latticexyz/block-logs-stream"; +import { storeEventsAbi } from "@latticexyz/store"; +import { RpcLog, formatLog, decodeEventLog, Hex } from "viem"; +import worldRpcLogs from "../../../test-data/world-logs.json"; +import { StoreEventsLog } from "../src/common"; + +// TODO: make test-data a proper package and export this +export const blocks = groupLogsByBlockNumber( + worldRpcLogs.map((log) => { + const { eventName, args } = decodeEventLog({ + abi: storeEventsAbi, + data: log.data as Hex, + topics: log.topics as [Hex, ...Hex[]], + strict: true, + }); + return formatLog(log as any as RpcLog, { args, eventName: eventName as string }) as StoreEventsLog; + }) +); diff --git a/packages/store-sync/test/utils.ts b/packages/store-sync/test/utils.ts new file mode 100644 index 0000000000..a4d5984215 --- /dev/null +++ b/packages/store-sync/test/utils.ts @@ -0,0 +1,26 @@ +import { drizzle } from "drizzle-orm/sql-js"; +import { createPublicClient, http } from "viem"; +import { foundry } from "viem/chains"; +import initSqlJs from "sql.js"; +import mudConfig from "../../../e2e/packages/contracts/mud.config"; +import { createWorld } from "@latticexyz/recs"; +import { resolveConfig } from "@latticexyz/store"; +import { recsStorage } from "../src/recs"; +import { sqliteStorage } from "../src/sqlite"; +import { createStorageAdapter, createStore } from "../src/zustand"; + +const publicClient = createPublicClient({ + chain: foundry, + transport: http(), +}); + +export const tables = resolveConfig(mudConfig).tables; + +export const { components, storageAdapter: recsStorageAdapter } = recsStorage({ world: createWorld(), tables }); + +export const useStore = createStore({ tables }); +export const zustandStorageAdapter = createStorageAdapter({ store: useStore }); + +const SqlJs = await initSqlJs(); +export const db = drizzle(new SqlJs.Database(), {}); +export const sqliteStorageAdapter = await sqliteStorage({ database: db, publicClient });