A 4 GB pipe-delimited file is read with one read call per line and parsed, taking 90 seconds. Say what dominates the time, then compare a large buffered block read with memory mapping the file, giving the mechanism of each and the case where each one is the wrong choice.
A 4 GB pipe-delimited file is read with one read call per line and parsed, taking 90 seconds. Say what dominates the time, then compare a large buffered block read with memory mapping the file, giving the mechanism of each and the case where each one is the wrong choice.
Approach: Count the number of system calls and the number of copies each approach performs per byte, then think about page faults and about a file that changes underneath you.
System call overhead dominates, roughly 40 million read calls at about a microsecond each, so the fix is to read in blocks of 1 to 8 MB and split lines in user space, or to use memory mapping and parse in place. A block read makes one system call per megabyte and one copy out of the page cache into your buffer, which is predictable and works on pipes and sockets as well as files. Memory mapping removes that copy and lets parsers point straight at the bytes, at the cost of a page fault every 4 KB unless huge pages or a sequential madvise hint are used, and it fails when the file is truncated underneath you, which arrives as a bus error rather than a short read. Mapping is also wrong on network file systems where a fault can block indefinitely, and block reads are wrong when you need random access into a very large file. Either change gives 5 to 20 times over the per-line loop, after which the parse itself is the cost.
Follow-up: How would you split the parse across eight threads without a line being cut between two threads?
Key concepts: system call overhead, page cache, memory mapping, page fault.