faidx with bgzf compressed output#2067
Conversation
51780ec to
6053a5a
Compare
jkbonfield
left a comment
There was a problem hiding this comment.
I have more review to do still, but this was a very rapid first pass.
|
One thing that is noticable on fasta data is the compressability of DNA sequence is just poor with deflate. On human chromosome 1 libdeflate maximum compression gets 1.94 bits per base. It's barely better than a naive 2-bit encoding. Zlib's best is only 2.2bpb. I plotted both zlib (top) and libdeflate (bottom) together. Each point is a compression level from 1 to 9. We see level 4 on libdeflate is a sharp corner and beyond that it's really not worth while trying harder. With zlib (which ideally no one would use) it's somewhere between 4, 5 or 6. I think therefore the default compression level should perhaps be different here. Also, I noticed saying |
|
Curiously, if you drive zlib correctly then this also turns things on its head. zlib can be better than libdeflate which can be better than zstd! I expect zstd -19 would win on size, and there are lots of options in zstd to tweak too. Note also this is whole file compression rather than block based, but it's just an easy test to implement. The key is just accepting that LZ matches are hard to find in fasta files so there's little benefit (unless we're printing up many entries and they're all related). So RLE mode is usually best bang-for-buck. Zlib lets us set this directly, and ironically it offers far better compression ratios than the default strategy, even at maximum compression level. I'm not sure what we should do with this information though. Probably just ignore it. Our recommended approach is libdeflate anyway and that basically means just sticking with a modest compression level as the default to hit the sweet spot. Edit: added a few more entries, including a demonstration of what pure entropy encoding gives instead of LZ encoding. We can see hunting for LZ matches does help in the end (zstd -19 or xz -5 onwards), but it's extremely costly. I still think light weight compression (given this will be block based anyway so long-range matches wouldn't ever exist) is the correct setting. |
|
It looks good, but I still think the default compression level ought to be 4 rather than 6. The default is just a really bad trade-off in CPU vs size for fasta data. Suggestion: diff --git a/faidx.c b/faidx.c
index 3fb70ca4..dc2afd9a 100644
--- a/faidx.c
+++ b/faidx.c
@@ -456,7 +456,7 @@ int faidx_core(int argc, char *argv[], enum fai_format_options format)
} else {
hts_opt *opts = (hts_opt *)(out.gopt->out.specific);
char mode[13] = "w";
- int level = -1;
+ int level = 4;
while (opts) {
if (opts->opt == HTS_OPT_COMPRESSION_LEVEL) { //compression level
level = opts->val.i; |
|
Set the default level as 4 instead of -1/zlib default. |
|
There's some trailing white space in the man page that I was about to just force push up prior to merge (we don't need a discussion on it) and one minor slight rewording about default compression levels to avoid an implication that the default output style is compressed. However I also spotted something with the kstring so I'll comment on that elsewhere. The man page change was this: (You can't see the "the output will be " space, but it's there.) Trivial and we don't strictly check man pages, only code, but I spotted it in diff so thought I'd just tidy it during merging. |
|
updated makefile and made changes on kstring* to kstring. |

fixes #2055
fasta/fastq output of faidx/fqidx will be bgzf compressed if the output file has an extension of .gz/.bgz/.bgzf.
stdout will not be compressed.
also added extended option output-fmt-option to set compression levels and threads for bgzf compression.