From: David Hanley
Subject: A program in both lisp and C
Date: 
Message-ID: <36E9BD1C.B9E12E96@ncgr.org>
I had a project which required me to simulate a community of organisms.
The result had to be in C++, but I prototyped it in lisp, because i felt
this would help me due to it's interactivity, and features like bounds
checking, etc.  I got the lisp version done in about 2 hours, and got it
working quite well.  It me nearly as long to translate it to C++ as it
took me to write it in lisp in the first place. It had a couple core
dumps, and some things didn't translate directly.  But when I got it to
work, I decided to benchmark them against eachother.  Here is the
result:

Lisp version : 616 seconds
C++ version : 771 seconds.

Both results are the average of five tests, run on an unloaded sparc-20.

The lisp and C compilers were both set at maximum optimizations.
The C++ compiler was gcc, and the lisp compiler was CMUCL.

I tried to make sure the C++ version was optimal.  I used to get paid to
write
tweaked(fast)  C code. Yes, it could be tweaked more, but not with the
flexibility constraints I am working under.  Both are doing pretty
much the exact same thing, except I simulated closures in lisp as
virtual
functions.  I also used an extra class, because I was having a few
problems
with levels of pointer.  Not a bad trade-off, but it adds to code
size( a few lines ),
but should not affect speed.

Here is the WC of both:

     421    1416    7847 project.cc
     256     789    6243 project.lsp

In general, the lisp code is more flexible (IMO).  If speed hadn't been
an issue,
the lisp code could have been much smaller and easier to write.  The
first
lisp version was about half the size of the current code, but much
slower.  The extra
size came from changing maps to loops, adding type declarations, etc.

I was worried that the random number function was a bottleneck, and that

the lisp version might be faster, so I wrote some test programs which
simply calculated 10 million random numbers, half 0-13, half 1-17.
Here are the results:

C++ version : 5.75 seconds
lisp version : 9.89 seconds.

Obviously, this is not hurting the C code. Again, this is the average of
five
tests on a sparc-20.

I thought this was a pretty interesting exercise, and I'll bring it up
next time someone tells me lisp is slow.  :)  There may be project
reasons not to use it, but speed is not one of them!

finally, here is the code:

-------------------C++-----------------------
#include <iostream.h>
#include <stdlib.h>
#include <assert.h>

class Gene;
class Organism;

int testing = 1;

inline void swap( Organism **arr , int p1 , int p2 )
{
    Organism *tmp = arr[ p1 ];
    arr[ p1 ] = arr[ p2 ];
    arr[ p2 ] = tmp;
}


// ;;;;;;
// ;;;
// ;;; basic data definitions
// ;;;
// ;;;;;;


inline int fastrand( int x )
{
    return rand() % x;
}


class Gene
{
public:
    int g1;
    int g2;
};


int randomAllelle( Gene *g )
{
    if ( fastrand(2) == 0 )
      return g->g1;
    else
      return g->g2;
}


Gene *createGene( int sz )
{
    Gene *g = new Gene;
    g->g1 = fastrand( sz );
    g->g2 = fastrand( sz );
    return g;
}


class Organism
{

  Gene **dna;
  int nc;

public:

  Organism(  int nchromo , int range )
    {
      Gene **arr = new Gene *[ nchromo ];
      for( int index = 0 ; index < nchromo ; ++ index )
 {
   arr[ index ] = createGene( range );
 }
      dna = arr;
      nc = nchromo;
    }

  ~Organism()
    {
      for( int a = 0 ; a < nc ; ++ a )
 {
   delete dna[ a ];
 }
      delete [] dna;
    }

  inline Gene *allelle( int i )
    {
      //assert( i >= 0 && i < nc );
      return dna[ i ];
    }
};

// ;;;;;;;;;;;;
// ;;;
// ;;; construct a child's genetic set from the genes
// ;;; of a parent.
// ;;; Takes 3 organsims, all of the same type
// ;;; the lists should all be composed of gene structures
// ;;;
// ;;;;;;;;;;;;


void createChild( Organism *parent1 , Organism *parent2 , Organism
*child , int len )
{
    for( int ng = 0 ; ng < len ; ++ ng )
      {
 child->allelle( ng )->g1 = randomAllelle( parent1->allelle( ng ) );
 child->allelle( ng )->g2 = randomAllelle( parent2->allelle( ng ) );
      }
}


// ;;;;;;;;;;;;
// ;;;
// ;;; construct a community of organisms
// ;;;
// ;;;;;;;;;;;;


Organism **createCommunity( int size , int nchromo , int range )
{
  Organism **arr = new Organism *[ size ];
  for( int i = 0 ; i < size ; ++ i )
    {
      arr[ i ] = new Organism( nchromo , range );
    }
  return arr;
}

// ;;;;;;;;;;;;
// ;;;
// ;;; from a pool of parents, with a certian number living,
// ;;; create a child community of fixed size
// ;;;
// ;;;;;;;;;;;;


void createNextGen( Organism **parents , int parentsLiving , Organism
**children ,
                    int childrenToMake , int ng )
{
    for( int childToMake = 0 ; childToMake < childrenToMake ; ++
childToMake )
      {
 createChild( children[ childToMake ] ,
       parents[ fastrand( parentsLiving ) ] ,
       parents[ fastrand( parentsLiving ) ] , ng );
      }
}


// ;;;;;;;;;;;;;
// ;;;
// ;;; using a "killing function" kill off a few members of a
population,
// ;;; moving dead ones to the end of the array.  Return the
// ;;; new population size.
// ;;;
// ;;;;;;;;;;;;;


class Cull
{
public:
    virtual int cull( Organism *o ) = 0;
};

int populationCull( Organism **population , int popSize , Cull *c )
{
  int index = 0;
  while ( index < popSize )
    {
      if ( c->cull( population[ index ] ) )
 {
   --popSize;
   swap( population , index , popSize );
 }
      else
 {
   ++index;
 }
    }
  return popSize;
}

// ;;;;;;;;
// ;;;
// ;;; various culling functions
// ;;;
// ;;; if the function returns TRUE it kills the organsim
// ;;;
// ;;;;;;;;


class RandomCuller : public Cull
{
  int percent;
public:
  RandomCuller( int p )
    {
      this->percent = p;
    }

  virtual int cull( Organism *o )
    {
      if ( testing )
 return 0;

      return fastrand( 100 ) > percent;
    }
};



// ;;;;;;;;
// ;;; kills half of the ones which do not match
// ;;;;;;;;


class CullerTemplate : public Cull
{
  int chromo;
  int value;
public:
  CullerTemplate( int c , int v )
    {
      this->chromo = c;
      this->value = v;
    }

  virtual int cull( Organism *o )
    {
      if ( testing )
 return 0;
      if ( fastrand( 2 ) == 0 )
 return 0;
      if ( o->allelle( chromo )->g1 == value )
 return 1;
      return 0;
    }
};

// ;;;;;;;;
// ;;;
// ;;;  few cullers
// ;;;
// ;;;;;;;;



Cull *culler1 = new CullerTemplate( 3 , 2 );

Cull *culler2 = new CullerTemplate( 2 , 1 );

Cull *culler3 = new CullerTemplate( 1 , 3 );


// ;;;;;;;;;
// ;;;
// ;;; measure the biodiversity of a population
// ;;;
// ;;;;;;;;;

int calculateAllelleDiversity( Organism ** community , int communitySize
, int allelle ,
                               int chromoRange )
{

  int *arr = new int[ chromoRange ];
  int sum = 0;
  for( int a = 0 ; a < chromoRange ; ++ a )
    {
      arr[ a ] = 0;
    }



  for( int i = 0 ; i < communitySize ; ++ i )
    {
      Organism *organism = community[ i ];

      Gene *gene = organism->allelle( allelle );

      ++arr[ gene->g1 ];
      ++arr[ gene->g2 ];
    }

  for( int a = 0 ; a < chromoRange ; ++ a )
    {
      if ( arr[ a ] > 0 )
 ++sum;
    }
  return sum;
}


int measureDiversity( Organism **community , int communitySize , int
allelles , int chromoRange )
{
  //cout << "md" << endl;
  int sum = 0;
  for( int i = 0 ; i < allelles ; ++ i )
    {
      sum += calculateAllelleDiversity( community , communitySize , i ,
chromoRange );
    }
  return sum;
}

// ;;;;;;;;;
// ;;;
// ;;; carry out a generation, returning how many are left afterwards.
// ;;;
// ;;;;;;;;;



int populationAdd( int curr , int max )
{
  if ( curr < max )
    {
      curr = (int)(curr * 1.08);
      if ( curr > max )
 curr = max;
    }
    return curr;
}



int nextGen( Organism **parents , int ps , int max , Organism **children
, int ng )
{
    int ns = populationAdd( ps , max );
    createNextGen( parents , ps , children , ns , ng );
    return ns;
}

Cull **makeKillerArray( Cull **disasters , int disNum , int mult , int
&numCulls )
{
  numCulls = disNum * mult;

  Cull **killers = new Cull*[ numCulls ];
  for( int a = 0 ; a < numCulls ; ++ a )
    {
      if ( a < disNum )
 killers[ a ] = disasters[ a ];
      else
 killers[ a ] = new RandomCuller( 97 + fastrand( 3 ) );
    }
  return killers;
}


void runSimulation( int communitySize , int numChromos , int
chromoDiversity , int numberGenerations ,
      Cull **disasters , int numDisasters , int gensPerDisaster )
{
  Organism **gen1 = createCommunity( communitySize , numChromos ,
chromoDiversity );
  Organism **gen2 = createCommunity( communitySize , numChromos ,
chromoDiversity );
  int max = communitySize;

  int numKillers;
  Cull **killerArray = makeKillerArray( disasters , numDisasters ,
gensPerDisaster , numKillers );

  cout << "Diversity : " << measureDiversity( gen1 , communitySize ,
numChromos , chromoDiversity ) <<
    " size : " << communitySize << endl;

  for( int i = 0 ; i < numberGenerations ; ++ i )
    {
      communitySize = populationCull( gen1 , communitySize ,
      killerArray[ fastrand( numKillers ) ] );
    communitySize = nextGen( gen1 , communitySize , max , gen2 ,
numChromos );

    Organism **gen = gen1;
    gen1 = gen2;
    gen2 = gen;
  }

  cout << "Diversity : " << measureDiversity( gen1 , communitySize ,
numChromos , chromoDiversity ) <<
    " size : " << communitySize << endl;

  for( int a = 0 ; a < communitySize ; ++ a )
    {
      delete gen1[ a ];
      delete gen2[ a ];
    }
  delete [] gen1;
  delete [] gen2;

  // I don't know why, but the following was causing a core dump
  //
  //for( int a = 0 ; a < numKillers ; ++ a )
  //  {
  //    delete killerArray[ a ];
  //  }
  //delete [] killerArray;
}


Cull *cullers[] = { culler1 , culler2 , culler3 };
int numCullers = 3;



void sim1()
{
  runSimulation( 1000 , 4 , 4 , 1000 , cullers , numCullers , 10 );
}

void sim2()
{
  runSimulation( 200 , 4 , 4 , 200 , cullers , numCullers , 10 );
}

void sim3()
{
  runSimulation( 10000 , 4 , 4 , 1000 , cullers , numCullers , 10 );
}

int main( int argc , char *argv[] )
{
  int a;
  for( int a = 0 ; a < 3 ; ++ a )
    {
      sim1();
      sim2();
      sim3();
    }
}






---------------------lisp----------------------------

(declaim (optimize (speed 3)(safety 0)))

;;;;;
;;
;; Genetic simulation in lisp
;;
;;;;;

(defparameter testing T)

;;;;;
;;
;; utils
;;
;;;;;

(declaim (inline swap))

(defmacro while(test &rest body)
  `(do ()
    ((not ,test))
   ,@body))

(defun swap( arr p1 p2 )
  (rotatef (svref arr p1) (svref arr p2)))

;;;;;
;;
;; basic data definitions
;;
;;;;;

(declaim (inline fastrand random-allelle create-gene))

(defun fastrand(x)
  (declare (type (integer 1 2147483647) x))
  (the fixnum (random x)))

(defstruct gene
  (g1 0 :type fixnum)
  (g2 0 :type fixnum))

(defun random-allelle( source-gene )
  (if (= (random 2) 0)
      (gene-g1 source-gene)
      (gene-g2 source-gene)))

(defun create-gene( sz )
  (make-gene :g1 (fastrand sz) :g2 (fastrand sz)))

(defun create-organism( nchromo range )
  (declare (type fixnum nchromo range))
  (let (
  (arr (make-array (list nchromo))))
    (dotimes (index nchromo)
      (declare (type fixnum index))
      (setf (svref arr index) (create-gene range)))
    arr))

;;;;;;;;;;;
;;
;; construct a child's genetic set from the genes
;; of a parent.
;; Takes 3 organsims, all of the same type
;; the lists should all be composed of gene structures
;;
;;;;;;;;;;;

(defun create-child( parent1 parent2 child len )
  (dotimes (ng len)
    (declare (type fixnum ng len))
    (setf (gene-g1 (svref child ng))
   (random-allelle (svref parent1 ng)))
    (setf (gene-g2 (svref child ng))
   (random-allelle (svref parent2 ng)))
    ))

;;;;;;;;;;;
;;
;; construct a community of organisms
;;
;;;;;;;;;;;

(defun create-community( size nchromo range )
  (declare (type fixnum size nchromo range))
  (let ((arr (make-array (list size))))
    (dotimes (i size)
      (declare (type fixnum i))
      (setf (svref arr i) (create-organism nchromo range)))
    arr))

;;;;;;;;;;;
;;
;; from a pool of parents, with a certian number living,
;; create a child community of fixed size
;;
;;;;;;;;;;;

(defun create-next-gen( parents parents-living children children-to-make
ng )
  (declare (type fixnum parents-living children-to-make ng))
  (dotimes (child-to-make children-to-make)
    (create-child (svref children child-to-make)
    (svref parents (fastrand parents-living))
    (svref parents (fastrand parents-living)) ng)))

;;;;;;;;;;;;
;;
;; using a "killing function" kill off a few members of a population,
;; moving dead ones to the end of the array.  Return the
;; new population size.
;;
;;;;;;;;;;;;

(defun population-cull( population pop-size killer)
  (declare (type fixnum pop-size))
  (let ((index 0))
    (declare (type fixnum index))
    (while (< index pop-size)
      (if (funcall killer (svref population index))
   (progn
     (decf pop-size)
     (swap population index pop-size))
   (incf index)))
    pop-size))

;;;;;;;
;;
;; various culling functions
;;
;; if the function returns TRUE it kills the organsim
;;
;;;;;;;


(defun random-culler( percentage-to-live )
  #'(lambda(x)(if testing nil (> (random 100) (the fixnum
percentage-to-live)))))


;;;;;;;
;; kills half of the ones which do not match
;;;;;;;
(defun culler-template( chromo value )
  #'(lambda(indiv)(if testing nil (and (not (= (the fixnum (gene-g1
(svref indiv chromo)))
          (the fixnum value)))
         (= (random 2) 0)))))

;;;;;;;
;;
;; a few cullers
;;
;;;;;;;


(defun culler-1()
  (culler-template 3 2))

(defun culler-2()
  (culler-template 2 1))

(defun culler-3()
  (culler-template 1 3))

;;;;;;;;
;;
;; measure the biodiversity of a population
;;
;;;;;;;;

(defun calculate-allelle-diversity( community community-size allelle
chromo-range)
  (declare (type fixnum community-size allelle chromo-range))
  (let ((arr (make-array (list chromo-range))) (cnt 0))
    (declare (type fixnum cnt))
    (dotimes (i community-size)
      (declare (type fixnum i))
      (let ((gene (svref (svref community i) allelle)))
 (incf (the fixnum (svref arr (gene-g1 gene))))
 (incf (the fixnum (svref arr (gene-g2 gene))))))
    (map nil #'(lambda(x)(if (> (the fixnum x) 0)(incf cnt))) arr)
    cnt))

(defun measure-diversity( community community-size allelles
chromo-range)
  (declare (type fixnum community-size allelles chromo-range))
  (let ((sum 0))
    (declare (type fixnum sum))
    (dotimes (i allelles)
      (declare (type fixnum i))
      (incf sum (the fixnum (calculate-allelle-diversity community
community-size i chromo-range))))
    sum))

;;;;;;;;
;;
;; carry out a generation, returning how many are left afterwards.
;;
;;;;;;;;


(defun population-add( curr max )
  (declare (type fixnum curr max))
  (the fixnum (if (< curr max)
     (min max (floor (* curr 1.08)))
     curr)) )

(defun next-gen( parents ps max children ng )
  (let ((ns (population-add ps max)))
  (create-next-gen parents ps children ns ng) ns))

(defun make-killer-array( disasters mult )
  (let ((l (* (length disasters) mult)))
    (dotimes (i l)
      (push (random-culler (+ (random 3) 97)) disasters)))
  (make-array (list (length disasters)) :initial-contents disasters))


(defun run-simulation( community-size nchromos chromo-diversity
          number-generations disasters gens-per-disaster)
  (labels (
    (cc ()(create-community community-size nchromos chromo-diversity))
    (ps (g cs) (format T "Diversity: ~d , size ~d ~%"
         (measure-diversity g community-size nchromos chromo-diversity)
cs)))
    (let (
   (gen1 (cc))
   (gen2 (cc))
   (max community-size)
   (killer-array (make-killer-array disasters gens-per-disaster)))
      (ps gen1 community-size)
      (dotimes (i number-generations)
 (setf community-size
          (population-cull gen1 community-size
            (svref killer-array (random (length killer-array)))))
 ;(format T "bg ~d " community-size)
 (setf community-size (next-gen gen1 community-size max gen2 nchromos))
 ;(format T "  ag ~d ~%" community-size)
 (rotatef gen1 gen2))
      (ps gen1 community-size)
      )
    )
)

(defparameter killers (list (culler-1) (culler-2) (culler-3)))

(defun sim1()
  (run-simulation 1000 4 4 1000 killers 10))

(defun sim2()
  (run-simulation 200 4 4 200 killers 10))

(defun sim3()
  (run-simulation 10000 4 4 1000 killers 10))

(defun bench()
  (dotimes (i 3)
    (sim1)
    (sim2)
    (sim3)))

From: Christopher R. Barry
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <87d82em7bl.fsf@2xtreme.net>
David Hanley <···@ncgr.org> writes:

> Lisp version : 616 seconds

Boring Friday night, nothing to do (other than 1-1/2 pages more math
homework, I estimate). I ran it on a P200MMX:

CMUCL

  227.78 seconds of real time
  214.45 seconds of user run time
  0.18 seconds of system run time
  [Run times include 0.36 seconds GC run time]
  2725 page faults and
  6747936 bytes consed.

Allegro CL 5

; cpu time (non-gc) 614,060 msec (00:10:14.060) user, 50 msec system
; cpu time (gc)     1,940 msec user, 30 msec system
; cpu time (total)  616,000 msec (00:10:16) user, 80 msec system
; real time  650,021 msec (00:10:50.021)
; space allocation:
;  474 cons cells, 0 symbols, 8,900,096 other bytes, 0 static bytes

I really burns under CMU for some reason, or performs poorly under
Allegro CL 5. Whether the glass is half-empty or half-full in this
case, I don't know.

Enough screwing around, back to work....

Christopher
From: Paul F. Dietz
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <36E9E298.BD1B98D2@interaccess.com>
> I really burns under CMU for some reason, or performs poorly under
> Allegro CL 5. Whether the glass is half-empty or half-full in this
> case, I don't know.

I think CMU CL can figure out that the array variables
really are arrays, and optimize the svrefs.  Try it
again in ACL with type declarations for the array
variables.

	Paul
From: Andrew Koenig
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <F8JK3D.BJs@research.att.com>
In article <·················@ncgr.org>, David Hanley  <···@ncgr.org> wrote:
> It me nearly as long to translate it to C++ as it
> took me to write it in lisp in the first place. It had a couple core
> dumps, and some things didn't translate directly.

> Lisp version : 616 seconds
> C++ version : 771 seconds.

I am not surprised that a C++ transliteration of a Lisp program
would not much faster than the original Lisp program compiled on
a good compiler.  I don't consider the degree of difference to
indicate all that much, though, because differences between individual
implementations are likely to be greater -- in both languages.

What would be more interesting would be to see whether an
implementation of the same algorithm from scratch in C++ would
be faster.

> Here is the WC of both:

>      421    1416    7847 project.cc
>      256     789    6243 project.lsp

What's interesting, though, is that if I remove from the C++ version
all lines that are either completely blank or that contain only { or },
I am left with 259 lines.  Moreover, I suspect that the C++ version
could be made significantly smaller by using the library more.

I think this experiment is an interesting first step, and encourage
deeper investigation.
-- 
				Andrew Koenig
				···@research.att.com
				http://www.research.att.com/info/ark
From: Marco Antoniotti
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <lw1zis41ln.fsf@copernico.parades.rm.cnr.it>
···@research.att.com (Andrew Koenig) writes:

> What's interesting, though, is that if I remove from the C++ version
> all lines that are either completely blank or that contain only { or },
> I am left with 259 lines.  Moreover, I suspect that the C++ version
> could be made significantly smaller by using the library more.

Come on. Also the CL version contains lines which could be gotten rid
of.

> 
> I think this experiment is an interesting first step, and encourage
> deeper investigation.

This is sure!
·······@copernico:genetics 56> g++ --version
2.7.2.2
·······@copernico:genetics 57> g++ gen.C
gen.C:155: warning: ANSI C++ forbids declaration `population' with no type or storage class
gen.C:163: parse error before `{'
gen.C:168: type specifier omitted for parameter
gen.C:168: parse error before `*'
gen.C: In function `int populationCull(...)':
gen.C:171: `popSize' undeclared (first use this function)
gen.C:171: (Each undeclared identifier is reported only once
gen.C:171: for each function it appears in.)
gen.C:173: `c' undeclared (first use this function)
gen.C:173: confused by earlier errors, bailing out


and, (already with some tweaking).....

CMU Common Lisp 18b, running on copernico
Send bug reports and questions to your local CMU CL maintainer, 
<·······@parades.rm.cnr.it> or to
<··········@cons.org> and <··········@cons.org>
Loaded subsystems:
    Python 1.0, target SPARCstation/Solaris 2
    CLOS based on PCL version:  September 16 92 PCL (f)
    CLX X Library MIT R5.02
    Motif toolkit and graphical debugger 1.0
* (compile-file "gen.lisp")
Python version 1.0, VM version SPARCstation/Solaris 2 on 14 MAR 99 02:48:36 pm.
Compiling: /users/marcoxa/lang/cl/tests/genetics/gen.lisp 14 MAR 99 02:49:03 pm


File: /users/marcoxa/lang/cl/tests/genetics/gen.lisp

In: DEFUN FASTRAND
  (RANDOM X)
Note: Unable to use inline (unsigned-byte 32) operations because:
      The range is too large to assure an accurate result.


In: DEFUN CREATE-GENE
  (FASTRAND SZ)
--> BLOCK THE 
==>
  (RANDOM X)
Note: Unable to use inline (unsigned-byte 32) operations because:
      The range is too large to assure an accurate result.
[Last message occurs 2 times]


In: DEFUN CREATE-ORGANISM
  (CREATE-GENE RANGE)
--> BLOCK MAKE-GENE FASTRAND BLOCK THE 
==>
  (RANDOM X)
Note: Unable to use inline (unsigned-byte 32) operations because:
      The range is too large to assure an accurate result.
[Last message occurs 2 times]


In: DEFUN CREATE-NEXT-GEN
  (FASTRAND PARENTS-LIVING)
--> BLOCK THE 
==>
  (RANDOM X)
Note: Unable to use inline (unsigned-byte 32) operations because:
      The range is too large to assure an accurate result.
[Last message occurs 2 times]


In: DEFUN POPULATION-CULL
  (FUNCALL KILLER (SVREF POPULATION INDEX))
--> C::%FUNCALL IF 
==>
  (KERNEL:%COERCE-TO-FUNCTION FUNCTION)
Note: Unable to optimize because:
      Might be a symbol, so must call FDEFINITION at runtime.


In: DEFUN RANDOM-CULLER
  #'(LAMBDA (X) (UNLESS TESTING (> # #)))
Warning: Variable X defined but never used.

  (RANDOM 100)
--> REM BLOCK MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL 
==>
  (TRUNCATE NUMBER KERNEL::DIVISOR)
Note: Doing unsigned word to integer coercion (cost 20) from KERNEL::Y.


In: DEFUN CALCULATE-ALLELLE-DIVERSITY
  (PLUSP (SVREF ARR I))
==>
  (> (SVREF ARR I) 0)
Note: Unable to optimize due to type uncertainty:
      The first argument is a REAL, not a FLOAT.
Note: Unable to optimize due to type uncertainty:
      The first argument is a REAL, not a INTEGER.
Note: Forced to do GENERIC-> (cost 10).
      Unable to do inline fixnum comparison (cost 3) because:
      The first argument is a REAL, not a FIXNUM.
      Unable to do inline fixnum comparison (cost 4) because:
      The first argument is a REAL, not a FIXNUM.
      etc.


In: DEFUN POPULATION-ADD
  (FLOOR (* CURR 1.08))
==>
  (FLOOR (* CURR 1.08) 1)
Note: Doing float to pointer coercion (cost 13).


In: DEFUN MAKE-KILLER-ARRAY
  (LENGTH DISASTERS)
Note: Unable to optimize due to type uncertainty:
      The first argument is a (OR VECTOR CONS NULL), not a (SIMPLE-ARRAY * (*)).
Note: Unable to optimize due to type uncertainty:
      The first argument is a (OR VECTOR CONS NULL), not a VECTOR.

  (* (LENGTH DISASTERS) MULT)
Note: Unable to optimize due to type uncertainty:
      The second argument is a NUMBER, not a FLOAT.
Note: Unable to convert x*2^k to shift due to type uncertainty:
      The second argument is a NUMBER, not a INTEGER.
Note: Unable to recode as shift and add due to type uncertainty:
      The second argument is a NUMBER, not a (UNSIGNED-BYTE 32).
      The result is a REAL, not a (UNSIGNED-BYTE 32).

  (DOTIMES (I L) (PUSH (RANDOM-CULLER #) DISASTERS))
--> DO BLOCK LET TAGBODY UNLESS COND IF NOT IF >= IF 
==>
  (< I #:G0)
Note: Unable to optimize due to type uncertainty:
      The second argument is a REAL, not a INTEGER.

  (LENGTH DISASTERS)
Note: Unable to optimize due to type uncertainty:
      The first argument is a (OR VECTOR CONS NULL), not a (SIMPLE-ARRAY * (*)).
Note: Unable to optimize due to type uncertainty:
      The first argument is a (OR VECTOR CONS NULL), not a VECTOR.

  (* (LENGTH DISASTERS) MULT)
Note: Forced to do GENERIC-* (cost 50).
      Unable to do fixnum * (cost 30) because:
      The second argument is a NUMBER, not a FIXNUM.
      The result is a REAL, not a FIXNUM.
      Unable to do unsigned * (cost 40) because:
      The second argument is a NUMBER, not a (UNSIGNED-BYTE 32).
      The result is a REAL, not a (UNSIGNED-BYTE 32).
      etc.

  (DOTIMES (I L) (PUSH (RANDOM-CULLER #) DISASTERS))
--> DO BLOCK LET TAGBODY UNLESS COND IF NOT IF >= IF 
==>
  (< I #:G0)
Note: Forced to do GENERIC-< (cost 10).
      Unable to do inline fixnum comparison (cost 4) because:
      The first argument is a UNSIGNED-BYTE, not a FIXNUM.
      The second argument is a REAL, not a FIXNUM.

--> DO BLOCK LET TAGBODY PSETQ LET 1+ 
==>
  (+ I 1)
Note: Forced to do GENERIC-+ (cost 10).
      Unable to do inline fixnum arithmetic (cost 1) because:
      The first argument is a UNSIGNED-BYTE, not a FIXNUM.
      The result is a (INTEGER 1), not a FIXNUM.
      Unable to do inline fixnum arithmetic (cost 2) because:
      The first argument is a UNSIGNED-BYTE, not a FIXNUM.
      The result is a (INTEGER 1), not a FIXNUM.
      etc.

  (RANDOM 3)
--> REM BLOCK MULTIPLE-VALUE-BIND MULTIPLE-VALUE-CALL 
==>
  (TRUNCATE NUMBER KERNEL::DIVISOR)
Note: Doing unsigned word to integer coercion (cost 20) from KERNEL::Y.


In: DEFUN RUN-SIMULATION
  (DOTIMES (I NUMBER-GENERATIONS)
    (SETF COMMUNITY-SIZE (POPULATION-CULL GEN1 COMMUNITY-SIZE #))
    (SETF COMMUNITY-SIZE (NEXT-GEN GEN1 COMMUNITY-SIZE MAX GEN2 ...))
    (ROTATEF GEN1 GEN2))
--> DO BLOCK LET TAGBODY UNLESS COND IF NOT IF >= IF 
==>
  (< I #:G0)
Note: Unable to optimize due to type uncertainty:
      The second argument is a REAL, not a INTEGER.

  (RANDOM (LENGTH KILLER-ARRAY))
Note: Unable to use inline (unsigned-byte 32) operations due to type uncertainty:
      The first argument is a (MOD 536870911), not a (INTEGER 1 4294967296).

  (DOTIMES (I NUMBER-GENERATIONS)
    (SETF COMMUNITY-SIZE (POPULATION-CULL GEN1 COMMUNITY-SIZE #))
    (SETF COMMUNITY-SIZE (NEXT-GEN GEN1 COMMUNITY-SIZE MAX GEN2 ...))
    (ROTATEF GEN1 GEN2))
--> DO BLOCK LET TAGBODY UNLESS COND IF NOT IF >= IF 
==>
  (< I #:G0)
Note: Forced to do GENERIC-< (cost 10).
      Unable to do inline fixnum comparison (cost 4) because:
      The first argument is a UNSIGNED-BYTE, not a FIXNUM.
      The second argument is a REAL, not a FIXNUM.

--> DO BLOCK LET TAGBODY PSETQ LET 1+ 
==>
  (+ I 1)
Note: Forced to do GENERIC-+ (cost 10).
      Unable to do inline fixnum arithmetic (cost 1) because:
      The first argument is a UNSIGNED-BYTE, not a FIXNUM.
      The result is a (INTEGER 1), not a FIXNUM.
      Unable to do inline fixnum arithmetic (cost 2) because:
      The first argument is a UNSIGNED-BYTE, not a FIXNUM.
      The result is a (INTEGER 1), not a FIXNUM.
      etc.


Compilation unit finished.
  1 warning
  29 notes


gen.sparcf written.
Compilation finished in 0:00:01.

#p"/users/marcoxa/lang/cl/tests/genetics/gen.sparcf"
T
T
* (load "gen")

-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
http://www.parades.rm.cnr.it
From: Andrew Koenig
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <F8LFyt.9@research.att.com>
In article <··············@copernico.parades.rm.cnr.it>,
Marco Antoniotti  <·······@copernico.parades.rm.cnr.it> wrote:

> This is sure!
> ·······@copernico:genetics 56> g++ --version
> 2.7.2.2
> ·······@copernico:genetics 57> g++ gen.C
> gen.C:155: warning: ANSI C++ forbids declaration `population' with no type or storage class

The reason for that is that the news-posting process split a // comment
across two lines so that the second line was no longer considered
a comment.

If the Lisp version had any comments long enough to split,
it would have had the same problem.
-- 
				Andrew Koenig
				···@research.att.com
				http://www.research.att.com/info/ark
From: Marco Antoniotti
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <lwhfrndtoh.fsf@copernico.parades.rm.cnr.it>
···@research.att.com (Andrew Koenig) writes:

> In article <··············@copernico.parades.rm.cnr.it>,
> Marco Antoniotti  <·······@copernico.parades.rm.cnr.it> wrote:
> 
> > This is sure!
> > ·······@copernico:genetics 56> g++ --version
> > 2.7.2.2
> > ·······@copernico:genetics 57> g++ gen.C
> > gen.C:155: warning: ANSI C++ forbids declaration `population' with no type or storage class
> 
> The reason for that is that the news-posting process split a // comment
> across two lines so that the second line was no longer considered
> a comment.

Yes, I figured that out. My fingers are just too fast, apologies for
that.  However, my post was really about the CL code and how good and
helpful the CMUCL compiler is.

Cheers

-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
http://www.parades.rm.cnr.it
From: Hanley
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <36EBF789.7BC5472F@nmia.com>
Andrew Koenig wrote:

> I am not surprised that a C++ transliteration of a Lisp program
> would not much faster than the original Lisp program compiled on
> a good compiler.  I don't consider the degree of difference to
> indicate all that much, though, because differences between individual
> implementations are likely to be greater -- in both languages.

Well, if you looked at the code, I think you would have seen it
was more than a transliteration.  I went through pains to change the code
to work faster in C++ where possible.  For example, swap() wasn't being inlined
because it was a template so I hard-wired a version of it.  I tried both
C compilers available to me and used the faster one.  I used a class with
a virtual member instead of the more flexible closures.   Remember, the C++
version is
being used on a real project.

> What would be more interesting would be to see whether an
> implementation of the same algorithm from scratch in C++ would
> be faster.

    Well, I'm not sure what diference that would make.  So far, no one has
suggested, except you by implication, that there was a problem with the C++
version that was making it slower, and I certianly haven't gotten any
constructive criticism in that regard.

>
>
> > Here is the WC of both:
>
> >      421    1416    7847 project.cc
> >      256     789    6243 project.lsp
>
> What's interesting, though, is that if I remove from the C++ version
> all lines that are either completely blank or that contain only { or },
> I am left with 259 lines.  Moreover, I suspect that the C++ version
> could be made significantly smaller by using the library more.

Well, sure.  The lisp version has blank lines and naked parens too.  I'm not
sure it makes any difference.  True, possibly library code could possibly have
make it smaller, but again, I'm not clear where, and no one has made any
suggestions on real improvements.  One way might be to allocate all the genes
contiguously (in place) in a vector, but then then they can't have constructors.

If this could be done without a big speed impact, that would be quite nice.

> I think this experiment is an interesting first step, and encourage
> deeper investigation.

Thanks.  Though it wasn't an experiment, it was something written for a project,

as I said in the first note.  I'd actually be pleased if someone could point out

a major improvement in the C++ version, because this code will be run by my
students hundreds of times, and it would make a difference.

dave
From: David Harmon
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <38192d0d.1219167701@source.netcom.com>
On Sun, 14 Mar 1999 10:53:13 -0700 in comp.lang.c++, Hanley
<····@nmia.com> wrote:

>Well, if you looked at the code, I think you would have seen it
>was more than a transliteration.  I went through pains to change the code
>to work faster in C++ where possible.  For example, swap() wasn't being inlined
>because it was a template so I hard-wired a version of it.

Would you mind checking how much you save if you make 
     int randomAllelle( Gene *g )
an inline?
From: Andrew Koenig
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <F8LuBv.69G@research.att.com>
In article <·················@nmia.com>, Hanley  <····@nmia.com> wrote:

> Andrew Koenig wrote:

> > I am not surprised that a C++ transliteration of a Lisp program
> > would not much faster than the original Lisp program compiled on
> > a good compiler.  I don't consider the degree of difference to
> > indicate all that much, though, because differences between individual
> > implementations are likely to be greater -- in both languages.

> Well, if you looked at the code, I think you would have seen it
> was more than a transliteration.  I went through pains to change the code
> to work faster in C++ where possible.  For example, swap() wasn't being inlined
> because it was a template so I hard-wired a version of it.  I tried both
> C compilers available to me and used the faster one.  I used a class with
> a virtual member instead of the more flexible closures.

OK, then, an augmented transliteration :-)

> > > Here is the WC of both:

> > >      421    1416    7847 project.cc
> > >      256     789    6243 project.lsp

> > What's interesting, though, is that if I remove from the C++ version
> > all lines that are either completely blank or that contain only { or },
> > I am left with 259 lines.  Moreover, I suspect that the C++ version
> > could be made significantly smaller by using the library more.

> Well, sure.  The lisp version has blank lines and naked parens too.  I'm not
> sure it makes any difference.  True, possibly library code could possibly have

The Lisp version has far fewer blank lines and naked parens.

> make it smaller, but again, I'm not clear where, and no one has made any
> suggestions on real improvements.  One way might be to allocate all the genes
> contiguously (in place) in a vector, but then then they can't have constructors.

Sure they can.  Why not?

> If this could be done without a big speed impact, that would be quite nice.

My first thought was that if it was known that each of the members of class Gene
would fit in a short, then you could do away with the pointers entirely with
no increase in the amount of data copied and, I suspect, a significant increase
in execution speed.

> as I said in the first note.  I'd actually be pleased if someone could point out

> a major improvement in the C++ version, because this code will be run by my
> students hundreds of times, and it would make a difference.

If I find one, I will certainly post it -- but I have a bunch of other
things I have to do also.
-- 
				Andrew Koenig
				···@research.att.com
				http://www.research.att.com/info/ark
From: Tim Bradshaw
Subject: Re: A program in both lisp and C
Date: 
Message-ID: <nkjn21fc8mq.fsf@tfeb.org>
···@research.att.com (Andrew Koenig) writes:

> 
> The Lisp version has far fewer blank lines and naked parens.
> 

But lisp coding style is to use far fewer blank lines and naked parens
than typical C++ styles.  So of course, I could make the C++ one
shorter by writing iit in a way that most C++ people would find hard
to read:

int foo(int a) {
    for (int i = 1; i <	a; i++) {
	frobnicate(i);
	zap(i,a); } }

And what this actually says is that lines of code is a really bad
measure of anything.  Except, I guess, how hard it is to edit your
code if you are stuck with an old-fashioned screen with a merely
finite number of lines.

--tim