faster integer arithmetics & arbitrary precision floating number - Ruby

1. Is there a way in Ruby to speed up 32bit integer arithmetics (only involving numbers & sums up to 2^32-1)? I want to use Ruby for summarizing network traffic logs, but it's pathetically slow compared to Perl: \$ time ruby -e'1000000.times{1073741823+1073741824}' real 0m23.693s user 0m5.720s sys 0m0.610s \$ time perl -e'for(1..1000000){1073741823+1073741824}' real 0m1.142s user 0m0.320s sys 0m0.050s since 2**30 is already in the Bignum range. 2. Doing arbitrary integer math is already very convenient in Ruby because of its automatic conversion. But Ruby still doesn't do seamless conversion to arbitrary floating point numbers: \$ irb irb(main):001:0> 0.00000000000000001 => 1.0e-17 ...

1. faster integer arithmetics & arbitrary precision floating number

1. Is there a way in Ruby to speed up 32bit integer arithmetics (only
involving numbers & sums up to 2^32-1)? I want to use Ruby for
summarizing network traffic logs, but it's pathetically slow compared to
Perl:

\$ time ruby -e'1000000.times{1073741823+1073741824}'

real 0m23.693s
user 0m5.720s
sys 0m0.610s
\$ time perl -e'for(1..1000000){1073741823+1073741824}'

real 0m1.142s
user 0m0.320s
sys 0m0.050s

since 2**30 is already in the Bignum range.

2. Doing arbitrary integer math is already very convenient in Ruby
because of its automatic conversion. But Ruby still doesn't do seamless
conversion to arbitrary floating point numbers:

\$ irb
irb(main):001:0> 0.00000000000000001
=> 1.0e-17
irb(main):002:0> 0.000000000000000001
=> 0.0

Any chance Ruby will do this in the future? Or perhaps in the nearer
future, include an arbitrary floating number package in its distribution
(is there any? GMP is GPL so it potentially a problem license-wise).

--
dave

David Guest 2. Re: faster integer arithmetics & arbitrary precision floating number

David Garamond wrote:

i just tried to run this, and this is really quite discouraging indeed..
:O/ even though i was personnally never hurt by ruby's speed (yet).

emmanuel

Emmanuel Guest 3. Re: faster integer arithmetics & arbitrary precision floating number

One idea would be to make a "FastInt" class that stores inegers as regular C
int's. It could work like this:

int1 = FastInt.new(1073741823)
int2 = FastInt.new(073741824)

1000000.times{ int1 + int2 }

So addition would be done in C, which would be faster.

Notice: If you create the integers about as often as you add them, the
conversion from Ruby to C and back would more than compensate for any gains
from C (I suspect).

I would expec that this:

1000000.times do
int1 = FastInt.new(1073741823)
int2 = FastInt.new(073741824)
int1 + int2
end

would be even slower.

Cheers,
Daniel.

On Tue, Jan 13, 2004 at 12:09:53AM +0900, David Garamond wrote:

--
Daniel Carrera | No trees were harmed in the generation of this e-mail.
PhD student. | A significant number of electrons were, however, severely
Math Dept. UMD | inconvenienced.

Daniel Guest 4. Re: faster integer arithmetics & arbitrary precision floating number

>>>>> "E" == Emmanuel Touzery <fr> writes:

E> David Garamond wrote: [/ref]
[...]

E> i just tried to run this, and this is really quite discouraging indeed..
E> :O/ even though i was personnally never hurt by ruby's speed (yet).

just use a faster cpu

svg% time ruby -e'1000000.times{1073741823+1073741824}'

real 0m2.638s
user 0m2.631s
sys 0m0.008s
svg%

Guy Decoux

ts Guest 5. Re: faster integer arithmetics & arbitrary precision floating number

David Garamond wrote:

i have the feeling (unconfirmed) that perl is optimising it away since
the result is unused or the operation repeated or something. i created a
file with 1000000 lines repeating the addition and perl is very slow too
then (and ruby too).

i'm not sure.

emmanuel

Emmanuel Guest 6. Re: faster integer arithmetics & arbitrary precision floating number

> >\$ time perl -e'for(1..1000000){1073741823+1073741824}'
>
> i have the feeling (unconfirmed) that perl is optimising it away since
> the result is unused or the operation repeated or something. i created a
> file with 1000000 lines repeating the addition and perl is very slow too
> then (and ruby too).[/ref]

That seems likely.

Which brings up another point, shouldn't ruby have such an optimization
also?

Yes, I know that you can redefine the Integer class so that + does something
weird, which means that you don't know what each + does until you are
actually there. But suppose that there were a Ruby flag where I "promise" I
haven't done anything odd to the numeric classes, and it is safe to optimize
them. Wouldn't that be a good idea?

--
Daniel Carrera | No trees were harmed in the generation of this e-mail.
PhD student. | A significant number of electrons were, however, severely
Math Dept. UMD | inconvenienced.

Daniel Guest 7. Re: faster integer arithmetics & arbitrary precision floating number

>>>>> "E" == Emmanuel Touzery <fr> writes:

E> i have the feeling (unconfirmed) that perl is optimising it away since
E> the result is unused or the operation repeated or something. i created a
E> file with 1000000 lines repeating the addition and perl is very slow too
E> then (and ruby too).

the P language make a constant (1073741823+1073741824) and compute it only
once

Guy Decoux

ts Guest 8. Re: faster integer arithmetics & arbitrary precision floating number

ts wrote:

reminds me of Perl's very powerful pragmas:

use less time;
use more cpu;

--
dave

David Guest 9. Re: faster integer arithmetics & arbitrary precision floating number

On Tue, 13 Jan 2004, David Garamond wrote:

if there is someway you can organized your problem into arrays (eg. collecting
the item to sum into one), narray might be the way to go, it offer blindingly
fast numerical operations:

~ > time ruby -r narray -e 'NArray.int(1000000)[true] = 1073741823+1073741824'

real 0m0.025s
user 0m0.010s
sys 0m0.010s

~ > time perl -e'for(1..1000000){1073741823+1073741824}'

real 0m0.128s
user 0m0.130s
sys 0m0.000s

in fact, the operations are fast enough that you might simply be able to use
normal ruby objects and ignore the Fixnum/Bignum distinction - which is a very
useful (accurate) abstraction:

~ > irb -r narray
irb(main):001:0> (na=NArray.object(1000000))[true] = 1073741823+1073741824
=> 2147483647

irb(main):002:0> a=Time.now; p(na.sum); b=Time.now; b.to_f - a.to_f
2147483647000000
=> 0.882834911346436

not bad for summing a million numbers with arbitrary precision eh?

-a
--

================================================== =============================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
| STP :: http://www.ngdc.noaa.gov/stp/
| NGDC :: http://www.ngdc.noaa.gov/
| NESDIS :: http://www.nesdis.noaa.gov/
| NOAA :: http://www.noaa.gov/
| US DOC :: http://www.commerce.gov/
|
| The difference between art and science is that science is what we
| understand well enough to explain to a computer.
| Art is everything else.
| -- Donald Knuth, "Discover"
|
| /bin/sh -c 'for l in ruby perl;do \$l -e "print \"\x3a\x2d\x29\x0a\"";done'
================================================== =============================

Ara.T.Howard@noaa.gov Guest 10. Re: faster integer arithmetics & arbitrary precision floating number

Emmanuel Touzery wrote:

do you mean you did:

#!/usr/bin/{perl,ruby}
1234567+123456;
1234567+123456;
1234567+123456;
1234567+123456;
1234567+123456;
...

?

then have you factored out compilation overhead? I would wild-guess that
Perl is slightly slower than Ruby, due to its complex syntaxes?

--
dave

David Guest 11. Re: faster integer arithmetics & arbitrary precision floating number

In article <fr>,
Emmanuel Touzery <fr> wrote:
>
>i have the feeling (unconfirmed) that perl is optimising it away since
>the result is unused or the operation repeated or something. i created a
>file with 1000000 lines repeating the addition and perl is very slow too
>then (and ruby too).
>
>i'm not sure.[/ref]

You are correct about perl optimising away a constant -

[mikeratdog mike]\$ perl -MO=Dep -e 'for(1..1000000){1073741823+1073741824}'
foreach \$_ (1 .. 1000000) {
'???';
}
-e syntax OK

??? is B::Dep's way of showing something that's been optimised away.
If you make perl do some work then it does slow down e.g.

[mikeratdog mike]\$ time perl -e 'for(1..1000000){1073741823+1073741824}'
0.12user 0.00system 0:00.26elapsed 45%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (297major+36minor)pagefaults 0swaps
[mikeratdog mike]\$ time perl -e 'for(1..1000000){\$_+1073741824}'
0.24user 0.00system 0:00.24elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (300major+36minor)pagefaults 0swaps
[mikeratdog mike]\$ perl -MO=Dep -e 'for(1..1000000){\$_+1073741824}'
foreach \$_ (1 .. 1000000) {
\$_ + 1073741824;
}
-e syntax OK

Hope this helps,

Mike

--
co.uk | The "`Stok' disclaimers" apply.
http://www.stok.co.uk/~mike/ | GPG PGP Key 1024D/059913DA
com | Fingerprint 0570 71CD 6790 7C28 3D60
http://www.exegenix.com/ | 75D2 9EC4 C1C0 0599 13DA
Mike Guest 12. Re: faster integer arithmetics & arbitrary precision floating number

"Daniel Carrera" <umd.edu> schrieb im Newsbeitrag
news:umd.edu...
regular C
gains

Of course you would define FastInt#add which adds something to the current
instance, removing the overhead of object creation.

Cheers

robert
[/ref]
to [/ref]
seamless [/ref]
distribution
>
> --
> Daniel Carrera | No trees were harmed in the generation of this e-mail.
> PhD student. | A significant number of electrons were, however,[/ref]
severely

Robert Guest 13. Re: faster integer arithmetics & arbitrary precision floating number

gov wrote:

Nice! I can surely try clustering the numbers into sizable bites of
Narray's before summing them up together.

Thanks,
--
dave

David Guest 14. Re: faster integer arithmetics & arbitrary precision floating number

David Garamond <6.isreserved.com> wrote in message news:<6.isreserved.com>...

No, Perl is much faster than ruby for these sort of operations. Where
Perl's speed suffers is in its OO (lots of nested classes can bring
Perl down to its knees not to mention make the code very hard to deal
with).

Python2.2 is now an extremely good compromise for both, as it has
great performance for simple things and it scales very well on complex
projects. But I cannot stand its syntax, personally.

Ruby is, for these type of operations, quite slow. It is somewhat
akin to the old python1.5 overall. Ruby is in my opiniong the
language that has the nicest syntax of them all, but both its speed
and library base is not on par with others.

In the python mailing list someone recently sent out a mail with a
silly benchmark like that (which I ported to perl and ruby, too).
"Nine Language Performance Round-up: Benchmarking Math & File I/O"
http://www.osnews.com/story.php?news_id=5602

This benchmark is not that great as it does not measure that many
other areas of a language that you use in real code, but I guess it is
okay if you want to get an idea of arithmetic speed.

The top performer on that benchmark from the scripting languages is
C#. And I was quite surprised that microsoft did something decent
this time around (albeit C# is perhaps closer to C++ in philosophy and
syntax than to a scripting language such as perl, ruby or python).

On the machine I am on (XP) (and reducing the # of iterations of each
one), I got:

C#: 560 milliseconds
Perl5.8.1: 5.08 sec.
Python2.2.3: 6.07115513285 sec.
Ruby1.8: 14.170000 sec.

Indeed, Ruby seems particularly bad with normal integer arithmetic.

Here's the not very scientific code for perl and ruby I used which I
ported from python (you can get the rest from the guy's website to
test). Since ruby deals with int/longs/bignums transparently, I am
not sure the test below is a good example, thou of long behavior. But
it does give you a rough idea where ruby stands in overall number
crunching performance.

#! /usr/bin/ruby

require "benchmark"
include Benchmark

intMax = 10000000000 # 1B
doubleMin = 10000000000.0 # 10B
doubleMax = 11000000000.0 # 11B
longMin = 10000000000 # 10B
longMax = 11000000000 # 11B
trigMax = 10000000.0 # 10M
ioMax = 1000000 # 1M

# I used these numbers to test as the orig. ones take too long
intMax = 10000000 # 1B
doubleMin = 10000000.0 # 10B
doubleMax = 11000000.0 # 11B
longMin = 10000000 # 10B
longMax = 11000000 # 11B
trigMax = 100000.0 # 10M
ioMax = 10000 # 1M

def intArithmetic(intMax)

i = 1
intResult = 1
while i < intMax
intResult = intResult - i
i = i + 1
intResult = intResult + i
i = i + 1
intResult = intResult * i
i = i + 1
intResult = intResult / i
i = i + 1
end
print " i:", i, "\n"
print " intResult:", intResult, "\n"
end

def doubleArithmetic(doubleMin, doubleMax)

i = doubleMin
doubleResult = doubleMin
while i < doubleMax
doubleResult = doubleResult - i
i = i + 1.0
doubleResult = doubleResult + i
i = i + 1.0
doubleResult = doubleResult * i
i = i + 1.0
doubleResult = doubleResult / i
i = i + 1.0
end
print " i:", i, "\n"
print " doubleResult:", doubleResult, "\n"
end

def longArithmetic(longMin, longMax)

i = longMin
longResult = longMin
while i < longMax
longResult = longResult - i
i = i + 1
longResult = longResult + i
i = i + 1
longResult = longResult * i
i = i + 1
longResult = longResult / i
i = i + 1
end
print " i:", i, "\n"
print " Result:", longResult, "\n"
end

def trig(trigMax)
i = 1.0
sine = 0.0
cosine = 0.0
tangent = 0.0
logarithm = 0.0
squareRoot = 0.0

while i < trigMax
sine = Math.sin(i)
cosine = Math.cos(i)
tangent = Math.tan(i)
logarithm = Math.log10(i)
squareRoot = Math.sqrt(i)
i = i + 1.0
end
print " i:", i, "\n"
print " sine:", sine, "\n"
print " cosine:", cosine, "\n"
print " tangent:", tangent, "\n"
print " logarithm:", logarithm, "\n"
print " squareRoot:", squareRoot, "\n"
end

def io(ioMax)
fileName = "TestRuby.txt"
myString = "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklm nopqrstuvwxyz1234567890abcdefgh\n"

linesToWrite = [myString]
for i in 2..ioMax
linesToWrite.push( myString )
end

file = File.open(fileName, 'w')
file.puts(linesToWrite)
file.close()

file = File.open(fileName, 'r')
file.close()
print "write=",linesToWrite.length()
end

# Main program begins here

puts "Start Ruby benchmark"

t = 0
benchmark(" " + CAPTION, 7, FMTSTR) do |x|
t = x.report("intArithmetic") { intArithmetic(intMax) }
t += x.report("doubleArithmetic") { doubleArithmetic(doubleMin,
doubleMax) }
t += x.report("longArithmetic") { longArithmetic(longMin, longMax)
}
t += x.report("trig") { trig(trigMax) }
t += x.report("io") { ioTime = io(ioMax) }
end

print "Total Ruby benchmark time:", t, "\n"
puts "End Ruby benchmark"

#####################For perl...
#! /usr/bin/perl
use Benchmark qw( timeit timestr );
use Math::Trig; #for tan

my \$intMax = 1000000000; # 1B
my \$intMax = 10000000; # 1B
my \$doubleMin = 10000000.0; # 10B
my \$doubleMax = 11000000.0; # 11B
my \$longMin = 10000000; # 10B
my \$longMax = 11000000; # 11B
my \$trigMax = 100000.0; # 10M
my \$ioMax = 10000; # 1M

sub intArithmetic(\$)
{
my (\$intMax) = _;

my \$i = 1;
my \$intResult = 1;
while (\$i < \$intMax)
{
\$intResult = \$intResult - \$i;
\$i++;
\$intResult = \$intResult + \$i;
\$i++;
\$intResult = \$intResult * \$i;
\$i++;
\$intResult = int \$intResult / \$i;
\$i++;
}
print " i:", \$i, "\n";
print " intResult:", \$intResult, "\n";
}

sub doubleArithmetic(\$\$)
{
my (\$doubleMin, \$doubleMax) = _;

my \$i = \$doubleMin;
my \$doubleResult = \$doubleMin;
while (\$i < \$doubleMax)
{
\$doubleResult = \$doubleResult - \$i;
\$i = \$i + 1.0;
\$doubleResult = \$doubleResult + \$i;
\$i = \$i + 1.0;
\$doubleResult = \$doubleResult * \$i;
\$i = \$i + 1.0;
\$doubleResult = \$doubleResult / \$i;
\$i = \$i + 1.0;
}
print " i:", \$i, "\n";
print " doubleResult:", \$doubleResult, "\n";
}

sub longArithmetic
{
my (\$longMin, \$longMax) = _;

my \$i = \$longMin;
my \$longResult = \$longMin;
while (\$i < \$longMax)
{
\$longResult = \$longResult - \$i;
\$i = \$i + 1;
\$longResult = \$longResult + \$i;
\$i = \$i + 1;
\$longResult = \$longResult * \$i;
\$i = \$i + 1;
\$longResult = \$longResult / \$i;
\$i = \$i + 1;
}
print " i:", \$i, "\n";
print " longResult:", \$longResult, "\n";
}

sub log10 { log(\$_)/log(10); }

sub trig(\$)
{
my (\$trigMax) = _;
my \$i = 1.0;
my \$sine = 0.0;
my \$cosine = 0.0;
my \$tangent = 0.0;
my \$logarithm = 0.0;
my \$squareRoot = 0.0;

while (\$i < \$trigMax)
{
\$sine = sin(\$i);
\$cosine = cos(\$i);
\$tangent = tan(\$i);
\$logarithm = log10(\$i);
\$squareRoot = sqrt(\$i);
\$i = \$i + 1.0;
}
print " i:", \$i, "\n";
print " sine:", \$sine, "\n";
print " cosine:", \$cosine, "\n";
print " tangent:", \$tangent, "\n";
print " logarithm:", \$logarithm, "\n";
print " squareRoot:", \$squareRoot, "\n";
}

sub io(\$)
{
my (\$ioMax) = _;
my \$fileName = "TestPerl.txt";
my \$myString = "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklm nopqrstuvwxyz1234567890abcdefgh\n";
my \$linesToWrite = \$myString;
for \$i ( 1..\$ioMax )
{
\$linesToWrite .= \$myString;
}

open(FILE, ">".\$fileName);
print FILE \$linesToWrite;
close(FILE);

open(FILE, "<".\$fileName);
my readLines = <FILE>;
close(FILE);
}

# Main program begins here

print "Start Perl benchmark\n";

my \$total= timeit( 1, sub
{
my \$t = timeit( 1, sub{intArithmetic(\$intMax); } );
print "intArithmetic: ",timestr(\$t),"\n";
\$t = timeit( 1, sub{doubleArithmetic(\$doubleMin,\$doubleMax); } );
print "doubleArithmetic: ",timestr(\$t),"\n";
\$t = timeit( 1, sub{doubleArithmetic(\$longMin,\$longMax); } );
print "longArithmetic: ",timestr(\$t),"\n";
\$t = timeit( 1, sub{trig(\$trigMax); } );
print "trig: ",timestr(\$t),"\n";
\$t = timeit( 1, sub{io(\$ioMax); } );
print "io: ",timestr(\$t),"\n";
}
);

print "total=",timestr(\$total),"\n";

print "End Perl benchmark";
GGarramuno Guest 15. Re: faster integer arithmetics & arbitrary precision floating number

On Monday 12 of January 2004 16:59, David Garamond wrote:
>
> do you mean you did:
>
> #!/usr/bin/{perl,ruby}
> 1234567+123456;
> 1234567+123456;
> 1234567+123456;
> 1234567+123456;
> 1234567+123456;
> ...
>
> ?
>
> then have you factored out compilation overhead? I would wild-guess that
> Perl is slightly slower than Ruby, due to its complex syntaxes?[/ref]

i did try with a file as you said, but due to a slow computer i had to kill it
before the end. when i first ran it, i saw perl was slow, which seemed to
confirm this 'perl is caching the result' intuition, i didn't think about the
syntax parsin overhead :O)

now i tried on a faster computer:

[emmanuelpapillon emmanuel]\$ time perl test.rbpl
10.63user 1.01system 0:35.49elapsed 32%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (6628major+98402minor)pagefaults 0swaps
[emmanuelpapillon emmanuel]\$ time ruby test.rbpl
test.rbpl:8127:in `+': Bignum can't be coerced into Fixnum (TypeError)
from test.rbpl:8127
Command exited with non-zero status 1
26.86user 0.71system 0:28.19elapsed 97%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (254major+51518minor)pagefaults 0swaps

i'm not really sure why ruby is choking on line 8127, all lines are identical
but anyway it shows that in any case ruby is still much slower than perl, so
nevermind what i said. as far as i'm concerned, it's not sure at all that
perl is saving the calculation. my initial impression was simply caused by
the huge difference between ruby and perl.

emmanuel

PS: if someone is interested in this ruby error, the file is too big to send,
but is so that:
[emmanuelpapillon emmanuel]\$ uniq test.rbpl
1073741823+1073741824;

[emmanuelpapillon emmanuel]\$ wc -l test.rbpl
1000001 test.rbpl

PPS: great trick with NArray.. :O)

Emmanuel Guest 16. Re: faster integer arithmetics & arbitrary precision floating number

On Tue, Jan 13, 2004 at 04:47:59AM +0900, Emmanuel Touzery wrote:

jimbo:~> ruby1.8 ./test1.rbpl
/test1.rbpl:6250: stack level too deep (SystemStackError)
33.665u 1.132s 0:35.33 98.4% 0+0k 0+0io 269pf+0w

jimbo:~> ruby1.8 --version
ruby 1.8.1 (2003-12-27) [i386-linux]

jimbo:~> ruby1.8 -r rbconfig -e "puts Config::CONFIG['configure_args']"
'--enable-frame-address' '--target=i386-linux' '--program-suffix=1.8' '--prefix=/usr' '--datadir=/usr/share' '--mandir=/usr/share/man' '--sysconfdir=/etc' '--localstatedir=/var' '--with-sitedir=/usr/local/lib/site_ruby' '--with-default-kcode=none' '--with-dbm-type=gdbm_compat' '--with-tklib=tk8.4' '--with-tcllib=tcl8.4' '--with-tcl-include=/usr/include/tcl8.4' '--enable-pthread' '--enable-shared' '--enable-ipv6' '--with-lookup-order-hack=INET' 'CFLAGS=-Wall -g -O2' 'target_alias=i386-linux'

debian 1.8.1-2, though I verified the effect of
--enable-pthread with current cvs as well.

-neil

Neil Guest 17. Re: faster integer arithmetics & arbitrary precision floating number

gov wrote:

But isn't this only performing the addition once?

Joel Guest 18. Re: faster integer arithmetics & arbitrary precision floating number

What abouts Rubys design would make integer arithmetic slower than integer
arithmetic in Perl?
--
Charlie

Charles Guest 19. Re: faster integer arithmetics & arbitrary precision floating number

On Jan 12, 2004, at 15:43, Charles Mills wrote:

class Fixnum
def +(other)
self.to_s + other.to_s
end
end

puts 1 + 2 #=> "12"

Ruby has to do full OO method dispatch for basic arithmetic operators.

Cheers

Dave

Dave Guest 20. Re: faster integer arithmetics & arbitrary precision floating number

On Tue, 13 Jan 2004, Dave Thomas wrote:

>
> class Fixnum
> def +(other)
> self.to_s + other.to_s
> end
> end
>
> puts 1 + 2 #=> "12"
>
>
> Ruby has to do full OO method dispatch for basic arithmetic operators.
>[/ref]
I guess I was expecting the addition of Fixnums to be implemented in C
(unless of course you override it).

Charles Guest  Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•