Golfing Faster FASTA

After reading Timotimo's excellent Faster FASTA Please blog post, I wanted to test some of the available performance tradeoffs.

IO.slurp.lines

This is my baseline which incorporates Timotimo's performance improvements to the original solution, somewhat simplified.

  my %seqs;
  my $s = '';
  my $id;
  for 'genome.fa'.IO.slurp(:enc<latin1>).lines -> $line {
      if $line.starts-with('>') {
          if $id {
              %seqs{$id} = $s;
              $id = Nil;
          }
          $id = $line.substr(1);
          $s = '';
      } else {
          $s ~= $line;
      }
  }
  if $id {
      %seqs{$id} = $s;
  }
  say "Took { now - BEGIN now } seconds";
Took 3.58698513 seconds

IO.lines

What if we avoid using slurp? Hopefully IO.lines will manage to be faster.

  my %seqs;
  my $s = '';
  my $id;
  for 'genome.fa'.IO.lines(:enc<latin1>) -> $line {
      if $line.starts-with('>') {
          if $id {
              %seqs{$id} = $s;
              $id = Nil;
          }
          $id = $line.substr(1);
          $s = '';
      } else {
          $s ~= $line;
      }
  }
  if $id {
      %seqs{$id} = $s;
  }
  say "Took { now - BEGIN now } seconds";
Took 4.71259838 seconds

After a few runs, it seems to average out at being just a bit slower than slurping the file before iterating. But it has the advantage of avoiding the memory required for the whole file and should scale better for much larger files.

Split and Skip

This is my baseline for the second implementation in Timotimo's post.

  my %seqs = slurp('genome.fa', :enc<latin1>).split('>').skip(1).map: {
      .head => .skip(1).join given .split("\n").cache;
  }
  say "Took { now - BEGIN now } seconds";
Took 7.4847424 seconds

Racing Split and Skip

  my %seqs = slurp('genome.fa', :enc<latin1>).split('>').skip(1).race.map: {
      .head => .skip(1).join given .split("\n").cache;
  }
  say "Took { now - BEGIN now } seconds";
4.2423127

Hyper Split and Skip

  my %seqs = slurp('genome.fa', :enc<latin1>).split('>').skip(1).hyper.map: {
      .head => .skip(1).join given .split("\n").cache;
  }
  say "Took { now - BEGIN now } seconds";
Took 5.2303269 seconds
perl6  raku