Conversation
|
I did some performance benchmarks on web, with 1M rows with large ids. This forces SQLite to write the temp b-tree to disk. Takeways:
Linux, native:
Web, IDBBatchAtomicVFS:
Linux, OPFSCoopSyncVFS:
Web, IDBBatchAtomicVFS,
Web, IDBBatchAtomicVFS,
The test data: BEGIN TRANSACTION;
-- 1M ops
WITH RECURSIVE generate_rows(n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM generate_rows WHERE n < 1000000
)
INSERT INTO ps_oplog (bucket, op_id, row_type, row_id, key, data, hash)
SELECT
(n % 10), -- Generate 10 different buckets
n,
'thisisatable',
'thisismyrowid' || n,
'thisisarowkeykey_' || n,
'{"n": ' || n || '}',
(n * 17) % 1000000000 -- Some pseudo-random hash
FROM generate_rows;
-- 10 buckets
WITH RECURSIVE generate_rows(n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM generate_rows WHERE n < 10
)
INSERT INTO ps_buckets (id, name)
SELECT
(n % 10),
'bucket' || n
FROM generate_rows;
COMMIT; |
|
After some reports of slow performance for this query on Android devices when syncing large databases, I did some further testing. It is quite tricky to simulate the same conditions on a desktop system. On Linux, I managed to get some good results by using cgroups v2 to limit filesystem throughput, and importantly also limit max memory. This ensures the temporary files are actually flushed to disk - otherwise it is effectively just buffered in memory, never seeing the slowdown from filesystem usage. With the above applied, I can sometimes see massive performance differences between the current query and the optimized ones - as much as 60s -> 2s for some tests. It does depend a lot on the data volumes, memory available and filesystem throughput. In some cases, everything performs great until you reach a certain threshold, and then performance degrades significantly due to the filesystem usage. I'll need to rebase this PR to work with the latest updates, and also do some proper real-life testing on e.g. Android devices, but I think it's worth taking this further. |
|
Superseded by #78. |
#40 fixed a performance issue in initial/bulk sync when there are many duplicate row_ids, but decreased the performance slightly for the general case. This attempts to optimize it again, mostly by removing the second temp b-tree used in query execution.
This does not make a massive difference in overall initial sync performance. On my machine, with 1M ops, the query time reduces from around 5s -> 3s, versus a total initial sync time of 60s. So it's not a big gain overall, but this is the slowest query that locks the database for writes and cannot be split into smaller subqueries, so any optimization here helps with app responsiveness.
There is another query form added in the comments, which can take the initial sync query time down in the above case to under 2s (with no temp b-tree at all), but it doesn't cater for incremental updates. It needs some stats tracking / heuristics added to know when to use one query or the other, so I'm leaving that for later.
The temp b-trees used by this query could also be related to
RangeError: Maximum call stack size exceedederrors seen on iOS, as well asdisk I/O erroroccasionally seen on Android, when SQLite is configured to use files for temporary storage. While those issues have other workarounds, any changes to reduce temporary b-trees here could help.TODO: