You said in another post

I'd like it to go as fast as possible and not restrict myself to a certain size, if possible

How critical is the need for speed? And how far are you prepeared to go to get it? Also, what platform are you on?

Your post made me revisit one of my back-boiler projects from a couple of years ago when I was playing with really big numbers and was disheartened by the lackadaisical performance of Math::Big*. From my assembler days, I remembered that many processors including the entire x86 range support BCD (unpacked) math opcodes and wondered how doing math with them would compare. The assembler was easy, but XS was a complete mystery at that point and I abandoned it for future attention, but not before proving that it ran rather faster than anything else I had tried from Perl.

The following only implements increment (Add with a constant of 1); will only work if you are set up for Inline::C using an MS compiler, but it shows rather dramatically how fast a full library could be. The assembler should be readily convertible to other compiler formats running on x86 so conversion to run on many Linux platforms shouldn't be onerous.

The difference in performance between using the BCD opcodes and Perl's string increment is so dramatic that I had to scale the work done by the BCD by a factor of 1000:1. Without this weighting factor, by the time the iteration count was high enough for the Benchmark module to stop complaining about too few iterations for the BCD version, the runtime for the magic increment version was so long (days!), that it was longer than I was prepared to wait. So, when the benchmark shows MAGIC as being 320% faster than BCD, the reality is that the BCD code runs 26700% faster. And when it says the BCD code is 2273% faster, it is really 2273000%!

Yes, I know those number seem ridiculous, but I've been sitting here for a couple of hours running over them, and I ran another benchmark with the internal multipliers set to the same value (1..1e4), and the results produced seem to bear out my math:

c:\test>mathbcd Comparing incrementing 100_000 digit numbers; 10,000 iterations. (warning: too few iterations for a reliable count) s/iter magic bcd magic 1.66 -- -100% bcd 6.20e-003 26740% -- 1000000000 ... 0000099999 1000000000 ... 0000099999 Comparing incrementing 1_000_000 digit numbers; 10,000 iterations (warning: too few iterations for a reliable count) s/iter magic bcd magic 17.4 -- -100% bcd 9.40e-003 185072% -- 1000000000 ... 0000099999 1000000000 ... 0000099999 Comparing incrementing 10_000_000 digit numbers; 10,000 iterations (warning: too few iterations for a reliable count) ** The BCD finishes in a second or so, but I gave up waiting for the magic to finish after it had thrashed my machine for more than an hour + **

If you doubt the results, please run your own tests :)

Now I've got a handle on the XS memory shuffling, I may have a go at implementing the full + - / * and see how they compare. Anyone interested (particlarly in writing a Linux version)?

The code and (weighted) benchmark results:

#! perl -slw package Math::BCD; use strict; use Inline C => << 'EOC', NAME => 'Math::BCD', CLEAN_AFTER_BUILD => 0; void _prep( SV *self ) { U32 len; char *p = SvPV( SvRV( self ), len ); for( ; len--; *p++ -= 48 ); } void inc( SV *self ) { U32 l; SV *n = SvRV( self ); char *a = SvPV( n, l ); if( *a == 9 ) { if( SvOOK( n ) && ( SvIV( n ) > 0 ) ) { SvLEN_set( n, SvLEN( n ) +1 ); SvCUR_set( n, ++l ); SvPV_set( n, --a ); SvIVX( n )--; } else { char pad[100] = { 0, }; sv_insert( n, 0, 0, pad, 100 ); sv_chop( n, SvPVX( n ) + 99 ); a = SvPVX( n ); ++l; } } a += l - 1; _asm { pushad; mov esi, a; mov al, 1; add al, [esi]; aaa; mov [esi], al jnc $1; $0: dec esi; adc al, [esi]; aaa; mov [esi], al; jc $0; $1: popad; } return; } SV *asString( SV *self ) { SV *copy = newSVsv( SvRV( self ) ); U32 l; char *p = SvPV( copy, l ); for( ; l--; *p++ += 48 ); return copy; } EOC sub new { my( $class, $init ) = @_; my $self = bless \$init, $class; $self->_prep; return $self; } package main; use Benchmark qw[ cmpthese ]; for my $len ( 1e5, 1e6, 1e7 ) { our $number = '9' x $len; our $bcd = new Math::BCD( $number ); print "\nComparing incrementing $len digit numbers"; print "10 thousand iterations of Perl's magic string increment aga +inst"; print "10 million iterations of using BCD and assembler"; cmpthese 10, { magic => q[ ++$number for 1 .. 1e3 ], bcd => q[ $bcd->inc for 1 .. 1e6 ], }; print substr( $number, 0, 10 ), ' ... ', substr $number, + -10; print substr( $bcd->asString, 0, 10 ), ' ... ', substr $bcd->asStr +ing, -10; } __END__ c:\test>mathbcd Comparing incrementing 100000 digit numbers 10 thousand iterations of Perl's magic string increment against 10 million iterations of using BCD and assembler Rate bcd magic bcd 1.48/s -- -76% magic 6.22/s 320% -- 1000000000 ... 0000009999 1000000000 ... 0009999999 Comparing incrementing 1000000 digit numbers 10 thousand iterations of Perl's magic string increment against 10 million iterations of using BCD and assembler s/iter magic bcd magic 1.72 -- -60% bcd 0.695 148% -- 1000000000 ... 0000009999 1000000000 ... 0009999999 Comparing incrementing 10000000 digit numbers 10 thousand iterations of Perl's magic string increment against 10 million iterations of using BCD and assembler s/iter magic bcd magic 17.2 -- -96% bcd 0.723 2273% -- 1000000000 ... 0000009999 1000000000 ... 0009999999

Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

In reply to Re: Incrementing a large number without Math::BigInt by BrowserUk
in thread Incrementing a large number without Math::BigInt by Cap'n Steve

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.