From: George J Carrette
Subject: A language extensibility benchmark
Date: 
Message-ID: <DLuKxo.DnE@world.std.com>
This is a language extensibility benchmark, previously posted
as an attachment from a Netscape browser that mangled mime type
into something absurd. The challenge is to write equivalent functions
in C as extensions for your favorite interpreter and compare for
readability, transparency, brevity, and maintainability.


/* name:    siodx.c
   purpose: demonstrate interpreter extension facility.
   author:  George Carrette, ···@mci.newscorp.com 
   uses:    ftp://ftp.std.com/pub/gjc/siod_tar.gz

The exersize is to present a complete solution for the following:
 [1] the makefile section required.
 [2] the interpreted code needed to load and test the extension.
 [3] source code for bootstrapping the extension.
 [4] source code to compute fib(n) using simple recursion.
 [5] source code to compute a string_toupper(s)
 [6] source code to compute a double_array_rms(a)

The exersize is to be graded on the inspection obviousness of the solutions,
brevity, straightforward use of the C language, and the general ease of use
demonstrated.

An important bonus is awarded if the solutions may be easily invoked from
both interpreted code and other C coded parts of the system, especially
focusing on other extensions.

[1] makefile

siodx.so: siodx.o
	$(LD) -o siodx.so -shared siodx.o -lsiod -lm -lc

siodx.o: siodx.c
	$(CC) -c -I/usr/local/include siodx.c

[2] load and test

#!/usr/local/bin/htqs
(require-so "siodx.so")
(print (fib 20))
(print (string_toupper "fooBar"))
(define a (cons-array 3 'double))
(aset a 0 1)
(aset a 1 -2)
(aset a 2 3)
(print (double_array_rms a))

*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include "siod.h"

/* [3] bootstrap, and declare for other C programmers to use. */

LISP fib(LISP);
LISP string_toupper(LISP);
LISP double_array_rms(LISP);

void init_siodx(void)
{init_subr_1("fib",fib);
 init_subr_1("string_toupper",string_toupper);
 init_subr_1("double_array_rms",double_array_rms);}

/* [4] */

LISP fib(LISP n)
{return((!NULLP(lessp(n,flocons(2.0))))
	? n
	: plus(fib(difference(n,flocons(1.0))),
	       fib(difference(n,flocons(2.0)))));}

/* [5] */

LISP string_toupper(LISP s1)
{LISP s2;
 long j,n;
 char *p1,*p2;
 p1 = get_c_string_dim(s1,&n);
 s2 = strcons(n,NULL);
 p2 = get_c_string(s2);
 for(j=0;j<n;++j)
   p2[j] = toupper(p1[j]);
 return(s2);}

/* [6] */

LISP double_array_rms(LISP a)
{long j,n;
 double r,*p;
 if (!TYPEP(a,tc_double_array)) err("not a double array",a);
 for(j=0,
     r=0.0,
     n=a->storage_as.double_array.dim,
     p=a->storage_as.double_array.data;
     j<n;
     ++j)
   r += p[j] * p[j];
 return(flocons(sqrt(r/n)));}

From: Tim Bunce
Subject: Re: A language extensibility benchmark
Date: 
Message-ID: <DLy912.A9@ig.co.uk>
Posted to comp.lang.perl by mistake. Forwarded to comp.lang.perl.misc.
If you reply to the original please edit the Newsgroup line since
comp.lang.perl no longer exists.

Tim.
p.s. I've no time to implement the perl equiv. Hopefully someone else will.


In article <··········@world.std.com>,
George J Carrette <···@world.std.com> wrote:

This is a language extensibility benchmark, previously posted
as an attachment from a Netscape browser that mangled mime type
into something absurd. The challenge is to write equivalent functions
in C as extensions for your favorite interpreter and compare for
readability, transparency, brevity, and maintainability.


/* name:    siodx.c
   purpose: demonstrate interpreter extension facility.
   author:  George Carrette, ···@mci.newscorp.com 
   uses:    ftp://ftp.std.com/pub/gjc/siod_tar.gz

The exersize is to present a complete solution for the following:
 [1] the makefile section required.
 [2] the interpreted code needed to load and test the extension.
 [3] source code for bootstrapping the extension.
 [4] source code to compute fib(n) using simple recursion.
 [5] source code to compute a string_toupper(s)
 [6] source code to compute a double_array_rms(a)

The exersize is to be graded on the inspection obviousness of the solutions,
brevity, straightforward use of the C language, and the general ease of use
demonstrated.

An important bonus is awarded if the solutions may be easily invoked from
both interpreted code and other C coded parts of the system, especially
focusing on other extensions.

[1] makefile

siodx.so: siodx.o
	$(LD) -o siodx.so -shared siodx.o -lsiod -lm -lc

siodx.o: siodx.c
	$(CC) -c -I/usr/local/include siodx.c

[2] load and test

#!/usr/local/bin/htqs
(require-so "siodx.so")
(print (fib 20))
(print (string_toupper "fooBar"))
(define a (cons-array 3 'double))
(aset a 0 1)
(aset a 1 -2)
(aset a 2 3)
(print (double_array_rms a))

*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include "siod.h"

/* [3] bootstrap, and declare for other C programmers to use. */

LISP fib(LISP);
LISP string_toupper(LISP);
LISP double_array_rms(LISP);

void init_siodx(void)
{init_subr_1("fib",fib);
 init_subr_1("string_toupper",string_toupper);
 init_subr_1("double_array_rms",double_array_rms);}

/* [4] */

LISP fib(LISP n)
{return((!NULLP(lessp(n,flocons(2.0))))
	? n
	: plus(fib(difference(n,flocons(1.0))),
	       fib(difference(n,flocons(2.0)))));}

/* [5] */

LISP string_toupper(LISP s1)
{LISP s2;
 long j,n;
 char *p1,*p2;
 p1 = get_c_string_dim(s1,&n);
 s2 = strcons(n,NULL);
 p2 = get_c_string(s2);
 for(j=0;j<n;++j)
   p2[j] = toupper(p1[j]);
 return(s2);}

/* [6] */

LISP double_array_rms(LISP a)
{long j,n;
 double r,*p;
 if (!TYPEP(a,tc_double_array)) err("not a double array",a);
 for(j=0,
     r=0.0,
     n=a->storage_as.double_array.dim,
     p=a->storage_as.double_array.data;
     j<n;
     ++j)
   r += p[j] * p[j];
 return(flocons(sqrt(r/n)));}
From: Aaron Watters
Subject: Re: A language extensibility benchmark
Date: 
Message-ID: <4ejcf0$52d@nntpa.cb.att.com>
Hi.  This thread is a great idea, and by the way I've looked
at siod and am very impressed by it.  I recommend it along with
python.  Here is a python code analogue, It's probably not as compact
as it could be, and a lot of the C code space is error checking,
which apparently is more streamlined in siod.

NOTE: fib works even with "number" objects defined as instances of
python classes (implemented in Python, not C), and double_array_rms
works with any python sequence object even instances of python
sequence classes (again, implemented in Python).  This generality is
provided by a very useful feature of python: C can call-back python as
easily as python can call-back C.  I don't know how to do arbitrary
recursive multilingual call-backs (probably my fault) in Perl or java,
for example.

I look forward to other installments for this thread.
   -- Aaron Watters
===
As seen throughout Eastern Europe:  COMMUNISM II.
IT'S ALIVE!!!  Just when you thought it was safe for democracy...
  -- spoof movie poster on the cover of the 22 Dec 95 Economist.
=== CUT CUT CUT
/*
This is a language extensibility benchmark.
 written for python.
*/

/* name:    testmodule.c
   purpose: demonstrate interpreter extension facility.
   author:  Aaron Watters (···@big.att.com)
   uses:    Python (http://www.python.org)

The exersize is to present a complete solution for the following:
 [1] the makefile section required.
 [2] the interpreted code needed to load and test the extension.
 [3] source code for bootstrapping the extension.
 [4] source code to compute fib(n) using simple recursion.
 [5] source code to compute a string_toupper(s)
 [6] source code to compute a double_array_rms(a)

[1] makefile

No need to change any makefile.  Just add the line
 test testmodule.c
to /Modules/Setup

[2] load and test

#!/usr/local/bin/python
from test import *
fib(20)
string_toupper("fooBar")
double_array_rms([1.0, -2.0, 3.0])
*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include "allobjects.h"
#include "modsupport.h"

/* [3] bootstrap, and declare for other C programmers to use.
   uses "old naming convention" for brevity.  The "right" way to
   implement these would be a bit longer */

object *fib(thismodule, args);
object *string_toupper(thismodule, args);
object *double_array_rms(thismodule, args);

static struct methodlist test_methods[] = {
  {"fib",  fib},
  {"string_toupper", string_toupper},
  {"double_array_rms", double_array_rms},
  {NULL, NULL} /* sentinel */
};

void inittest()
{ initmodule("test", test_methods); }

/* [4] */
/* fibonnacci function, recursive, exponentially inefficient,
   generalized to work with floats and arbitrary precision integers,
   as well as yet to be implemented numeric representations --
   hence unnecessarily complicated wrt error checking.  Note: this
   implementation will work even with "numerics" defined in python.
 */
static object *fibxxx(number) /* helper */
 object *number;
{
   object *result, *one, *two, *numl1, *numl2, *fib1, *fib2;
   int test;
   result = one = two = fib1 = fib2 = numl1 = numl2 = NULL;
   /* check for base case */
   two = newintobject(2);
   if (PyObject_Cmp(number, two, &test) != -1) { 
      if (test < 1) {
        XINCREF(number);
	result = number;  /* base case */
      } else {
	one = newintobject(1);
        if (numl1 = PyNumber_Subtract(number, one)) {
	  if (numl2 = PyNumber_Subtract(number, two)) {
	    if (fib1 = fibxxx(numl1)) {
		if (fib2 = fibxxx(numl2)) {
		  result = PyNumber_Add( fib1, fib2 );
   }}}}}}
   XDECREF(fib1); XDECREF(fib2); XDECREF(numl1);
   XDECREF(numl2); XDECREF(one); XDECREF(two);
   return result;
 }

object *fib(thismodule, args)
 object *thismodule;
 object *args;
{
   object *number;
   if (!getargs(args, "O", &number)) {  /* verify argument */
      err_setstr(TypeError, "fib needs exactly one arg");
      return NULL;
    }
   return fibxxx(number);
 }

/* [5] */

/* taken from stropsmodule.c, deoptimized */

static object *string_toupper(self, args)
        object *self; /* Not used */
        object *args;
{
        char *s, *s_new;
        int i, n;
        object *new;
        if (!getargs(args, "s#", &s, &n))
                return NULL;
        new = newsizedstringobject(NULL, n);
        if (new == NULL)
                return NULL;
        s_new = getstringvalue(new);
        for (i = 0; i < n; i++) {
                *s_new = toupper(Py_CHARMASK(*s++));
                s_new++;
        }
        return new;
}

/* [6] */

/* This will work for any sequence object, even those implemented
   in Python or some future extension module.
   -- but I assume the elements must be floats, which are
   always doubles in python. 
*/
object *double_array_rms(self, args)
  object *self, *args;
{
  int index, length;
  double sum, this;
  object *elt, *sequence;
  if (getargs(args, "O", &sequence) && PySequence_Check(sequence)) {
    sum = 0.0;
    length = PyObject_Length(sequence);
    for (index = 0; index<length; index++) {
      elt = PySequence_GetItem(sequence, index);
      if (!PyFloat_Check(elt)) {
	err_setstr(TypeError, "only float elements allowed");
	return NULL;
      }
      this = PyFloat_AS_DOUBLE((PyFloatObject *) elt);
      sum += this*this;
      XDECREF(elt);
    }
    return PyFloat_FromDouble(sqrt(sum/length));
  } else {
    err_setstr(TypeError, "requires one sequence of floats");
  }
  return NULL;
}
/* end of module */
From: George J. Carrette
Subject: Re: A language extensibility benchmark
Date: 
Message-ID: <4ejj39$d2v@merlin.delphi.com>
···@quarto.info.att.com (Aaron Watters) wrote:
>Hi.  This thread is a great idea ...

And I think that when comparisons of this kind come up it often
causes language implementors to improve their work.

> ... Python ...
>[1] makefile
>No need to change any makefile.  Just add the line
> test testmodule.c
>to /Modules/Setup

Python must have a dynamic linking mechanism for compiled C extensions,
doesn't it?
From: Dean Roehrich
Subject: Re: A language extensibility benchmark
Date: 
Message-ID: <1996Feb3.213953.11512@driftwood.cray.com>
In article <··········@merlin.delphi.com>,
George J. Carrette <···@mci.newscorp.com> wrote:
>········@cray.com (Dean Roehrich) wrote:

>>Here's my attempt at the benchmark.  
>
>Great. I would like to try it as soon as you provide the commands
>needed to "make" the extension module. 
>
>>[1] the makefile section required.
>>/* [1] not needed--automated */
>>step [1] is basically a
>>null-op.
>
>Even if it is "basically" a no-op, a person who is unfamiliar with Perl 5
>extensions (I've only done Perl 4 extensions for example) would still need to be
>given an explicit sequence of commands to execute in order to compile and 
>link the extension. 

You're right, I omitted the automated parts.

Any and all Perl extensions begin this way (exact choice of options may vary
depending on what is needed in the template, the following will be
sufficient for the extension I supplied):

	$ h2xs -An Soidx  (creates templates for the extension's pieces)
	$ cd Siodx
	< fill in the Siodx.xs and test.pl files with the stuff I provided >
	$ perl5 Makefile.PL  (creates a makefile)
	$ make  (somewhere in here step [3] happens--automatically)

This process is covered in the perlxstut manpage.

>>you'll have to forgive me for deviating from it and writing C code as if it
>>were...well, C code. 
>
>Is it really C code that you wrote? I thought it was "Perl Extension Language"
>something that needs to be run through a specialized pre-processor before
>it can be compiled by a C compiler.

The language (what there is of it) is called XS.  The CODE: blocks and fib()
function were C.

My comment about writing C as C was a jab aimed at the benchmark's lisp
solution and its lispy style of formatting C, a style which caused me some
pain while I was trying to decipher it.  I was uncertain that I had properly
interpreted what was supposed to be happening in those functions.

I still believe that the benchmark was not objective.  Siod is a lisp
interpreter (as far as I can tell), so the lisp solution did not have to
translate between siod arrays and lisp arrays.  Both Perl and Python (and
Tcl, when someone gets around to it) will have to include this translation,
in some form, at which point the benchmark is no longer the same one that
the lisp solution applies to.

As it is now, the Python and Perl solutions don't fit the benchmark.  If the
benchmark is ever reformed I would like it to be more specific, and less
biased, about the datatype or function that is to be interfaced.  Try
something that will really show the API of the language being benchmarked.
Maybe something like building, destroying, and walking a linked list of C
structures (or an array of them--that would be interesting, too) and
accessing fields in those structures.  This benchmark should be done by
calling C functions which are in a separate library, or module, and which
know how to manipulate the structures (Perl's pack/unpack, and the
equivalent in any other language, do not fit the benchmark)-- thus
demonstrating how the high level language can use C's complex datatypes.

The benchmark missed other important points on language extensions: how easy
is it to build the extension for static linking?  for dynamic linking
(assume it's available)?  What if the extension is handed to John Doe, who
hasn't heard of dynamic linking until now and who has a different OS with
different ld and cc options?  How long will it take him to get the makefile
into working order so the extension will build static?  dynamic?

If the Python version is reworked, I'd like it to stick to the scope of the
benchmark, rather than cluttering it with the capability to handle every
possible Python datatype :)  That's cool, but it's not what the benchmark
was about.  This benchmark would also be a good way to show off PyMaker,
which I wouldn't mind seeing in action.

(Please, someone, show us a Java solution after this benchmark is reworked.)

Dean
········@cray.com
From: Jim Fulton
Subject: Re: A language extensibility benchmark
Date: 
Message-ID: <JFULTON.96Feb5183248@disqvarsa.er.usgs.GOV>
>>>>> "George" == George J Carrette <···@mci.newscorp.com> writes:
In article <··········@merlin.delphi.com> "George J. Carrette" <···@mci.newscorp.com> writes:

> ···@quarto.info.att.com (Aaron Watters) wrote:
>> Hi.  This thread is a great idea ...

> And I think that when comparisons of this kind come up it often
> causes language implementors to improve their work.

>> ... Python ...
>> [1] makefile
>> No need to change any makefile.  Just add the line
>> test testmodule.c
>> to /Modules/Setup

> Python must have a dynamic linking mechanism for compiled C extensions,
> doesn't it?

Yes, and this is definately the way to go for systems that support
dynamic linking.  Unfortunately, the details of compiling modules for
dynamic linking vary from system to system.


--
-- Jim Fulton      ·······@usgs.gov          (703) 648-5622
                   U.S. Geological Survey, Reston VA  22092 
This message is being posted to obtain or provide technical information
relating to my duties at the U.S. Geological Survey.