Bignum package implementation in C++/OO question

M

mike3

Hi.

I once got the following response to a query of mine here:
http://groups.google.com/group/comp.lang.c++/msg/b80993cb0f735810?dmode=source

Now, after trying this approach, with all these "Bignum" and "slice"
objects to handle
my primitives, I found the performance was dismal. I need maximum
performance.

What I'm wondeirng about is why not keep the passed length parameters
and offload the
checks to whatever routine is calling these primitives, as they are
just that: primitive
operations designed to be used to build the more sophisticated
operations. Writing
a full-on integer package (the approach I ended up adopting based on
these suggestions)
and then never using it for anything but to build a floating-point
package has proven
not only inefficient performance-wise, but bloated with functionality
I don't use. All that
abstraction seems to eat more time than the underlying digit operation
itself!

So my idea is this:

1. Write primitives with length parameters (add, sub, copy, etc. all
work on runs of the
same length, mul and div on differing lengths.). These primitives take
as operands
iterators from an std::vector<> and work on that.

2. Use those *primitives* to build more complex BigInt and BigFloat
classes, which
implement all bounds-checking. This eliminates redundant bounds-checks
in an "RawInt"
class used to build the Float, for example, as well as integer-only
assumptions, things
that make the code messy, complicated, and poor in performance.

What exactly is so bad about this approach?

In the case of the methods presented, we have this thing that creates
several "slices"
of an object, does the boundschecking there (never mind that the
initial selections done
in the floating-point operation contain boundschecking by their very
nature), and then we
feed those to our bignum routines, then do the operations, then
destroy these, and then
we repeat this billions and billions of times in the calculation loop.
With very long
operands (1024 bits or more) this does not really matter, but long
operands like that I
do not expect to use often since they are intrinsically time-consuming
to operate upon.
Shorter operands -- 128 bits or so -- are more common, and here I
notice a big performance
hit.

What is the best bignum-package design for maximum performance?
 
P

peter koch

Hi.

I once got the following response to a query of mine here:http://groups.google.com/group/comp.lang.c++/msg/b80993cb0f735810?dmo...

Now, after trying this approach, with all these "Bignum" and "slice"
objects to handle
my primitives, I found the performance was dismal. I need maximum
performance.
[snip unread]

Sorry for answering a thread I haven't read! I did read some of your
previous posts, however.
Did you attempt to find a publicly available BigNum package and test
the performance of that package against your own implementation? That
could give you some interesting answers.

/Peter
 
M

mike3

I once got the following response to a query of mine here:http://groups.google.com/group/comp.lang.c++/msg/b80993cb0f735810?dmo...
Now, after trying this approach, with all these "Bignum" and "slice"
objects to handle
my primitives, I found the performance was dismal. I need maximum
performance.

[snip unread]

Sorry for answering a thread I haven't read! I did read some of your
previous posts, however.

This new thread has only one message, so what, you didn't read
that message? I'm sorry, but I'm confused.
Did you attempt to find a publicly available BigNum package and test
the performance of that package against your own implementation? That
could give you some interesting answers.

Yes, I did: GMP, the GNU Bignum package (MP = Multiple Precision
package.).
It's written in C, not C++, however. And it does something similar to
what I
mentioned, with explicitly-passed length parameters in the core
primitives.
Which is why I was wondering what the reason for the disapproval of
this
approach was. Boundschecking can easily be implemented in the routines
that _use_ those primitives. They're just primitives used to build
more complex
routines that actually handle bignum objects and the primitives are
not meant
to be used directly.

For example, in GMP, mpf_add calls the mpn_add primitive, which
accepts three pointers to the digit data that is being worked on, plus
two length
parameters. So that's why I thought that for my attempt I should have
a primitive
that takes three vector iterators (instead of raw pointers as this is C
++, not C)
plus a length parameter. I was also thinking of having a special "safe
iterator" that
could be passed that would feature toggleable boundschecking (you can
toggle it
off by #defining/undefining the right preprocessor directive. So for
debug builds you
can flip it on, for maximum performance you can flip it off.). None of
that weird
"slice" stuff is used.
 
A

alex

mike3 said:
I once got the following response to a query of mine here:
http://groups.google.com/group/comp.lang.c++/msg/b80993cb0f735810?dmode=source

Now, after trying this approach, with all these "Bignum" and "slice"
objects to handle
my primitives, I found the performance was dismal. I need maximum
performance.
Did you profile your code? Is a particular basic operation slow?
What function(s) are taking most of the time?

On a more general note, is this a toy / learning exercise or do
you just need a working package? If you are happy with the
performance of an existing package (e.g. GMP) but want a different
interface why don't you wrap it the way you like (then make a short
writeup to explain what you did and why, and make your work public).
 
P

peter koch

[snip unread]
Sorry for answering a thread I haven't read! I did read some of your
previous posts, however.

This new thread has only one message, so what, you didn't read
that message? I'm sorry, but I'm confused.

As I told you, I've read some of your posts in older, related
messages.
Yes, I did: GMP, the GNU Bignum package (MP = Multiple Precision
package.).
It's written in C, not C++, however. And it does something similar to
what I
mentioned, with explicitly-passed length parameters in the core
primitives.

Great! So how was the performance? If the performance is similar to
the performance of your own library, (and you can't readily find a
faster library), the indication is that your library is as fast as can
be. If your library is slower, you probably has optimal code. Only
way to improve is by improving algorithm - e.g. by reducing
generality. If your own code is slower, it might be a good idea to use
the code that is already optimized and probably rather bugfree.
In all cases, I presume you did profile your code so that you know the
bottlenecks?
[snip]

/Peter
 
M

mike3

I once got the following response to a query of mine here:http://groups.google.com/group/comp.lang.c++/msg/b80993cb0f735810?dmo...
Now, after trying this approach, with all these "Bignum" and "slice"
objects to handle
my primitives, I found the performance was dismal. I need maximum
performance.
[snip unread]
Sorry for answering a thread I haven't read! I did read some of your
previous posts, however.
This new thread has only one message, so what, you didn't read
that message? I'm sorry, but I'm confused.

As I told you, I've read some of your posts in older, related
messages.

But you said "sorry for answering a thread I haven't read" in relation
to answering my original message on this thread, which is strange
since this is a new thread with just that one message.
Great! So how was the performance? If the performance is similar to
the performance of your own library, (and you can't readily find a
faster library), the indication is that your library is as fast as can
be.  

Performance was better than mine, in generic mode. Also, my original
bignum package (which was critiqued for having maintainability
problems
here, see a thread titled "Bug in my C++ program seems really strange"
in the Google archives. The new attempts were a reaction to this but
performance seems to have suffered and I didn't like that!) was faster
than this new one.
If your library is slower, you probably has optimal code. Only
way to improve is by improving algorithm - e.g. by reducing
generality. If your own code is slower, it might be a good idea to use
the code that is already optimized and probably rather bugfree.
In all cases, I presume you did profile your code so that you know the
bottlenecks?
[snip]

Anyway, guess what: it seems I lost the new package in a botched
system upgrade. Oh well! (I still have a copy of the rest of the code
of the program, though.) So I'll need to make a new one, and therefore
I want to find out how to write the code "good" and yet also be very
fast.
 
M

mike3

Did you profile your code? Is a particular basic operation slow?
What function(s) are taking most of the time?

On a more general note, is this a toy / learning exercise or do
you just need a working package? If you are happy with the
performance of an existing package (e.g. GMP) but want a different
interface why don't you wrap it the way you like (then make a short
writeup to explain what you did and why, and make your work public).

Need a working package, but I don't like the licensing/copyright
issues
involved with using someone else's as I have not made up my mind on
what sort of license I intend to release this program under.
 
M

mike3

I once got the following response to a query of mine here:http://groups.google.com/group/comp.lang.c++/msg/b80993cb0f735810?dmo...
Now, after trying this approach, with all these "Bignum" and "slice"
objects to handle
my primitives, I found the performance was dismal. I need maximum
performance.
[snip unread]
Sorry for answering a thread I haven't read! I did read some of your
previous posts, however.
This new thread has only one message, so what, you didn't read
that message? I'm sorry, but I'm confused.
As I told you, I've read some of your posts in older, related
messages.

But you said "sorry for answering a thread I haven't read" in relation
to answering my original message on this thread, which is strange
since this is a new thread with just that one message.






Great! So how was the performance? If the performance is similar to
the performance of your own library, (and you can't readily find a
faster library), the indication is that your library is as fast as can
be.  

Performance was better than mine, in generic mode. Also, my original
bignum package (which was critiqued for having maintainability
problems
here, see a thread titled "Bug in my C++ program seems really strange"
in the Google archives. The new attempts were a reaction to this but
performance seems to have suffered and I didn't like that!) was faster
than this new one.
If your library is slower, you probably has optimal code. Only
way to improve is by improving algorithm - e.g. by reducing
generality. If your own code is slower, it might be a good idea to use
the code that is already optimized and probably rather bugfree.
In all cases, I presume you did profile your code so that you know the
bottlenecks?
[snip]

Anyway, guess what: it seems I lost the new package in a botched
system upgrade. Oh well! (I still have a copy of the rest of the code
of the program, though.) So I'll need to make a new one, and therefore
I want to find out how to write the code "good" and yet also be very
fast.

So is it good or not to make such "primitives" that do not implement
boundschecking themselves, but the higher-up routines that use the
primitives do do such checking?
 
M

mike3

I once got the following response to a query of mine here:http://groups.google.com/group/comp.lang.c++/msg/b80993cb0f735810?dmo...
Now, after trying this approach, with all these "Bignum" and "slice"
objects to handle
my primitives, I found the performance was dismal. I need maximum
performance.
[snip unread]
Sorry for answering a thread I haven't read! I did read some of your
previous posts, however.
This new thread has only one message, so what, you didn't read
that message? I'm sorry, but I'm confused.
As I told you, I've read some of your posts in older, related
messages.
But you said "sorry for answering a thread I haven't read" in relation
to answering my original message on this thread, which is strange
since this is a new thread with just that one message.
Performance was better than mine, in generic mode. Also, my original
bignumpackage (which was critiqued for having maintainability
problems
here, see a thread titled "Bug in my C++ program seems really strange"
in the Google archives. The new attempts were a reaction to this but
performance seems to have suffered and I didn't like that!) was faster
than this new one.
If your library is slower, you probably has optimal code. Only
way to improve is by improving algorithm - e.g. by reducing
generality. If your own code is slower, it might be a good idea to use
the code that is already optimized and probably rather bugfree.
In all cases, I presume you did profile your code so that you know the
bottlenecks?
[snip]
Anyway, guess what: it seems I lost the new package in a botched
system upgrade. Oh well! (I still have a copy of the rest of the code
of the program, though.) So I'll need to make a new one, and therefore
I want to find out how to write the code "good" and yet also be very
fast.

So is it good or not to make such "primitives" that do not implement
boundschecking themselves, but the higher-up routines that use the
primitives do do such checking?

Any answers?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top