Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

SQLite benchmark

This Expo app compares ordinary SQLite operations through op-sqlite, Nitro SQLite, and expo-sqlite. Build a Release app for iOS or Android, open it, and tap Run defaults for the suite, Run normalized for the suite with matched PRAGMAs, or Original four tests for the 1,000-row screen from the published charts. Use Share run data and manifest to save the samples, checksums, failures, and environment details as JSON.

The latest results, with screenshots and raw samples, are in results/2026-11-13.

Each workload uses 250 rows and, for read cases, 250 queries per sample. It runs one warmup round followed by three measured rounds. The runner rotates library order for each case and round, waits 2.5 seconds after setup for every sample, and measures only the operation loop or transaction. It checks affected row counts and stored values after writes and consumes and checks all selected values during full reads. A failed check appears on screen and excludes that library and case from the summary. Individual samples, median, and minimum-to-maximum range appear together.

The HostObjects creation case only obtains results and checks row counts. The separate HostObjects full read checks every id, name, and value. Neither case is presented as equivalent to the other libraries' plain-object full reads. The original screen used 1,000 rows and 1,000 read queries with one pass per library, so its times are not directly comparable to this runner's times.

Both op-sqlite and Nitro SQLite compile their own SQLite, and package.json gives them the same build settings. Each has its performance compile flags turned on, and both use fdatasync() for commits on Android. F2FS batch atomic writes are off for both, because they let commits skip the rollback journal on F2FS phones and would turn the single-row insert cases into a filesystem comparison. To measure with them, set nitroSQLite.enableBatchAtomicWrite to true and add -DSQLITE_ENABLE_BATCH_ATOMIC_WRITE=1 to op-sqlite.sqliteFlags, then rebuild. On iOS both libraries export the same sqlite3_* symbols, so one SQLite build serves both in this app. The manifest records the compile options each connection reports.

The default mode leaves SQLite settings at each library's reported defaults. Normalized mode requests WAL journal mode, synchronous=FULL, a 2 MiB page cache, and memory temp storage on each benchmark connection, then verifies those PRAGMAs. Expo's exclusive callback transaction creates another connection and begins its transaction before calling the benchmark. Its connection-local settings cannot be established outside the timed section, so that case is visibly omitted from normalized mode; it remains available in default mode. The modes use separate database files because WAL is persistent. The exported manifest includes the exact run policy, Release or development mode, device and OS, JavaScript engine, React Native and package versions, SQLite version and source ID, compile options, and actual PRAGMAs for the benchmark connection. Matching source IDs cannot establish whether two libraries share one native engine. Keep runs from different modes and builds separate when comparing results.

The additional read cases compare ordinary and reusable prepared statements using identical SQL and bound values. Full reads consume every field of every row; indexed point reads consume one checked row per query and expose the cost of small calls. Each has synchronous and asynchronous variants. The insert cases compare ordinary and prepared calls inside an explicit BEGIN/COMMIT transaction, separate from the callback transaction case. op-sqlite and Nitro SQLite also run one asynchronous native batch; expo-sqlite has no equivalent parameter batch API and appears as unsupported. These cases use the same seeded values and the selected run mode's durability settings.

Further cases cover library features the original four tests leave out. The raw reads return rows as arrays through op-sqlite's executeRaw, Nitro SQLite's executeRaw and Expo's executeForRawResult. Expo returns arrays only from a statement, so its raw case prepares and finalizes one per query. The wide read selects 20 columns per row, and the BLOB read returns 16 KB values as each library's byte type. The large read runs one query that returns 400 times the configured row count. Its frame gap case reports the longest time between animation frames while that query and its conversion run, which shows how long the JS thread could not render. The concurrent case starts every indexed point read before awaiting any of them. Nitro SQLite also runs its synchronous native batch; op-sqlite 19.0.5 has no public synchronous batch API and Expo has no parameter batch API, so both appear as unsupported there.

Statement creation and input-array construction happen before the timer. Each timed prepared execution includes binding and result consumption, and teardown releases the statement through the library's supported lifecycle. Nitro SQLite and expo-sqlite expose explicit finalization; op-sqlite does not, so the adapter drops its statement reference after the case. The batch case checks affected rows when the library reports them and always verifies the stored row count and checksum. The explicit transaction cases roll back on error. Compare only cases with the same query shape, dispatch mode, and transaction label; no result here measures a proposed queue or worker change.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages