#ifndef lint static char sccsid[] = "@(#)performance.c 9.2 88/01/19 Copyright 1987 Sun Micro"; #endif /* * Copyright (c) 1987 by Sun Microsystems, Inc. */ /* * Performance monitoring of the PostScript interpreter */ /* * new PostScript primitives: * * array executioncount int * print the number of times array has been executed since * the last call to clearexecutioncount on that array or since * the interpreter started; for instance, executive is * implemented as an executable array so the PostScript command * /executive load executioncount = * will print the number of times executive has been executed * * array clearexecutioncount - * reset the execution count of array to zero * * - collectprimitivecounts - * start recording the number of times each built-in primitive * is executed; see dumpprimitivelog * * file dumpprimitivecounts - * for each primitive which has been executed since the last * call to logprimitives, print out how many times it has been * executed; the list is sorted by the count from greatest to * least number of calls; the data is printed to the file * (BEWARE: if the output file is a terminal emulator running * on the same NeWS server and more than 4096 characters are * printed, the server may hang due to a bug in psio) * * - collectlookupdata - * start collecting statistics about NeWS' dictionary lookup * algorithm * * - dumplookupdata - * print a summary of the dictionary lookup statistics * * - dumpqueuedata - * print some statistics about the event queue */ #ifdef REF #include #include #endif #include "PostScript.h" #ifdef PERFORMANCE #ifndef SYSVREF #include #endif executioncount(ee) register struct execution_environment *ee; { register struct object *optop = ee->optop; register struct body *ary; int count; if (optop[-1].type != array_type) { ee->error_code = typecheck_error_code; return; } else ee->error_code = no_error_code; ary = body_of(optop-1); count = ary->body.array.exec_count; object_decref(optop-1); set_fixed_object(optop-1,fracti(count)); } clear_execution_count(ee) register struct execution_environment *ee; { register struct object *optop = ee->optop; register struct body *ary; if (optop[-1].type != array_type) { ee->error_code = typecheck_error_code; return; } else ee->error_code = no_error_code; ary = body_of(optop-1); ary->body.array.exec_count = 0; ee->optop--; object_decref(optop-1); } /* * a puts for psio (what I really want is psio_fprintf but I'm too lazy) */ ps_puts(str, f) register char *str; register PSFILE *f; { register int len = strlen(str), c; for (c = 0; c < len; c++) psio_putc(*str++, f); } /* * the following routines allow the server to log how many times * each primitive is called; the name of the primitive is looked * up in a hash table and the associated value (number of calls) * is incremented; the first time a primitive is called, its name * is also stored in an array; the data stored in the hash table * is a pointer to the location in the array where the count should * be stored; the array is sorted by the execution before it is printed */ #include #include "psio.h" #define MAXOPCOUNT 500 /* maximum number of operators expected */ struct prim_count { char *name; unsigned long calls; } call_table[MAXOPCOUNT]; int primitive_count; int count_primitive_calls; turn_on_primitive_logging(ee) struct execution_environment *ee; { if (count_primitive_calls) hdestroy(); (void) hcreate(MAXOPCOUNT); primitive_count = 0; count_primitive_calls = 1; } turn_off_primitive_logging(ee) struct execution_environment *ee; { register struct object *optop = ee->optop; int compare_primitives(); register c, len; register PSFILE *f; char buf[128]; if (optop[-1].type != file_type) { ee->error_code = typecheck_error_code; return; } f = body_of(optop -1)->body.file.outbuf; if (f == 0 || psio_error(f)) { ee->error_code = ioerror_error_code; return; } ee->optop--; object_decref(optop-1); if (!count_primitive_calls) { ps_puts("No data.\n", f); return; } qsort(call_table, primitive_count, sizeof(struct prim_count), compare_primitives); for (c = primitive_count-1; c >= 0; c--) { sprintf(buf, "%lu\t%s\n", call_table[c].calls, call_table[c].name); ps_puts(buf, f); } primitive_count = 0; hdestroy(); count_primitive_calls = 0; } compare_primitives(a, b) struct prim_count *a, *b; { return((int)(a->calls - b->calls)); } primitive_called(name) char *name; { ENTRY name_to_enter, *old_value, *hsearch(); name_to_enter.key = name; old_value = hsearch(name_to_enter, FIND); if (old_value == NULL) { call_table[primitive_count].name = name; call_table[primitive_count].calls = 1; name_to_enter.data = (char *) &call_table[primitive_count++].calls; if (hsearch(name_to_enter, ENTER) == NULL) printf("Primitive logging hash table full!\n"); } else { unsigned long *value; value = (unsigned long *) old_value->data; (*value)++; } } /* * the following routines are for controlling monitoring of the dictionary * search algorithm */ int collect_lookup_data; /* flag to turn collection on or off */ unsigned long total_lookups; /* total number of keys looked up in the dictionary stack since data collection was enabled */ int high_dict_count; /* greatest number of dictionaries which had to be checked to find a key */ int low_dict_count; /* least number of dictionaries which had to be checked to find a key (probably 1) */ unsigned long total_dict_count; /* total number of dictionaries checked; total_dict_count / total_lookups gives the average number of dictionaries which had to be checked per key */ int dict_count; /* number of dictionaries checked on current key (temporary storage) */ int high_compare_count; /* greatest number of compares which had to be made to find a key on the dict stack */ int low_compare_count; /* least number of compares which had to be made to find a key on the dict stack */ unsigned long total_compare_count; /* total number of compares made; total_compare_count / total_lookups gives the average number of compares which had to be made per key */ int compare_count; /* number of compares made on current key (temporary storage) */ start_dict_data_collection(ee) struct execution_environment *ee; { collect_lookup_data = 1; total_lookups = 0; high_dict_count = 0; low_dict_count = 9999; total_dict_count = 0; dict_count = 0; high_compare_count = 0; low_compare_count = 9999; total_compare_count = 0; compare_count = 0; } dump_collected_dict_data(ee) struct execution_environment *ee; { register c, len; register PSFILE *f; char buf[128]; f = ee->stdprnt->body.file.outbuf; if (f == 0 || psio_error(f)) { ee->error_code = ioerror_error_code; return; } if (!collect_lookup_data) { ps_puts("No data.\n", f); return; } collect_lookup_data = 0; sprintf(buf, "Number of key lookups: %ul\n", total_lookups); ps_puts(buf, f); sprintf(buf, "\nMinimum number of dicts checked: %d\n", low_dict_count); ps_puts(buf, f); sprintf(buf, "Maximum number of dicts checked: %d\n", high_dict_count); ps_puts(buf, f); sprintf(buf, "Average number of dicts checked: %.1f\n", (double)total_dict_count / (double)total_lookups); ps_puts(buf, f); sprintf(buf, "\nMinimum number of compares made: %d\n", low_compare_count); ps_puts(buf, f); sprintf(buf, "Maximum number of compares made: %d\n", high_compare_count); ps_puts(buf, f); sprintf(buf, "Average number of compares made: %.1f\n", (double)total_compare_count / (double)total_lookups); ps_puts(buf, f); sprintf(buf, "\nAverage number of compares made per dict: %.1f\n", (double)total_compare_count / (double)total_dict_count); ps_puts(buf, f); } int queue_length; int max_queue_length; long queue_event_count; int queue_compares; dump_queue_data(ee) struct execution_environment *ee; { register PSFILE *f; char buf[128]; f = ee->stdprnt->body.file.outbuf; if (f == 0 || psio_error(f)) { ee->error_code = ioerror_error_code; return; } sprintf(buf, "Current queue length: %d\n", queue_length); ps_puts(buf, f); sprintf(buf, "Maximum queue length: %d\n", max_queue_length); ps_puts(buf, f); sprintf(buf, "Average number of compares to put an event on queue: %.1f\n", (double)queue_compares / (double)queue_event_count); ps_puts(buf, f); } #endif /* PERFORMANCE */ initialize_performance() { #ifdef PERFORMANCE define_operator("executioncount", executioncount, 0, 1, 1); define_operator("clearexecutioncount", clear_execution_count, 0, 1, 0); define_operator("collectprimitivecounts", turn_on_primitive_logging, 0, 0, 0); define_operator("dumpprimitivecounts", turn_off_primitive_logging, 0, 1, 0); count_primitive_calls = 0; define_operator("collectlookupdata", start_dict_data_collection, 0, 0, 0); define_operator("dumplookupdata", dump_collected_dict_data, 0, 0, 0); collect_lookup_data = 0; define_operator("dumpqueuedata", dump_queue_data, 0, 0, 0); queue_length = 0; queue_event_count = 0; queue_compares = 0; max_queue_length = 0; #endif }