Skip to content

Commit 50d58a2

Browse files
Centrilgefjoncoolreader18
authored
Add modules/benchmarks-ts (#3408)
# Description of Changes Adds `modules/benchmarks-ts`. # API and ABI breaking changes None # Expected complexity level and risk 2? # Testing This is a test. --------- Co-authored-by: Phoebe Goldman <phoebe@goldman-tribe.org> Co-authored-by: Noa <coolreader18@gmail.com>
1 parent afb8c08 commit 50d58a2

21 files changed

Lines changed: 1352 additions & 145 deletions

File tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
export * from './type_builders';
2-
export { schema } from './schema';
2+
export { schema, type InferSchema } from './schema';
33
export { table } from './table';
44
export * as errors from './errors';
55
export { SenderError } from './errors';
6+
export { type Reducer, type ReducerCtx } from './reducers';
67

78
import './polyfills'; // Ensure polyfills are loaded
89
import './register_hooks'; // Ensure module hooks are registered

crates/bindings-typescript/src/server/schema.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,24 +175,26 @@ class Schema<S extends UntypedSchemaDef> {
175175
name: string,
176176
params: Params,
177177
fn: Reducer<S, Params>
178-
): void;
178+
): Reducer<S, Params>;
179179
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
180-
reducer(name: string, fn: Reducer<S, {}>): void;
180+
reducer(name: string, fn: Reducer<S, {}>): Reducer<S, {}>;
181181
reducer<Params extends ParamsObj | RowObj>(
182182
name: string,
183183
paramsOrFn: Params | Reducer<S, any>,
184184
fn?: Reducer<S, Params>
185-
): void {
185+
): Reducer<S, Params> {
186186
if (typeof paramsOrFn === 'function') {
187187
// This is the case where params are omitted.
188188
// The second argument is the reducer function.
189189
// We pass an empty object for the params.
190190
reducer(name, {}, paramsOrFn);
191+
return paramsOrFn;
191192
} else {
192193
// This is the case where params are provided.
193194
// The second argument is the params object, and the third is the function.
194195
// The `fn` parameter is guaranteed to be defined here.
195196
reducer(name, paramsOrFn, fn!);
197+
return fn!;
196198
}
197199
}
198200

crates/core/src/host/v8/syscall.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,13 @@ fn console_log<'scope>(scope: &mut PinScope<'scope, '_>, args: FunctionCallbackA
12051205
<_>::default()
12061206
};
12071207

1208-
let env = get_env(scope)?;
1208+
let env = get_env(scope).inspect_err(|_| {
1209+
tracing::warn!(
1210+
"{}:{} {msg}",
1211+
filename.as_deref().unwrap_or("unknown"),
1212+
frame.get_line_number()
1213+
);
1214+
})?;
12091215

12101216
let function = env.log_record_function();
12111217
let record = Record {

crates/schema/tests/ensure_same_schema.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,12 @@ macro_rules! declare_tests {
6969
}
7070

7171
declare_tests! {
72+
benchmarks => "benchmarks",
7273
module_test => "module-test",
7374
sdk_test_connect_disconnect => "sdk-test-connect-disconnect",
7475
sdk_test => "sdk-test",
7576
}
7677

77-
// FIXME: Move `benchmarks => "benchmarks,` back into the macro once `benchmarks-ts` exists
7878
#[test]
7979
#[serial]
8080
fn ensure_same_schema_rust_csharp_benchmarks() {

crates/testing/src/modules.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,17 @@ impl ModuleLanguage for Rust {
311311
&MODULE
312312
}
313313
}
314+
315+
pub struct TypeScript;
316+
317+
impl ModuleLanguage for TypeScript {
318+
const NAME: &'static str = "typescript";
319+
320+
fn get_module() -> &'static CompiledModule {
321+
lazy_static::lazy_static! {
322+
pub static ref MODULE: CompiledModule = CompiledModule::compile("benchmarks-ts", COMPILATION_MODE);
323+
}
324+
325+
&MODULE
326+
}
327+
}

crates/testing/tests/standalone_integration_test.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serial_test::serial;
22
use spacetimedb_lib::sats::{product, AlgebraicValue};
33
use spacetimedb_testing::modules::{
4-
CompilationMode, CompiledModule, Csharp, LogLevel, LoggerRecord, ModuleHandle, ModuleLanguage, Rust,
4+
CompilationMode, CompiledModule, Csharp, LogLevel, LoggerRecord, ModuleHandle, ModuleLanguage, Rust, TypeScript,
55
DEFAULT_CONFIG, IN_MEMORY_CONFIG,
66
};
77
use std::{
@@ -323,6 +323,12 @@ fn test_calling_bench_db_circles_csharp() {
323323
test_calling_bench_db_circles::<Csharp>();
324324
}
325325

326+
#[test]
327+
#[serial]
328+
fn test_calling_bench_db_circles_typescript() {
329+
test_calling_bench_db_circles::<TypeScript>();
330+
}
331+
326332
fn test_calling_bench_db_ia_loop<L: ModuleLanguage>() {
327333
L::get_module().with_module_async(DEFAULT_CONFIG, |module| async move {
328334
#[rustfmt::skip]
@@ -352,3 +358,9 @@ fn test_calling_bench_db_ia_loop_rust() {
352358
fn test_calling_bench_db_ia_loop_csharp() {
353359
test_calling_bench_db_ia_loop::<Csharp>();
354360
}
361+
362+
#[test]
363+
#[serial]
364+
fn test_calling_bench_db_ia_loop_typescript() {
365+
test_calling_bench_db_ia_loop::<TypeScript>();
366+
}

modules/benchmarks-ts/.gitignore

Whitespace-only changes.

modules/benchmarks-ts/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "benchmarks-ts",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"build": "cargo build -p spacetimedb-standalone && cargo run -p spacetimedb-cli -- build",
8+
"generate-ts": "cargo build -p spacetimedb-standalone && cargo run -p spacetimedb-cli -- generate --lang typescript --out-dir ts-codegen",
9+
"publish": "cargo run -p spacetimedb-cli -- publish"
10+
},
11+
"keywords": [],
12+
"author": "",
13+
"license": "ISC",
14+
"dependencies": {
15+
"spacetimedb": "workspace:^"
16+
}
17+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//! STDB module used for benchmarks based on "realistic" workloads we are focusing in improving.
2+
3+
import { type Load, newLoad, blackBox } from './load';
4+
import { spacetimedb, type Entity, type Circle, type Food } from './schema';
5+
import { Timestamp } from 'spacetimedb';
6+
import { t } from 'spacetimedb/server';
7+
8+
function newEntity(id: number, x: number, y: number, mass: number): Entity {
9+
return {
10+
id,
11+
position: { x, y },
12+
mass,
13+
};
14+
}
15+
16+
function newCircle(
17+
entity_id: number,
18+
player_id: number,
19+
x: number,
20+
y: number,
21+
magnitude: number,
22+
last_split_time: Timestamp
23+
): Circle {
24+
return {
25+
entity_id,
26+
player_id,
27+
direction: { x, y },
28+
magnitude,
29+
last_split_time,
30+
};
31+
}
32+
33+
function newFood(entity_id: number): Food {
34+
return {
35+
entity_id,
36+
};
37+
}
38+
39+
function massToRadius(mass: number): number {
40+
return Math.sqrt(mass);
41+
}
42+
43+
function isOverlapping(entity1: Entity, entity2: Entity): boolean {
44+
const entity1Radius = massToRadius(entity1.mass);
45+
const entity2Radius = massToRadius(entity2.mass);
46+
const distance = Math.sqrt(
47+
(entity1.position.x - entity2.position.x) ** 2 +
48+
(entity1.position.y - entity2.position.y) ** 2
49+
);
50+
return distance < Math.max(entity1Radius, entity2Radius);
51+
}
52+
53+
// ---------- insert bulk ----------
54+
55+
const insertBulkEntity = spacetimedb.reducer('insert_bulk_entity', { count: t.u32() }, (ctx, { count }) => {
56+
for (let id = 0; id < count; id++) {
57+
ctx.db.entity.insert(newEntity(0, id, id + 5, id * 5));
58+
}
59+
console.info(`INSERT ENTITY: ${count}`);
60+
});
61+
62+
const insertBulkCircle = spacetimedb.reducer('insert_bulk_circle', { count: t.u32() }, (ctx, { count }) => {
63+
for (let id = 0; id < count; id++) {
64+
ctx.db.circle.insert(newCircle(id, id, id, id + 5, id * 5, ctx.timestamp));
65+
}
66+
console.info(`INSERT CIRCLE: ${count}`);
67+
});
68+
69+
const insertBulkFood = spacetimedb.reducer('insert_bulk_food', { count: t.u32() }, (ctx, { count }) => {
70+
for (let id = 1; id <= count; id++) {
71+
ctx.db.food.insert(newFood(id));
72+
}
73+
console.info(`INSERT FOOD: ${count}`);
74+
});
75+
76+
// Simulate
77+
// ```
78+
// SELECT * FROM Circle, Entity, Food
79+
// ```
80+
const crossJoinAll = spacetimedb.reducer('cross_join_all', { expected: t.u32() }, (ctx, { expected }) => {
81+
let count: number = 0;
82+
83+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
84+
for (const circle of ctx.db.circle.iter()) {
85+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
86+
for (const entity of ctx.db.entity.iter()) {
87+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
88+
for (const food of ctx.db.food.iter()) {
89+
count += 1;
90+
}
91+
}
92+
}
93+
94+
console.info(`CROSS JOIN ALL: ${expected}, processed: ${count}`);
95+
});
96+
97+
// Simulate
98+
// ```
99+
// SELECT * FROM Circle JOIN ENTITY USING(entity_id), Food JOIN ENTITY USING(entity_id)
100+
// ```
101+
const crossJoinCircleFood = spacetimedb.reducer(
102+
'cross_join_circle_food',
103+
{ expected: t.u32() },
104+
(ctx, { expected }) => {
105+
let count: number = 0;
106+
107+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
108+
for (const circle of ctx.db.circle.iter()) {
109+
const entityId = ctx.db.entity.id;
110+
const circleEntity = entityId.find(circle.entity_id);
111+
if (circleEntity == null) {
112+
continue;
113+
}
114+
115+
for (const food of ctx.db.food.iter()) {
116+
count += 1;
117+
118+
const foodEntity = entityId.find(food.entity_id);
119+
if (foodEntity == null) {
120+
let string = JSON.stringify(circleEntity);
121+
throw new Error(`Entity not found: ${food.entity_id}`);
122+
}
123+
124+
blackBox(isOverlapping(circleEntity, foodEntity));
125+
}
126+
}
127+
128+
console.info(`CROSS JOIN CIRCLE FOOD: ${expected}, processed: ${count}`);
129+
}
130+
);
131+
132+
spacetimedb.reducer(
133+
'init_game_circles',
134+
{ initial_load: t.u32() },
135+
(ctx, { initial_load }) => {
136+
const load = newLoad(initial_load);
137+
138+
insertBulkFood(ctx, { count: load.initialLoad });
139+
insertBulkEntity(ctx, { count: load.initialLoad });
140+
insertBulkCircle(ctx, { count: load.smallTable });
141+
}
142+
);
143+
144+
spacetimedb.reducer(
145+
'run_game_circles',
146+
{ initial_load: t.u32() },
147+
(ctx, { initial_load }) => {
148+
const load = newLoad(initial_load);
149+
150+
crossJoinCircleFood(ctx, { expected: initial_load * load.smallTable });
151+
crossJoinAll(ctx, {
152+
expected: initial_load * initial_load * load.smallTable,
153+
});
154+
}
155+
);

0 commit comments

Comments
 (0)