John zerocopy#214
Conversation
sampsyo
left a comment
There was a problem hiding this comment.
Awesome; this is looking good already! I have a few suggestions around the edges—please let me know where I can clarify further.
There was a problem hiding this comment.
Let's leave the VS Code settings out of the repo, probably?
There was a problem hiding this comment.
All these pubs and pub(crate)s seem fine! To resolve the conflict, maybe try rebasing on the current main branch and perahps applying these access modifiers again?
There was a problem hiding this comment.
We actually already have a main.rs, under the src/cli directory. You'll notice that this is organized around subcommands: for example, fgfa paths or fgfa stats or fgfa bed. Instead of adding a new main.rs with a new main function, let's just add a new subcommand there! Or perhaps two—we could call them seq-import and seq-export, for example.
| #[derive(FromBytes, FromZeroes, AsBytes, Debug)] | ||
| #[repr(packed)] | ||
| pub struct PackedToc { | ||
| magic: u8, |
There was a problem hiding this comment.
I know this is a silly thing, but you probably want the magic number to be an entire word, i.e., a u64. Here are two reasons:
- There are just too many collisions in the space of a
u8! That is, au64that you pick is likely to be unique; au8that you pick is likely to be the same as someone else'su8magic number. - Using a single byte at the start of the file will make the rest of the data unaligned. If the magic number is a
u64, then the next field will start on a word-aligned boundary.
| pub struct PackedToc { | ||
| magic: u8, | ||
| data: Size, | ||
| high_nibble_end: Size, |
There was a problem hiding this comment.
This strategy stores the high_nibble_end field after the actual sequence data, and it uses a "pointer/span" to allow for any amount of bytes to be stored there. But, unlike for the sequence data, we know that we only ever need to store a single byte (or actually, a single bit)! So it would probably be more sensible to just store this right here, in the table of contents.
In other words, we could just make this field have type bool or u8 or or u64 or something, instead of Size. And instead of using that Size to refer to data stored elsewhere in the file, we just store the flag right here, in the TOC. Would that make sense?
| slice.vec_ref.as_ref().get_range(slice.span) | ||
| } | ||
|
|
||
| pub fn total_bytes(num_elems: usize) -> usize { |
There was a problem hiding this comment.
Not 100% sure about this one, but perhaps this API could be a tiny bit friendlier if it took a PackedSeqStore or PackedSeqView as an argument. It would of course just immediately use .len() to get the size, but it would make it a little clearer to callers that they're supposed to use this to get the file size for one of these data structures.
| } | ||
|
|
||
| /// Check if the pool is empty. | ||
| /// Check if th e pool is empty. |
| if args.len() > 2 && args[1] == "import" { | ||
| let mmap = memfile::map_file(&args[2]); // args[2] is the filename | ||
| let seq = packedseq::view(&mmap); | ||
| println!("Sequence: {}", seq); |
There was a problem hiding this comment.
I say let's just print the raw seq itself, without the Sequence: prefix. This will make the CLI friendlier for use in larger pipelines.
| let vec = PackedSeqStore::create(vec![ | ||
| Nucleotide::A, | ||
| Nucleotide::C, | ||
| Nucleotide::T, | ||
| Nucleotide::G, | ||
| ]); |
There was a problem hiding this comment.
Instead of constructing a specific sequence here, let's read a text file from standard input! That is, we'd like to do something like this:
$ cat myseq.txt | fgfa seq-export myseq.seq
where myseq.txt contains text like ACTGTGGACCAATG or whatever. In other words, let's make a CLI here that can convert between two file types: text files with one nucleotide per character, and our brand-new binary sequence data format.
There was a problem hiding this comment.
A standalone shell script is perfectly fine, and maybe the right tool for the job here. The "next level up" for testing this that I had in mind was to set up Turnt, along the lines of what we already do for round-trips from GFA to FlatGFA:
Lines 162 to 172 in 8a4fd75
Basically, the idea would be to create a little tests directory here and to populate it with a few small text sequence files. Then, a Turnt config would look something like this:
command = "fgfa seq-import < {filename} | fgfa seq-export"
output.txt = "-"This would test that, starting with the original text file, a round trip through importing and exporting (I forget which is which!) eventually produces exactly the same .txt file, byte for byte. Would that strategy make sense?
cafa33b to
247847e
Compare
|
It seems like this PR is now outdated—can we close it? |
|
In doing a little bit of prep for the Panorama presentation today, I thought I'd do some quick & dirty benchmarking of the current compressed version. Sadly, we're currently "underwater": the compressed version seems to be both larger and slower than the current main branch? I used this sequence of commands to do a quick test: And I did that for both branches. The sizes of the files are:
And running that I'm not shocked that the speed is a bit slower (there is overhead when accessing compressed data), but I think we should probably get to the bottom of why the file size is larger? |
|
Superseded by #241. |
So far I've created a system for writing and reading compressed sequence data from files. I've created a Rust unit test for this, as well as a command-line test case in a Bash file (running code in a new main.rs file). The current merge conflicts seem to stem from me making certain functions (and the Size struct) in file.rs public.