|
Boost : |
From: Brian McNamara (lorgon_at_[hidden])
Date: 2004-03-02 05:31:36
Gennadiy,
I didn't reply to your earlier message about FC++ performance, because I
agreed with your major point (or what I thought was your major point).
That is, the tw_xxx.cpp example programs/benchmarks which compare FC++
performance and Haskell performance have no business being in Boost, as
C++ users will not care about comparisons with Haskell.
However, I think you are implying that FC++ lazy evaluation is very
slow, and this is not true; see below.
On Tue, Mar 02, 2004 at 03:44:43AM -0500, Gennadiy Rozental wrote:
> 2. There is nothing cool about class designed and implemented the way so it
> works 320 times slower then similar solution written in different style. And
> I am not talking about minor issues related to abstraction overhead and
> portability workarounds. There should be something wrong in the very heart
> of the design, for this to happened.
There is a misunderstanding here. The "similar solution" you posted is
not similar at all. Whereas the FC++ version computes primality by
creating a list of all the integers from 1 to N, then filtering out all
of the factors of N, and then comparing this list to the list [1,N],
your solution simply tests for divisors in a loop from 2..N/2.
The whole point of the tw_primes.cpp example was to use (or abuse) HOFs
and lazy evaluation at every point in the computation (in order to mimic
a Haskell program which did the same) so as to create a microbenchmark
which stress-tests these aspects of the implementation. The algorithm
chosen for the primality test is absurdly inefficient; it was chosen
because the straightforward/efficient approach does not stress-test the
desired elements.
Below I am attaching a revised version of your code and the FC++ code,
where the FC++ version uses your primality test. The FC++ version
still computes an "infinite list of primes" and then just inspects the
Nth element. But this version runs nearly as fast as your version on
my machine (there's about a 2% difference).
Again, the summary point is that it was the choice of algorithm in the
example (and not the library design/implementation) which made the
example so slow.
That said, it's still all my fault. :) This misunderstanding is just
another example of my own failure to create documentation/examples
which are useful to C++ers (and instead presenting docs/examples
tailored to FPers).
Revised code:
----------------------------------------------------------------------
#define BOOST_FCPP_ENABLE_LAMBDA
#include "prelude.hpp"
using namespace boost::fcpp;
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
// Gennadiy's code
bool is_prime( int i ) {
for( int c = 2; c <= i/2; ++c ) {
if( i % c == 0 )
return false;
}
return true;
}
int nth_prime( int N ) {
int i = 1;
while( N > 0 ) {
i++;
if( is_prime( i ) )
N--;
}
return i;
}
// My revised code, which uses is_prime() for the primality test
struct Prime : public c_fun_type<int,bool> {
bool operator()( int x ) const {
return is_prime(x);
}
} prime;
struct Primes : public c_fun_type<int,odd_list<int> > {
odd_list<int> operator()( int n ) const {
return take( n, filter( prime, enum_from(1) ) );
}
} primes;
int main() {
int i, N, t;
cin >> N;
{
Timer timer; // please substitute your own timing mechanism
i = nth_prime( N );
t = timer.ms_since_start();
cout << t << " " << i << endl;
}
{
Timer timer;
i = at( primes( N+1 ), N+1 );
t = timer.ms_since_start();
cout << t << " " << i << endl;
}
}
----------------------------------------------------------------------
-- -Brian McNamara (lorgon_at_[hidden])
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk