This benchmark is designed to measure the performance of JavaScript frontend frameworks and reactive libraries in handling complex, dynamic data updates and queries, complementing the focus of the existing JS Framework Benchmark.
The JS Reactivity Benchmark simulates a data-driven application with multiple, interdependent UI components, all displaying different reactive views (queries) over the same large, in-memory dataset. Its primary goal is to evaluate how efficiently frameworks handle:
- Reactive Query Execution: The performance of defining and automatically re-running queries (filtering, sorting, joining, aggregating, projecting, paginating) as the underlying data changes.
- Fine-Grained UI Updates: How effectively the framework translates the results of these reactive queries into minimal and timely updates in the DOM, especially when many different views need to update from a single data change.
- Event Propagation: The overhead and efficiency of routing simulated user/system events to the reactive system to trigger state changes and subsequent UI updates.
The standard JS Framework Benchmark primarily measures:
- Raw rendering speed: How quickly frameworks can create and update large numbers of DOM elements from a simple, structured dataset (a list).
- Virtual DOM/Templating efficiency: The performance of the framework's mechanism for mapping a straightforward data structure to a visual representation, focusing on batch list operations (create, update 10th, delete, swap).
- Direct data-to-UI mapping: The benchmark tests scenarios where there's a relatively direct, one-to-one or one-to-many mapping from a single data list to the UI representation.
The JS Reactivity Benchmark, on the other hand, measures:
- The full reactive pipeline: From an event -> data change -> query re-computation -> UI update.
- Complex data dependencies: How frameworks handle UI derived from multiple tables, joins, aggregations, and filtered/sorted subsets of data.
- Overlapping views: How efficiently the system manages many different reactive queries over the same data, where a single data change may require multiple views to update simultaneously.
- Decision logic performance: The overhead of the reactive system's internal work to determine what needs to re-run (queries) and what in the UI needs to update based on granular state changes.
In essence, while the JS Framework Benchmark tests the efficiency of rendering given a data structure, the JS Reactivity Benchmark tests the efficiency of the entire process of reactively deriving and updating the UI in response to dynamic events and data relationships.
This benchmark is particularly relevant for:
- Framework/Library Authors: To understand how their reactivity system, state management integration, and rendering pipeline perform under conditions involving complex data relationships and event-driven updates, which are common in modern apps.
- Developers Building Data-Intensive Applications: Especially those working with:
- Client-side databases or sync engines: These often involve complex querying and reactive updates from a local data store.
- Sophisticated state management solutions: Which deal with normalizing data and deriving UI state from multiple sources.
- Real-time collaborative applications: Where granular, overlapping updates are frequent.
- Analytics dashboards or admin panels: Which often display multiple views and aggregates over the same core data.
- Anyone Evaluating Frameworks for Dynamic UIs: To gain insight beyond raw list rendering into how a framework handles the complexity inherent in many real-world applications.
-
In-Memory Data Store: The benchmark uses a normalized, in-memory data store with two primary tables:
itemsandcategories, implemented using JavaScript Maps for efficient access. -
Reactive Views: Several distinct reactive queries are implemented:
- Full scan/identity query
- Dynamic filtering (search text, status)
- Dynamic sorting by any field
- Pagination with configurable page size
- Category-based filtering with joins
- Combined filters with batched updates
-
Measurement System:
- High-precision timing using
performance.now() - Measures complete reactive cycle including UI updates
- Tracks operation counts and batch sizes
- Persistent history using localStorage
- Interactive results viewer with sorting
- High-precision timing using
-
Batching Support:
- Efficient batch processing for data mutations
- Configurable batch sizes
- Coalesced updates for better performance
Once the application is running in your browser:
- Open your browser's developer console
- Use the following commands to run benchmarks:
// Run all benchmark scenarios
window.benchmark.runAll();
// Run a specific scenario with optional batch size
window.benchmark.run("updateSingle", 50); // Updates 50 items
window.benchmark.run("searchFilter"); // Tests search filtering
window.benchmark.run("sortChange"); // Tests sort operations
// Available scenarios:
- updateSingle // Single field updates
- updateMultiple // Multiple field updates
- deleteItems // Item deletion
- addItems // Item creation
- searchFilter // Text search performance
- statusFilter // Status filtering
- sortChange // Sort field/direction changes
- pagination // Page size and navigation
- categoryFilter // Category-based filtering
// View current results in the UI or check the console output:
// Total time, operation count, and average time per operation| Operation | Time (ms) | Operations | Description |
|---|---|---|---|
| Pagination | 18.33 | 25 | Page size changes and navigation |
| Status Filter | 29.41 | 12 | Toggle between item statuses |
| Sort | 48.05 | 24 | Field and direction changes |
| Search | 59.69 | 12 | Text-based filtering |
| Category Filter | 212.68 | 30 | Category-based filtering |
| Operation | Time (ms) | Operations | Description |
|---|---|---|---|
| Update Multiple | 108.82 | 50 | Multiple field updates |
| Update Single | 109.46 | 50 | Single field updates |
| Delete | 111.07 | 50 | Item deletion |
| Add | 116.58 | 50 | Item creation |
-
Reactive System:
- Uses SolidJS signals and computations
- Measures complete cycle: event → data change → UI update
- Supports both individual and batched operations
-
Data Store:
- Map-based storage for O(1) lookups
- Normalized data with item-category relationships
- Support for CRUD operations and batch updates
-
Measurement Approach:
// High-precision timing const now = () => performance.now(); // Measure complete reactive cycle async function measureReactiveUpdate(action: () => void): Promise<number> { await nextFrame(); // Let current cycle complete const start = now(); action(); await nextFrame(); // Wait for update to process const end = now(); return end - start; }
-
History Tracking:
- Persistent storage of benchmark results
- Sortable results table
- Clear history functionality
- Tracks operation type, count, timing, and batch size