mirror of
https://github.com/lana-k/sqliteviz.git
synced 2025-12-06 10:08:52 +08:00
Sqljs upgrade and benchmark improvements (#103)
* Update to sql.js 1.7.0 * Update to emsdk 3.0.1, replace/remove deprecated/irrelevant settings - Renamed .bc extension to .o - Remove deprecated INLINING_LIMIT setting - Remove SINGLE_FILE * Update SQLite to 3.39.3 * Collect and plot CPU and RSS charts from the benchmark containers * Move procpath commands to a playbook, plot only top 2 RSS & CPU usage * Optimise for size, put -flto for both compile and link
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
FROM emscripten/emsdk:2.0.24
|
||||
FROM emscripten/emsdk:3.0.1
|
||||
|
||||
WORKDIR /tmp/build
|
||||
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
# SQLite WebAssembly build micro-benchmark
|
||||
|
||||
This directory contains a micro-benchmark for evaluating SQLite
|
||||
WebAssembly builds performance on typical SQL queries, run from
|
||||
`make.sh` script. It can also serve as a smoke test.
|
||||
This directory contains a micro-benchmark for evaluating SQLite WebAssembly
|
||||
builds performance on read and write SQL queries, run from `make.sh` script. If
|
||||
the script has permission to `nice` processes and [Procpath][1] is installed,
|
||||
e.g. it is run with `sudo -E env PATH=$PATH ./make.sh`, it'll `renice` all
|
||||
processes running inside the benchmark containers. It can also serve as a smoke
|
||||
test (e.g. for memory leaks).
|
||||
|
||||
The benchmark operates on a set of SQLite WebAssembly builds expected
|
||||
in `lib/build-$NAME` directories each containing `sql-wasm.js` and
|
||||
`sql-wasm.wasm`. Then it creates a Docker image for each, and runs
|
||||
the benchmark in Firefox and Chromium using Karma in the container.
|
||||
The benchmark operates on a set of SQLite WebAssembly builds expected in
|
||||
`lib/build-$NAME` directories each containing `sql-wasm.js` and
|
||||
`sql-wasm.wasm`. Then it creates a Docker image for each, and runs the
|
||||
benchmark in Firefox and Chromium using Karma in the container.
|
||||
|
||||
After successful run, the benchmark result of each build is contained
|
||||
in `build-$NAME-result.json`. The JSON result files can be analysed
|
||||
using `result-analysis.ipynb` Jupyter notebook.
|
||||
After successful run, the benchmark produces the following per each build:
|
||||
|
||||
- `build-$NAME-result.json`
|
||||
- `build-$NAME.sqlite` (if Procpath is installed)
|
||||
- `build-$NAME.svg` (if Procpath is installed)
|
||||
|
||||
These files can be analysed using `result-analysis.ipynb` Jupyter notebook.
|
||||
The SVG is a chart with CPU and RSS usage of each test container (i.e. Chromium
|
||||
run, then Firefox run per container).
|
||||
|
||||
[1]: https://pypi.org/project/Procpath/
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
cleanup () {
|
||||
rm -rf lib/dist $flag_file
|
||||
rm -rf lib/dist "$renice_flag_file"
|
||||
docker rm -f sqljs-benchmark-run 2> /dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
@@ -11,34 +12,36 @@ if [ ! -f sample.csv ]; then
|
||||
| gunzip -c > sample.csv
|
||||
fi
|
||||
|
||||
PLAYBOOK=procpath/karma_docker.procpath
|
||||
|
||||
# for renice to work run like "sudo -E env PATH=$PATH ./make.sh"
|
||||
test_ni=$(nice -n -1 nice)
|
||||
if [ $test_ni == -1 ]; then
|
||||
flag_file=$(mktemp)
|
||||
test_ni=$(nice -n -5 nice)
|
||||
if [ $test_ni == -5 ]; then
|
||||
renice_flag_file=$(mktemp)
|
||||
fi
|
||||
(
|
||||
while [ -f $flag_file ]; do
|
||||
root_pid=$(
|
||||
docker ps -f status=running -f name='^sqljs-benchmark-' -q \
|
||||
| xargs -r -I{} -- docker inspect -f '{{.State.Pid}}' {}
|
||||
)
|
||||
if [ ! -z $root_pid ]; then
|
||||
procpath query -d $'\n' "$..children[?(@.stat.pid == $root_pid)]..pid" \
|
||||
| xargs -I{} -- renice -n -1 -p {} > /dev/null
|
||||
fi
|
||||
sleep 1
|
||||
done &
|
||||
)
|
||||
{
|
||||
while [ -f $renice_flag_file ]; do
|
||||
procpath --logging-level ERROR play -f $PLAYBOOK renice:watch
|
||||
done
|
||||
} &
|
||||
|
||||
shopt -s nullglob
|
||||
for d in lib/build-* ; do
|
||||
rm -rf lib/dist
|
||||
cp -r $d lib/dist
|
||||
sample_name=$(basename $d)
|
||||
|
||||
name=$(basename $d)
|
||||
docker build -t sqliteviz/sqljs-benchmark:$name .
|
||||
docker rm sqljs-benchmark-$name 2> /dev/null || true
|
||||
docker run -it --cpus 2 --name sqljs-benchmark-$name sqliteviz/sqljs-benchmark:$name
|
||||
docker cp sqljs-benchmark-$name:/tmp/build/suite-result.json ${name}-result.json
|
||||
docker rm sqljs-benchmark-$name
|
||||
docker build -t sqliteviz/sqljs-benchmark .
|
||||
docker rm sqljs-benchmark-run 2> /dev/null || true
|
||||
docker run -d -it --cpus 2 --name sqljs-benchmark-run sqliteviz/sqljs-benchmark
|
||||
{
|
||||
rm -f ${sample_name}.sqlite
|
||||
procpath play -f $PLAYBOOK -o database_file=${sample_name}.sqlite track:record
|
||||
procpath play -f $PLAYBOOK -o database_file=${sample_name}.sqlite \
|
||||
-o plot_file=${sample_name}.svg track:plot
|
||||
} &
|
||||
|
||||
docker attach sqljs-benchmark-run
|
||||
docker cp sqljs-benchmark-run:/tmp/build/suite-result.json ${sample_name}-result.json
|
||||
docker rm sqljs-benchmark-run
|
||||
done
|
||||
|
||||
28
lib/sql-js/benchmark/procpath/karma_docker.procpath
Normal file
28
lib/sql-js/benchmark/procpath/karma_docker.procpath
Normal file
@@ -0,0 +1,28 @@
|
||||
# This command may run when "sqljs-benchmark-run" does not yet exist or run
|
||||
[renice:watch]
|
||||
interval: 2
|
||||
repeat: 30
|
||||
environment:
|
||||
ROOT_PID=docker inspect -f "{{.State.Pid}}" sqljs-benchmark-run 2> /dev/null || true
|
||||
query:
|
||||
PIDS=$..children[?(@.stat.pid in [$ROOT_PID])]..pid
|
||||
command:
|
||||
echo $PIDS | tr , '\n' | xargs --no-run-if-empty -I{} -- renice -n -5 -p {}
|
||||
|
||||
# Expected input arguments: database_file
|
||||
[track:record]
|
||||
interval: 1
|
||||
stop_without_result: 1
|
||||
environment:
|
||||
ROOT_PID=docker inspect -f "{{.State.Pid}}" sqljs-benchmark-run
|
||||
query:
|
||||
$..children[?(@.stat.pid == $ROOT_PID)]
|
||||
pid_list: $ROOT_PID
|
||||
|
||||
# Expected input arguments: database_file, plot_file
|
||||
[track:plot]
|
||||
moving_average_window: 5
|
||||
title: Chromium vs Firefox (№1 RSS, №2 CPU)
|
||||
custom_query_file:
|
||||
procpath/top2_rss.sql
|
||||
procpath/top2_cpu.sql
|
||||
29
lib/sql-js/benchmark/procpath/top2_cpu.sql
Normal file
29
lib/sql-js/benchmark/procpath/top2_cpu.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
WITH diff_all AS (
|
||||
SELECT
|
||||
record_id,
|
||||
ts,
|
||||
stat_pid,
|
||||
stat_utime + stat_stime - LAG(stat_utime + stat_stime) OVER (
|
||||
PARTITION BY stat_pid
|
||||
ORDER BY record_id
|
||||
) tick_diff,
|
||||
ts - LAG(ts) OVER (
|
||||
PARTITION BY stat_pid
|
||||
ORDER BY record_id
|
||||
) ts_diff
|
||||
FROM record
|
||||
), diff AS (
|
||||
SELECT * FROM diff_all WHERE tick_diff IS NOT NULL
|
||||
), one_time_pid_condition AS (
|
||||
SELECT stat_pid
|
||||
FROM record
|
||||
GROUP BY 1
|
||||
ORDER BY SUM(stat_utime + stat_stime) DESC
|
||||
LIMIT 2
|
||||
)
|
||||
SELECT
|
||||
ts,
|
||||
stat_pid pid,
|
||||
100.0 * tick_diff / (SELECT value FROM meta WHERE key = 'clock_ticks') / ts_diff value
|
||||
FROM diff
|
||||
JOIN one_time_pid_condition USING(stat_pid)
|
||||
13
lib/sql-js/benchmark/procpath/top2_rss.sql
Normal file
13
lib/sql-js/benchmark/procpath/top2_rss.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
WITH one_time_pid_condition AS (
|
||||
SELECT stat_pid
|
||||
FROM record
|
||||
GROUP BY 1
|
||||
ORDER BY MAX(stat_rss) DESC
|
||||
LIMIT 2
|
||||
)
|
||||
SELECT
|
||||
ts,
|
||||
stat_pid pid,
|
||||
stat_rss / 1024.0 / 1024 * (SELECT value FROM meta WHERE key = 'page_size') value
|
||||
FROM record
|
||||
JOIN one_time_pid_condition USING(stat_pid)
|
||||
File diff suppressed because one or more lines are too long
@@ -2,9 +2,11 @@ import logging
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# See the setting descriptions on these pages:
|
||||
# - https://emscripten.org/docs/optimizing/Optimizing-Code.html
|
||||
# - https://github.com/emscripten-core/emscripten/blob/main/src/settings.js
|
||||
cflags = (
|
||||
'-O2',
|
||||
# SQLite configuration
|
||||
'-DSQLITE_DEFAULT_CACHE_SIZE=-65536', # 64 MiB
|
||||
'-DSQLITE_DEFAULT_MEMSTATUS=0',
|
||||
'-DSQLITE_DEFAULT_SYNCHRONOUS=0',
|
||||
@@ -13,26 +15,26 @@ cflags = (
|
||||
'-DSQLITE_ENABLE_FTS3',
|
||||
'-DSQLITE_ENABLE_FTS3_PARENTHESIS',
|
||||
'-DSQLITE_ENABLE_FTS5',
|
||||
'-DSQLITE_ENABLE_JSON1',
|
||||
'-DSQLITE_ENABLE_NORMALIZE',
|
||||
'-DSQLITE_EXTRA_INIT=extra_init',
|
||||
'-DSQLITE_OMIT_DEPRECATED',
|
||||
'-DSQLITE_OMIT_LOAD_EXTENSION',
|
||||
'-DSQLITE_OMIT_SHARED_CACHE',
|
||||
'-DSQLITE_THREADSAFE=0',
|
||||
# Compile-time optimisation
|
||||
'-Os', # reduces the code size about in half comparing to -O2
|
||||
'-flto',
|
||||
)
|
||||
emflags = (
|
||||
# Base
|
||||
'--memory-init-file', '0',
|
||||
'-s', 'RESERVED_FUNCTION_POINTERS=64',
|
||||
'-s', 'ALLOW_TABLE_GROWTH=1',
|
||||
'-s', 'SINGLE_FILE=0',
|
||||
# WASM
|
||||
'-s', 'WASM=1',
|
||||
'-s', 'ALLOW_MEMORY_GROWTH=1',
|
||||
# Optimisation
|
||||
'-s', 'INLINING_LIMIT=50',
|
||||
'-O3',
|
||||
'-s', 'ENVIRONMENT=web,worker',
|
||||
# Link-time optimisation
|
||||
'-Os',
|
||||
'-flto',
|
||||
# sql.js
|
||||
'-s', 'EXPORTED_FUNCTIONS=@src/sqljs/exported_functions.json',
|
||||
@@ -50,22 +52,22 @@ def build(src: Path, dst: Path):
|
||||
'emcc',
|
||||
*cflags,
|
||||
'-c', src / 'sqlite3.c',
|
||||
'-o', out / 'sqlite3.bc',
|
||||
'-o', out / 'sqlite3.o',
|
||||
])
|
||||
logging.info('Building LLVM bitcode for extension-functions.c')
|
||||
subprocess.check_call([
|
||||
'emcc',
|
||||
*cflags,
|
||||
'-c', src / 'extension-functions.c',
|
||||
'-o', out / 'extension-functions.bc',
|
||||
'-o', out / 'extension-functions.o',
|
||||
])
|
||||
|
||||
logging.info('Building WASM from bitcode')
|
||||
subprocess.check_call([
|
||||
'emcc',
|
||||
*emflags,
|
||||
out / 'sqlite3.bc',
|
||||
out / 'extension-functions.bc',
|
||||
out / 'sqlite3.o',
|
||||
out / 'extension-functions.o',
|
||||
'-o', out / 'sql-wasm.js',
|
||||
])
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from pathlib import Path
|
||||
from urllib import request
|
||||
|
||||
|
||||
amalgamation_url = 'https://sqlite.org/2022/sqlite-amalgamation-3390000.zip'
|
||||
amalgamation_url = 'https://sqlite.org/2022/sqlite-amalgamation-3390300.zip'
|
||||
|
||||
# Extension-functions
|
||||
# ===================
|
||||
@@ -31,7 +31,7 @@ extension_urls = (
|
||||
('https://github.com/jakethaw/pivot_vtab/raw/08ab0797/pivot_vtab.c', 'sqlite3_pivotvtab_init'),
|
||||
)
|
||||
|
||||
sqljs_url = 'https://github.com/sql-js/sql.js/archive/refs/tags/v1.5.0.zip'
|
||||
sqljs_url = 'https://github.com/sql-js/sql.js/archive/refs/tags/v1.7.0.zip'
|
||||
|
||||
|
||||
def _generate_extra_init_c_function(init_function_names):
|
||||
|
||||
2
lib/sql-js/dist/sql-wasm.js
vendored
2
lib/sql-js/dist/sql-wasm.js
vendored
File diff suppressed because one or more lines are too long
BIN
lib/sql-js/dist/sql-wasm.wasm
vendored
BIN
lib/sql-js/dist/sql-wasm.wasm
vendored
Binary file not shown.
Reference in New Issue
Block a user