Show HN: Radix sort big files in memory
github.com
github.com
- Saving the start and end position of a string that represents a date with 16 bytes is silly. Just convert the date to 64 or 32 bit integer (or even less depending on the granularity and range of the dates).
- run through the file converting the dates to a smaller integer representation. When the array of integers is too big, sort it and use that to write a sorted text file chunk
- once that is done, merge the text file chunks together
Any good sorting algorithm is going to be able to do this in under 20 minutes with 16 cores. If IO is a bottleneck it would be much worse while trying to swap around text lines in a giant memory mapped file.
If you want to test something meant to scale, feed everything into an sqlite database and read it back. Sqlite should chew through this without any problem.
Background: "samtools," a tool meant for processing multi-gigabyte genomic alignment data, uses a sort algorithm basically the same as `sort`. This fork instead jams the data into Facebook's RocksDB, and then extracts it back out.
Speedup ranged from 1.2x to 5x, with bigger input files and slower disks seeing the most gains.
We use rfc3339 because there's a lot of gotchas that appear when you conflate civil timekeeping with an absolute time scale. The number of seconds in a day is not constant, and generally only known a few months ahead of a coming leap second. Civil timekeeping syntaxes can deal with this gracefully exactly because they're a structured representation rather than absolute nanos relative to some epoch.
I am not saving the start and end positions of a string that represents a data; I am saving start and end position of a line, since I need to split the loaded file into lines. This requires extra memory. I tried sorting without caching the end position of the line (and always reading from start till \n), but it was slower to sort this way.
There's no swapping of lines in a giant memory mapped file, the byte array of the file remains intact.
The program only swaps the line markers.
> Just convert the date to 64 or 32 bit integer
This would require extra memory for each line, complicate user interface (data formats vary a lot) and make the utility less generic, since you don't always want to sort chronologically. The current idea is to sort using as little extra memory as possible. This is because when files are already so big fitting them into RAM is a challenge already.
---
Finally, there's no evidence why the proposed solution would be faster. Empirically I have found that sorting chunks and merging is considerably slower than sorting in memory, which makes sense given how much faster RAM is than HDD.
As an alternative I used is to load the file in a database, then sort by the key I want (which only loads the key in memory) and then output the result into a file. It does go through disk but you can address larger files as you only need the key in memory, and not the whole file.
$ stat --printf="%s\n" p.csv
1258291200
$ time sort -t, -k1 -S100% -o sorted.csv p.csv
real 0m50,186s user 4m6,962s sys 0m4,562s
$ time sort -o sorted.csv p.csv
real 0m43,483s user 3m36,473s sys 0m4,282s
Edit: it finished!
real 35m28.370s
user 40m17.129s
sys 4m31.081s
You could extract it from BigQuery's bitcoin public data.
sort -f -s --batch-size=1024 -T/home LC_ALL=C sort --parallel=16 -t, -k1 -S100% -f -s --batch-size=1024 -T/home /tmp/test real 30m54.557s
user 82m9.055s
sys 3m1.723s
no improvement over sort without the batch-size...gzip input.csv -c | wc -c
versus
cat input | wc -l
?