/* * ATPERF -- PC Tech Journal AT Hardware Performance Test * * Version 1.01 * Originally written 05/17/86 * Last modified 10/25/86 * Changes: * 1. Correct to work with zero-wait state memory. * 2. Detects when not running on 80286 and issues * a warning. * * Copyright (c) 1986, Ziff Communications Company * Program by: Ted Forgeron, Paul Pierce * * Measures clock rates and memory speeds * of AT compatible computers. */ /* * Measurements are accumulated in the acctime array. Each * element is identified by one of these constants. * IRW Instruction fetch, word * MRB RAM read byte * MWB RAM write byte * RRB ROM read byte * MRW RAM read word * MWW RAM write word * RRW ROM read word * ERB EMM read byte * EWB EMM write byte * ERW EMM read word * EWW EMM write word * VWB Video write byte * VWW Video write word */ #define IRW 0 #define MRB 1 #define MWB 2 #define RRB 3 #define MRW 4 #define MWW 5 #define RRW 6 #define ERB 7 #define EWB 8 #define ERW 9 #define EWW 10 #define VWB 11 #define VWW 12 #define VARS 13 /* Timer rate in MHz */ #define TIMER2_RATE 1.193180 /* Number of processor clocks in a multiply instruction */ #define MULCLKS 21 /* Overhead in the multiply test */ #define MULOVH ( 15 + 14*count/100 ) /* Overhead in the mov instruction test */ #define MOVOVH ( clktime * (15 + 14*count/100) ) /* Overhead in pusha instruction test */ #define WPOVH ( clktime * (15 + 9*count/200) ) /* Number of numeric processor clocks in a FP divide */ #define FPCLKS 203 /* Processor overhead in the FP divide test */ #define FPOVH ( clktime * 9 * FPCOUNT ) /* Count for most tests */ #define COUNT 1000 /* Count for the f. p. divide test */ #define FPCOUNT 100 /* Number of trials for each test */ #define TRIALS 100 int cpu; /* CPU type: 0=86/88,1=80186,2=80286,3=80386 */ double clkrate; /* Processor clock rate, MHz */ double clktime; /* Processor clock period, usec */ double fprate; /* FP processor clock rate, MHz */ double fpacc; /* FP processor clock period accumulator */ int emmok; /* Set if extended memory is present */ int ndpok; /* Set if math coprocessor is present */ /* * Main program. */ main(argc, argv) int argc; char **argv; { double raw, brw, wrw, rif; /* Variables for raw data */ double acctime[VARS]; /* Accumulators for speeds */ int count; /* Number of ops per trial */ int trials; /* Number of repetitions */ register int i; count = COUNT; trials = TRIALS; /* * Find out the CPU type and set * parameters accordingly. */ cpu = cpu_type(); if (cpu != 2) { printf("\nThis version of ATPERF is for "); printf("80286-based machines only.\n"); exit(); } /* * Measure the clock rate by executing * multiply instructions. Each multiply * takes a fixed number of clock cycles. */ clktime = 0; for (i = 0; i < trials; i++) { /* * Obtain the number of clock ticks for * "count" multiplies. */ raw = multime(count); /* * Accumulate the clock time in microseconds * by adjusting for the timer rate, * number of clocks per multiply, * instruction count, and test overhead. */ clktime += raw / (TIMER2_RATE * (MULCLKS*count + MULOVH)); } /* * Calculate the average clock period by dividing by * the number of trials. The clock rate is the * inverse of the clock period. */ clktime /= trials; clkrate = 1.0/clktime; /* * Determine whether there a math coprocessor * in the system. */ ndpok = ndp_present(); /* * Determine whether there is extended memory in the * system and allocate a piece of it for testing. */ emmok = (setup_emm() == 0); /* * Detect the type of video card and save the * information for the video measurements. */ setup_video(); /* * Clear all of the memory speed accumulators. */ for (i = 0; i < VARS; i++) acctime[i] = 0; /* * Do the memory speed tests. */ for (i = 0; i < trials; i++) { /* * Obtain the number of timer ticks for * "count" mov instructions, which are * limited by memory fetch time. */ rif = wmovtime(count); /* * Accumulate the number of microseconds * per instruction fetch by adjusting for * the timer rate, test overhead, and * instruction count. */ acctime[IRW] += (rif / TIMER2_RATE - MOVOVH) / count; /* * Measure byte read+write time * measuring movs instructions. */ raw = bmvstime(count); acctime[MRB] += raw/(TIMER2_RATE*count); /* * Calculate ROM read time by * measuring movs from ROM to RAM. */ raw = bromtime(count); acctime[RRB] += raw/(TIMER2_RATE*count); /* * Measure word write using the * pusha instruction. */ wrw = wpshtime(count) - WPOVH; acctime[MWW] += wrw/(TIMER2_RATE*count); /* * Measure movs (read+write) time. */ raw = wmvstime(count); acctime[MRW] += raw/(TIMER2_RATE*count); raw = wromtime(count); acctime[RRW] += raw/(TIMER2_RATE*count); /* * If EMM is present, do measurements * on it using the same techniques. */ if (emmok) { /* * Measure byte mov in EMM. */ raw = bemmtime(count); acctime[ERB] += raw/(TIMER2_RATE*count); /* * Measure word write, * calculate word read. */ wrw = wemptime(count) - WPOVH; acctime[EWW] += wrw/(TIMER2_RATE*count); raw = wemmtime(count); acctime[ERW] += raw/(TIMER2_RATE*count); } /* * Measure byte and word writes * into video RAM. */ raw = bvidtime(count); acctime[VWB] += raw/(TIMER2_RATE*count); raw = wvidtime(count); acctime[VWW] += raw/(TIMER2_RATE*count); } /* * Calculate averages for all measurements. */ for (i = 0; i < VARS; i++) acctime[i] /= trials; /* * Adjust word write times by subtracting * the instruction fetch time. */ acctime[MWW] -= acctime[IRW]/16; if (emmok) acctime[EWW] -= acctime[IRW]/16; /* * Adjust for extra time per instruction when * measuring zero wait state memory. */ if (acctime[MWW] < 3*clktime) acctime[MWW] -= clktime/8; if (emmok) if (acctime[EWW] < 3*clktime) acctime[EWW] -= clktime/8; /* * Calculate byte write time by assuming the same * ratio between read and write as for word access. */ acctime[MWB] = acctime[MRB] * acctime[MWW] / acctime[MRW]; if (emmok) acctime[EWB] = acctime[ERB] * acctime[EWW] / acctime[ERW]; /* * Calculate read times by subtracting write time from * mov (read+write) time. */ acctime[MRB] = acctime[MRB] - acctime[MWB]; acctime[MRW] = acctime[MRW] - acctime[MWW]; acctime[RRB] = acctime[RRB] - acctime[MWB]; acctime[RRW] = acctime[RRW] - acctime[MWW]; if (emmok) { acctime[ERB] = acctime[ERB] - acctime[EWB]; acctime[ERW] = acctime[ERW] - acctime[EWW]; } /* * Release EMM memory page. */ if (emmok) finish_emm(); /* * Calculate numeric processor clock * rate using floating point divide * instructions, using the same * technique as was used to measure * the processor clock rate. */ if (ndpok) { fprate = 0; for (i = 0; i < trials; i++) { raw = fptime(FPCOUNT); fpacc += (raw / TIMER2_RATE - FPOVH) / FPCLKS / FPCOUNT; } fpacc /= trials; fprate = 1.0/fpacc; } /* * Display the basic measurement results and * performance index relative to a 8 MHz AT. */ printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); printf("ATPERF -- PC Tech Journal AT Hardware "); printf("Performance Test\n"); printf("Version 1.01, Copyright (c) 1986 Ziff "); printf("Communications Co.\n"); printf("Written by Ted Forgeron and Paul Pierce\n"); printf("IBM PC/AT model 339 (8 MHz) = 1.00 for relative "); printf("measurements.\n "); printf(" Byte Word Relative\n"); printf("Average RAM instruction fetch:"); printf(" "); printf("%#10.2g uS", acctime[IRW]); printf("%#10.2g\n", 0.403/acctime[IRW]); printf("Average RAM read time: "); printf("%#10.2g uS ", acctime[MRB]); printf("%#10.2g uS", acctime[MRW]); printf("%#10.2g\n", 0.401/acctime[MRW]); printf("Average RAM write time: "); printf("%#10.2g uS ", acctime[MWB]); printf("%#10.2g uS", acctime[MWW]); printf("%#10.2g\n", 0.401/acctime[MWW]); if (emmok) { printf("Average EMM read time: "); printf("%#10.2g uS ", acctime[ERB]); printf("%#10.2g uS", acctime[ERW]); printf("%#10.2g\n", 0.402/acctime[ERW]); printf("Average EMM write time: "); printf("%#10.2g uS ", acctime[EWB]); printf("%#10.2g uS", acctime[EWW]); printf("%#10.2g\n", 0.402/acctime[EWW]); } printf("Average ROM read time: "); printf("%#10.2g uS ", acctime[RRB]); printf("%#10.2g uS", acctime[RRW]); printf("%#10.2g\n", 0.401/acctime[RRW]); printf("Average Video write time: "); printf("%#10.2g uS ", acctime[VWB]); printf("%#10.2g uS", acctime[VWW]); printf("%#10.2g\n", 2.415/acctime[VWW]); printf("CPU clock rate: "); printf("%#4.1g MHz", clkrate); printf(" Relative: %#4.2g\n", clkrate/8.0); if (ndpok) { printf("Math Coprocessor clock rate: "); printf("%#4.1g MHz", fprate); printf(" Relative: %#4.2g\n", fprate/5.33); } /* * Calculate refresh overhead from instruction * fetch time by assuming that each fetch takes * an exact multiple of the clock period. The * difference between average time and the time * for an individual fetch is due to memory * refresh cycles. */ raw = acctime[MRB] / clktime; printf("Refresh overhead: %#2.1g%%\n", ( (raw - (int)raw) / (int)raw ) * 100); /* * Print information about the memory based * on the speed measurements. */ printf("\nMemory "); printf(" Access width Wait states\n"); analyze("RAM read", acctime[MRB], acctime[MRW]); analyze("RAM write", acctime[MWB], acctime[MWW]); if (emmok) { analyze("EMM read", acctime[ERB], acctime[ERW]); analyze("EMM write", acctime[EWB], acctime[EWW]); } analyze("ROM read", acctime[RRB], acctime[RRW]); analyze("Video write", acctime[VWB], acctime[VWW]); } /* * analyze * * This procedure deduces information about the memory based on * the measured times. * If byte (8 bits) and word (16 bits) times are different then * the memory is byte oriented since each word operation takes * two byte operations. Otherwise, if the byte and word * times are about the same, the memory is word oriented and can * access either a word or a byte in a single memory cycle. * * Each memory access takes an exact number of processor clock * cycles. The first two are required by the processor, but * any additional cycles are determined by the memory and are * called wait states (because the processor is waiting for * the memory.) */ analyze(name, btime, wtime) char *name; double btime; double wtime; { /* * Print the heading */ printf("%-12s", name); /* * Determine whether the memory is byte * oriented, word oriented, or neither. * (If neither, the data are suspect.) */ if (btime > wtime*0.75 && btime < wtime*1.25) printf(" Word "); else if (btime*2 > wtime*0.75 && btime*2 < wtime*1.25) printf(" Byte "); else printf(" Strange"); /* * Determine the number of wait states * by subtracting two processor clock times, * dividing by the clock period, * and rounding down to an integer. */ printf(" %6.0f\n", (btime - 2*clktime) / clktime); }