_PERFORMANCE TUNING: SLUGGING IT OUT!_ by Michael R. Dunlavey [LISTING ONE] /* FAST.H DEFINITION OF DISPATCHn MACROS ------------------ */ #define DISPATCH0 #define DISPATCH1 \ if (p->state==1) goto L1;\ DISPATCH0 #define DISPATCH2 \ if (p->state==2) goto L2;\ DISPATCH1 #define DISPATCH3 \ if (p->state==3) goto L3;\ DISPATCH2 #define DISPATCH4 \ if (p->state==4) goto L4;\ DISPATCH3 /* FAST.C REDESIGNED IMPLEMENTATION ----------------------- */ #include #include "fast.h" /* MACRO TO GEN STANDARD STATE MACHINE VARS */ #define STDVARS int (*func)(); int state; struct machine_struct *caller /* "ROOT CLASS" OF STATE MACHINE */ typedef struct machine_struct { STDVARS; } machine_t; /* MACRO TO GEN SETUP CODE FOR A CLASS OF MACHINE */ #define PROLOGUE(typ,f)\ typ *p = (typ*)malloc(sizeof(*p));\ extern int f();\ p->caller = caller;\ p->func = f;\ p->state = 0;\ (*p->func)(p); /* BREAK STATEMENT */ #define BREAK(n,lab) p->state=(n); enque(p); return; lab: /* CALL STATEMENT */ #define CALL(n,lab,expr) p->state=(n); (expr); return; lab: machine_t * ptemp=NULL; int retn_val=0; /* RETURN STATEMENT */ #define RETURN(v)\ ptemp=p->caller;\ retn_val=(v);\ free(p);\ if (ptemp){(*ptemp->func)(ptemp);}; /* THE GLOBAL WAIT QUEUE */ int enq=0, deq=0, ninq=0; machine_t *queue[256]; enque(p) machine_t *p;{ queue[enq++] = p; if (enq>=256) enq=0; ninq++; } machine_t * deque(){ machine_t *p = NULL; if (ninq){ p = queue[deq++]; if (deq>=256) deq=0; ninq--; } return(p); } /* -------- APPLICATION CODE -------------- */ int jobs_started=0; int jobs_completed=0; #define NBOPS (rand()%5 + 10) #define NTASK 10 #define NJOBS 100 /* MAIN() */ main(){ machine_t *p; /* REPEAT UNTIL ALL JOBS ARE COMPLETE */ while(jobs_completed < NJOBS){ /* RUN WHATEVER CAN BE RUN */ if (ninq){ p = deque(); (*p->func)(p); } /* IF < 100 JOBS STARTED AND < 10 JOBS IN PROCESS */ if (jobs_startedjobid = jobs_started++; p->nbops = NBOPS; /* FOR EACH OPERATION */ for (p->i=0; p->i < p->nbops; p->i++){ /* PERFORM THE OPERATION */ CALL(1,L1,opn(p)); } jobs_completed++; printf("Ack Job %d\n",p->jobid); RETURN(1); } /* OPN_T: OPERATION STATE MACHINE */ typedef struct { STDVARS; int taskid; int ntask; } opn_t; opn(caller) machine_t *caller;{ PROLOGUE(opn_t,opn_func); } opn_func(p) opn_t *p;{ DISPATCH2; p->ntask = NTASK; /* FOR EACH OPERATION */ for (p->taskid=0; p->taskid < p->ntask; p->taskid++){ /* PERFORM DEVICE CONTROL TASK */ CALL(1,L1,dev_ctl(p)); /* PERFORM MATERIAL HANDLING TASK */ CALL(2,L2,mh_ctl(p)); } RETURN(1); } /* DEV_CTL_T: DEVICE CONTROL TASK STATE MACHINE */ typedef struct { STDVARS; } dev_ctl_t; dev_ctl(caller) machine_t *caller;{ PROLOGUE(dev_ctl_t,dev_ctl_func); } dev_ctl_func(p) dev_ctl_t *p;{ DISPATCH1; /* IT'S A NO-OP. DELAY A LITTLE AND RETURN */ BREAK(1,L1); RETURN(1); } /* MH_CTL_T: MATERIAL HANDLING TASK STATE MACHINE */ typedef struct { STDVARS; } mh_ctl_t; mh_ctl(caller) machine_t *caller;{ PROLOGUE(mh_ctl_t,mh_ctl_func); } mh_ctl_func(p) mh_ctl_t *p;{ DISPATCH1; /* IT'S A NO-OP. DELAY A LITTLE AND RETURN */ BREAK(1,L1); RETURN(1); } [LISTING TWO] /* fast.c */ #include #include "fast.h" #pragma check_stack(off) #define STDVARS int state; int (*func)(); struct machine_struct *caller typedef struct machine_struct { STDVARS; } machine_t; /* STACK STRUCTURES FOR CACHEING USED STATE MACHINES */ struct mstk_struct { int n; struct machine_struct *stk[64]; }; #define M_ALLOC(mstk,size,p) {\ if (mstk.n <= 0) p = (struct machine_struct*)malloc(size);\ else p = mstk.stk[--mstk.n];\ } #define M_FREE(mstk,p) {\ if (mstk.n >= 64) free(p);\ else mstk.stk[mstk.n++] = p;\ } #define PROLOGUE(typ,f,stk)\ register typ *p;\ extern int f();\ M_ALLOC(stk,sizeof(*p),p);\ p->caller = caller;\ p->func = f;\ p->state = 0;\ (*p->func)(p); #define BREAK(n,lab) p->state=(n); ENQUE(p); return; lab: #define CALL(n,lab,expr) p->state=(n); (expr); return; lab: machine_t * ptemp=NULL; int retn_val=0; #define RETURN(v,stk)\ ptemp=p->caller;\ retn_val=(v);\ M_FREE(stk,p);\ if (ptemp){(*ptemp->func)(ptemp);}; unsigned int ninq=0; machine_t *queue[256]; machine_t **enq = queue, **deq = queue; #define ENQUE(p) {*enq++ = p; if (enq>=(queue+256)) enq=queue; ninq++;} #define DEQUE(p) {p = *deq++; if (deq>=(queue+256)) deq=queue; ninq--;} int jobs_started=0; int jobs_completed=0; #define NBOPS (rand()%5 + 10) #define NTASK 10 int njobs = 1000; main(){ register machine_t *p; /* REPEAT UNTIL ALL JOBS ARE COMPLETE */ while(jobs_completed < njobs){ /* RUN WHATEVER CAN BE RUN */ if (ninq){ DEQUE(p); (*p->func)(p); } /* IF < 100 JOBS STARTED AND < 10 JOBS IN PROCESS */ if (jobs_startedjobid = jobs_started++; p->nbops = NBOPS; /* FOR EACH OPERATION */ for (p->i=0; p->i < p->nbops; p->i++){ p->ntask = NTASK; /* FOR EACH TASK */ for (p->taskid=0; p->taskid < p->ntask; p->taskid++){ /* DO DEVICE CONTROL */ BREAK(1,L1); /* DO MATERIAL HANDLING */ BREAK(2,L2); } } jobs_completed++; RETURN(1,jobstk); } Example 1: (a) struct { ... } a[...]; int i; while ( ... ){ ... a[i] ... } (b) float num, newnum; char digit; while (...){ newnum = (int)(num / 10); digit = num - newnum * 10 + '0'; num = newnum; ... store digit for output ... } Example 2: (a) /* IF ALL TASKS DONE, SEND ITC_ACKOP AND DELETE OP */ if (ptop->current_task >= ILST_LENGTH(ptop->tasklist)){ (b) /* FOR EACH OPERATION REQUEST */ for ( ptop = ILST_FIRST(oplist); ptop != NULL; ptop = ILST_NEXT(oplist,ptop) ){ (c) ptask = ILST_NTH(ptop->tasklist,ptop->current_task); Example 3: (a) ILST_APPEND(ptop->tasklist,ptask); (b) ILST_APPEND(trnque,ptrn); Example 4: (a) for (l=oplist; l; l=l->next){ ptop = l->thing; if (ptop->id==ptn->tskid) break; } if (ptop==NULL){ /* ERROR: INVALID OPERATION ID */ } (b) for (l=oplist ; l && ((operation_t*)l->thing)->id != ptn->tskid ; l=l->next){ } if (l==NULL){ /* ERROR: INVALID OPERATION ID */ } ptop = l->thing;