Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Re^3: Fast - Compact That String

by mbethke (Hermit)
on Feb 10, 2012 at 05:12 UTC ( #952912=note: print w/replies, xml ) Need Help??

in reply to Re^2: Fast - Compact That String
in thread Fast - Compact That String

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.

Replies are listed 'Best First'.
Re^4: Fast - Compact That String
by BrowserUk (Pope) on Feb 10, 2012 at 05:34 UTC

    Nice!++ On my machine, both your routines are almost exactly twice as fast as mine.

    I get the table sizes as totaling around 400k: @c3:87792 @c4:295160, but that is a perfect trade for speed.

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

Re^4: Fast - Compact That String
by Limbic~Region (Chancellor) on Feb 10, 2012 at 14:49 UTC
    Very nice. I had to slightly modify it in order to get the sort order I wanted:
    my @c3 = map { my $c = $_; (map { "$c$_" } @c1) } @c1; sub myFromB37 { my $n = shift; my $s = ' '; substr( $s, 4, 2, $c3[$n % 1369] ); $n /= 1369; substr( $s, 2, 2, $c3[$n % 1369] ); $n /= 1369; substr( $s, 0, 2, $c3[$n] ); $s; } my @c4; $c4[unpack 'S', $c3[$_]] = $_ foreach 0 .. 1368; sub myToB37 { 1874161 * $c4[unpack 'S', substr($_[0], 0, 2)] + 1369 * $c4[unpack 'S', substr($_[0], 2, 2)] + $c4[unpack 'S', substr($_[0], 4, 2)]; }
    If you have time, I would really appreciate an in-depth explanation.

    Cheers - L~R

      Yeah, that's what I meant with reversing the bytes: BrowserUK's version generates "little-endian" strings, I just made mine produce the same to more easily check that I'm getting the correct results. To be a proper base37 number they should really have their most significant digit left.

      If you understand BrowserUK's version, mine is a relatively simple extension. fromB37 is probably easiest: first preallocate a string of six characters (faster than to keep appending characters if you know the final length anyway), then take the remainder of the current number divided by 37 and look up the digit to represent this with in @c1. Do this six times and you've converted the number.

      For the reverse conversion, you need a lookup table that has, under the index of a character code, the associated decimal value (ord(' ') => 0, ord('A') => 1, etc.). Then you have to split up the string into characters (the unpack is just a fast alternative to "split //"), and for each characters multiply the old value of $n by 37 and add the current character's "worth".

      What my version does different is just that it processes two characters at a time. @c3 is the cross product @c1x@c1, i.e. every possible 2-character string from ' ' to 'ZZ'. That way I don't have to modulo and divide by 37 six times but only thrice by 37^2. Surprisingly, not using a loop but three separate substr()s was quite a bit faster. Apparently Perl does some clever optimizations there if the string indexes are known at compile time.

      My unpack 'S' ... is just a "wider" version of ord() that works for two characters at a time by converting them to an unsigned short. So I use unpack to convert the 2-char string to an array index, and just like in BrowserUK's version, @c4 gives me the "worth" of that snippet. According to its position in the string I still have to multiply it by 37**4 and 37**2 respectively—for clarity I should probably have used these expressions instead of the "magic" numbers.

Re^4: Fast - Compact That String
by tobyink (Abbot) on Feb 10, 2012 at 20:28 UTC

    According to my benchmarking, this is still about 10% slower than my "bitshift" version. It's faster than my "multiplication" version though.

      I am confused by that statement. After eliminating the subroutine overhead (inlining everything), I can encode and decode 1 million strings in under 3 seconds. When I read that yours was taking 28 seconds I didn't even bother to check it. Can you please provide your benchmark code?

      Cheers - L~R

        The code I checked was the version posted, which did not inline subs. Obviously, inlining subs will speed up all the versions.

        My version averages about 27 seconds for a million strings on my computer which is a fairly low-powered netbook, several years old. Versus Re^3: Fast - Compact That String which averaged about 30 seconds for a million strings, on the same computer.

        Scripts I tested, including input data are available at

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://952912]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2017-03-25 00:23 GMT
Find Nodes?
    Voting Booth?
    Should Pluto Get Its Planethood Back?

    Results (310 votes). Check out past polls.