The method is pretty much optimal but a little loop unrolling and speed/memory tradeoff can again speed it up a bit (a bit over twice as fast for fromB37() and a good 50% faster for toB37()). Using a couple 100K extra, it probably depends on the CPU's cache size though. Only changed/extra parts:
my @c3 = map { my $c = $_; (map { "$_$c" } @c1) } @c1;
sub myFromB37 {
my $n = shift;
my $s = ' ';
substr( $s, 0, 2, $c3[$n % 1369] );
$n /= 1369;
substr( $s, 2, 2, $c3[$n % 1369] );
$n /= 1369;
substr( $s, 4, 2, $c3[$n] );
$s;
}
my @c4;
$c4[unpack 'S', $c3[$_]] = $_ foreach 0 .. 1368;
sub myToB37 {
1874161 * $c4[unpack 'S', substr($_[0], 4, 2)] +
1369 * $c4[unpack 'S', substr($_[0], 2, 2)] +
$c4[unpack 'S', substr($_[0], 0, 2)];
}
my @num_data = map int( rand 37**6 ), 1 .. 1e6;
my @asc_data = map " $_", 'AAAAA' .. 'CEXHN';
The last lines pre-generate test data because my version depends on strings being no less then 6 characters; the rest of the benchmarking is analogous to yours. You could save one "reverse" in your version though by putting the characters in reverse order, which they should be anyway to represent a normal left-to-right base37 number.