Someone opened a PR to implement readahead in Turso. It was a throwaway implementation, but a good excuse to measure io_uring and understand more about it.
Turso has two backends. syscall uses pread(2). io_uring uses io_uring, and opens the database file with O_DIRECT with no option to use buffered I/O. O_DIRECT takes away kernel readahead, so getting it back means implementing it in the application.
The PR’s results are impressive. io_uring with an application buffer is faster. I want to understand why.
Without readahead, io_uring issues only one entry at a time. The problem is the lack of concurrency: each read waits for the previous one. The application knows that “hey, at this time, I need page 100”, which means: Turso submits a read SQE for page 100, then waits for it. The scan continues. Now Turso needs page 101, so it submits a new read SQE that page.
With readahead on, things change. Turso needs page 100, detects sequential access, and instead of asking for one page it submits reads for pages 100 through 131. Now 32 reads are in flight at the same time.
The measurements below use TPC-H, a standard benchmark for analytic databases, on a 1.2 GiB database.
Q6, the query I measured, does a full scan on lineitem , the biggest table of the benchmark.
, the biggest table of the benchmark. off means PRAGMA prefetch_pages=0 : The PR’s code without readahead.
means : The PR’s code without readahead. on means a window of 32 pages.
Request merging
... continue reading