10k dependencies problem ======================== How to efficiently load applications with an extreme number of dependencies? For example, an application with 10k dependencies. This is of course an unlikely worst-case scenario, but I think it makes sense to think about it. Especially given that I plan to add class extenions / extension classes, which could lead to tiny "extension modules" for various builtin types e.g. string, similar to how things work in node.js. Loader Service -------------- Have a per-system "Loader Service" with the following API: * (map_fd, entry, ctx) = map_file_refcounted(exec_fd, process-handle) The Loader Service would keep all libraries that are needed in memory (but packing several tiny libraries into each VM page). The map API would provide access to: * map_fd: all the mapped memory (shared for all processes) * entry: an entry point offset (in the memory area) that can be called. * ctx: a context object to pass to the entry point. It could keep reference counts for all modules, so unused ones can be de-allocated (but perhaps delay that until something else needs the memory and/or there's a significant amount of memory pages that can be free'd). Fast loading of files --------------------- It would have to open 10k tiny files and copy all into memory! Could io_uring speed things up? Would keeping an fd to the library directory speed things up? Would using a different directory than /usr/lib speed things up? Does it need sub-directories? E.g. /usr/lib/.../slul-libs/a/aa/libaaa.so Maybe execution can start before everything is loaded? Maybe userfaultfd can be used for that? Module format ------------- The module format needs to be performance-optimized! Use a compression format that is really fast to decompress, even on small files. Perhaps LZ4 or zstd. Use some really fast data structure for looking up dependencies. Security -------- The loader service should only be able to load from the system library directory. It should be sandboxed to ONLY have access to this directory, and only read-only access. And of course to the unix socket (but maybe that can be provided by systemd?) It should only have access to the minimum amount of syscalls needed. It does not need to have any other access to the system (devices, etc). OS performance -------------- With a SSD, not sure about exact specs: time head /usr/lib/x86_64-linux-gnu/*.so.{0,1,2} > /dev/null 2>&1 real 0.327s user 0.009s sys 0.059s ls -1 /usr/lib/x86_64-linux-gnu/*.so.{0,1,2} | wc -l 762 0.327/762*10e3 = 4.3 seconds (assuming the workload was / would be I/O bound in both cases) Running it a second time (with hot caches) gave: time head /usr/lib/x86_64-linux-gnu/*.so.{0,1,2} > /dev/null 2>&1 real 0.018s user 0.009s sys 0.009s Alternative solution ==================== Could have an post-installation task that rebuilds some "mega-package-file" that contains every libraries that is installed. That file could then be mapped into memory when an application is loaded. However, there are some downsides: * The large amount of mapped pages could possibly cause various slowness and/or use lots of kernel memory. Actually, I don't know what would happen and I guess it is OS specific. * It could use quite a bit of virtual address space, which could be a problem on 32-bit platforms.