i’ve been working on dconf recently. one of the things i’ve been up to is writing some proper test cases. of course, testing is a good chance to check performance…
the test case is written against the dconf gsettings backend. it generates 1000 random changesets and writes them into an empty dconf database while tracking the changes in two hash tables. one hash table is managed explicitly by adding all changes to it and the other is managed implicitly by connecting to change notification signals and updating itself when they are received. after each of the 1000 changesets is written a full three-way comparison is performed to ensure that the tables match each other and the contents of the dconf database.
the test system is a quad core i7 at 2.67GHz.
while the 1000 writes are going on the performance is quite awful. each dconf lookup takes approximately 30µs (compared to approximately 0.16µ for a GHashTable lookup, for comparison). that’s about 200 times slower. this can be almost entirely blamed on the fact that the gsettings keeps a local list of outstanding changes (those that have not made it to disk yet) and scans that list linearly on each read. you won’t run into this performance case unless your app is doing a *lot* of writes all at once.
the test then waits for the writes to make it to disk and runs the three-way comparison 1000 times over again. the results of this test are a more fair indication of dconf read performance under “normal” conditions. it takes approximately 1µs to do a lookup using the dconf gsettings backend (which is approximately 7 times as long as it takes to do a GHashTable lookup). for some time i’ve been telling people that “dconf lookups should be within an order of magnitude of GHashTable performance” and now i have hard numbers for that. this also gives a lower bound for GVDB performance (since dconf uses it).
tl;dr: a dconf read takes 1µs (which is approximately 7 times GHashTable).
btrfs and fsync
when doing this testing i noticed that dconf is really slow. it takes 64ms for the dconf-service to write each change to the disk. the reason for this is because it does an fsync() after each and every change. i have spinning disks. i sort of expected this and i designed around it; that’s why the gsettings backend for dconf keeps the local list of changes in flight: your app doesn’t wait for this 64milliseconds (and is actually completely unaware of it). it starts to look bad, though, when you’re doing 1000 writes.
i did a bit of reading and i discovered something very cool: btrfs guarantees that overwrite-by-rename is atomic. no need to call fsync() between writing the data to the temp file and renaming it over the old file. i have a patch to glib in #637544 to detect when g_file_set_contents() is being used on btrfs and skip the fsync() in that case. this reduces the amount of time taken to write a change from 64ms to 3.5ms.
tl;dr: btrfs is cool.
edit: another interesting dconf statistic: the dconf database generated by one instance of the test case is 475680 bytes on disk while containing 4455 entries. that’s about 107 bytes per entry.
edit2: after applying the glib btrfs no-fsync patch the 30µs dconf lookup time in the first phase of the test drops to about 4µs per lookup. this can be attributed to the fact that the service is having a much easier time keeping up with the write requests and therefore the list of outstanding changes (which is linearly searched) is kept much smaller.