work with mark: array primitives and benchmarking the framework

i spent a lot of time benchmarking the code for the agents in the different versions of the multi-agent frameworks i posted about here earlier. best performance boost was (of course) when i ported some vital parts of the supercollider code to c. so for example one cpu-intensive task that the agents had to do a lot was to find out about their surroundings. and another 'heavy' task was to calculate the distance to other objects. each operation wasn't very demanding on its own, but when hundreds of agents would do this at the same time, we really needed the speed of the c primitives.

below is the c code i came up with. it replaces some of the computational heavy parts in the Surroundings and ALoaction supercollider classes. to try them out you'd need to download the supercollider source code from svn, add my code to the file PyrArrayPrimitives.cpp and then recompile the whole application. edit the file extArrayedCollection.sc in the A4.zip package posted here earlier to use the primitives.
one issue we then had was to distribute this 'hack'. supercollider doesn't have an api for adding extensions like this to the language (but there's a nice plugin architecture for the server). so i had to build dedicated sc applications including this speed hack.

int prArrayAsALocationIndex(struct VMGlobals *g, int numArgsPushed)
{
        PyrSlot *a, *b;
        PyrObject *obj;
        int size, i;
        double asIndex, w, worldSize;
        a = g->sp - 1;
        b = g->sp;
        worldSize = b->ui;
        obj = a->uo;
        size = obj->size;
        asIndex= 0;
        for (i=0; i<size; ++i) {
                getIndexedDouble(obj, i, &w);
                asIndex= asIndex+(pow(worldSize, i)*w);
        }
        SetFloat(a, asIndex);
        return errNone;
}
int prArrayAsALocationRoundedIndex(struct VMGlobals *g, int numArgsPushed)
{
        PyrSlot *a, *b;
        PyrObject *obj;
        int size, i;
        double asIndex, w, worldSize;
        a = g->sp - 1;
        b = g->sp;
        worldSize = b->ui;
        obj = a->uo;
        size = obj->size;
        asIndex= 0;
        for (i=0; i<size; ++i) {
                getIndexedDouble(obj, i, &w);
                w= sc_round(w, 1.0);
                w= sc_clip(w, 0, worldSize-1);
                asIndex= asIndex+(pow(worldSize, i)*w);
        }
        SetFloat(a, asIndex);
        return errNone;
}
int prArrayDistance(struct VMGlobals *g, int numArgsPushed)
{
        PyrSlot *a, *b;
        PyrObject *obj1, *obj2;
        int size, i;
        double w1, w2, distance;
        a = g->sp - 1;
        b = g->sp;
        if (b->utag != tagObj || a->uo->classptr != b->uo->classptr) return errWrongType;
        obj1 = a->uo;
        obj2 = b->uo;
        size = obj1->size;
        distance= 0;
        for (i=0; i<size; ++i) {
                getIndexedDouble(obj1, i, &w1);
                getIndexedDouble(obj2, sc_mod(i, size), &w2);
                distance= distance+pow(w2-w1, 2);
        }
        SetFloat(a, fabs(sqrt(distance)));
        return errNone;
}
int prArraySurroundings(struct VMGlobals *g, int numArgsPushed)
{
        PyrSlot *a, *b, *c, *d, *areaArraySlots, *indexArraySlots, *outArraySlots;
        PyrObject *obj, *areaArray, *indexArray, *outArray;
        int areaSize, outSize, i, j, worldDim, area;
        double w;
        a = g->sp - 3;          //list
        b = g->sp - 2;          //worldDim
        c = g->sp - 1;          //area - as float possible later?
        d = g->sp;                      //boolean - exclude/include fix later
        if (b->utag != tagInt) return errWrongType;
        if (c->utag != tagInt) return errWrongType;
        if (d->utag != tagTrue && d->utag != tagFalse) return errWrongType;
        obj = a->uo;
        worldDim = b->ui;
        area = c->ui;
        areaSize = area*2+1;
        indexArray = newPyrArray(g->gc, worldDim, 0, true);
        indexArraySlots = indexArray->slots;
        indexArray->size = worldDim;
        if (IsTrue(d)) {        //--build index array excluding
                areaArray = newPyrArray(g->gc, areaSize-1, 0, true);
                areaArraySlots = areaArray->slots;
                areaArray->size = areaSize-1;
                int j = 0;
                for (i=0; i<areaSize-1; ++i) {
                        int temp = 0-area+i;
                        if (temp==0) {j++;}
                        areaArraySlots[i].ucopy = temp+j;
                }
                outSize = pow(areaSize, worldDim)-1;
        } else {                        //--build index array including
                areaArray = newPyrArray(g->gc, areaSize, 0, true);
                areaArraySlots = areaArray->slots;
                areaArray->size = areaSize;
                for (i=0; i<areaSize; ++i) {
                        areaArraySlots[i].ucopy = 0-area+i;
                }
                outSize = pow(areaSize, worldDim);
        }
        for (i=0; i<worldDim; ++i) {
                SetObject(indexArraySlots+i, areaArray);
        }
        //indexArray is here... [[-1, 0, 1]] or [[-1, 0, 1], [-1, 0, 1]] etc. for area=1
        //or [[-2, -1, 0, 1, 2]] or [[-2, -1, 0, 1, 2], [-2, -1, 0, 1, 2]] etc. for area=2
        //--all tuples
        outArray = newPyrArray(g->gc, outSize*sizeof(PyrObject), 0, true);
        outArraySlots = outArray->slots;
        outArray->size = outSize;
        for (i=0; i<outSize; ++i) {
                int k = i;
                PyrObject *tempArray = newPyrArray(g->gc, worldDim, 0, true);
                PyrSlot *tempArraySlots = tempArray->slots;
                tempArray->size = worldDim;
                for (j=worldDim-1; j>=0; --j) {
                        tempArraySlots[j].ucopy = areaArraySlots[k%areaSize].ucopy;
                        getIndexedDouble(obj, j, &w);
                        tempArraySlots[j].ucopy += w;
                        k /= areaSize;
                }
                SetObject(outArraySlots+i, tempArray);
        }
        a->uo = outArray;
        return errNone;
}

supercollider code for benchmarking...

//speedtest
({
        var size= 100, cSize= 2, rule= 30;
        var world, agents, y= 0, dict;
        dict= ();               /*lookup dictionary for rules*/
        8.do{|i| dict.put(i.asBinaryDigits(3).join.asSymbol, rule.asBinaryDigits[7-i])};
        ACell.rules= dict;
        world= APattern(size);                                          /*create 1d world*/
        size.do{|i| ACell(ALocation(world, [i]))};      /*fill up 1d grid with agents*/
        world.get(ALocation(world, [(size/2).round])).value= 1; /*middle agent value=1 as init*/
        agents= world.items;
        while({y&lt;size}, {
                /*here update.  first all agents.sense then all agents.act*/
                agents.do{|a| a.sense};
                /*agents.do{|a| a.location.list_([(size/2).round])};*/
                /*agents.do{|a| a.location= ALocation(a.location.world, [(size/2).round])};*/
                agents.do{|a| a.act};
                y= y+1;
        });
}.bench)