chris@mimsy.UUCP (Chris Torek) (07/18/89)
In article <579@targon.UUCP> andre@targon.UUCP (andre) writes: >Yes, I think you can write a program that checks the nr of registers, >but you will get the answer at compile time, not run time :-) > >the program [takes the address of variables declared with the `register' keyword] >The compiler will assing n1 to n{x} to the registers it has available and >the rest will be normal variables. You can take the address of a variable >but not of a register, so the compiler will start to complain at the first >line that tries to take the address of a register. That's why the a = &n{x}; >lines must count backwards. The first statement is false. The first part of the second statement is true. The second part of the second statement is false. A compiler is free to assign variables to registers without regard to the `register' keyword. The programmer is required not to attempt to use the `&' address-of operator on any variables declared with the `register' keyword. That is, you cannot take the address of a register variable, whether or not it is in fact in a machine register. The real problem here is that the compiler is free to use none, any or all machine registers whenever it wants; it is possible (if stupid) for a compiler to switch from `none' to `all' simply because the code was compiled in the afternoon rather than the morning. That said, the way to find out how many registers the compiler will use on your code is to compile your code and count how many registers were used. This is completely reliable, although not necessarily repeatable. It is also likely to be a useless statistic. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
walter@hpclwjm.HP.COM (Walter Murray) (07/19/89)
> That said, the way to find out how many registers the compiler will > use on your code is to compile your code and count how many registers > were used. This is completely reliable, although not necessarily > repeatable. It is also likely to be a useless statistic. Agreed. Another way to find out the extent to which the register specifier will have effect is to read the manual. When you take delivery of your first ANSI-conforming C compiler, it will come with some sort of a manual. This is one of the items that are required to be documented there. Walter Murray -------------
johnm@occam.ksr.com (John Martin) (07/20/89)
In article <18603@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: > >... the way to find out how many registers the compiler will >use on your code is to compile your code and count how many registers >were used. This is completely reliable, although not necessarily >repeatable. It is also likely to be a useless statistic. >-- >In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) >Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris All true. For amusement's sake though, here is a C program that finds out what register variables one can have by looking at their effect on execution time, which presumably is why one might want to know in the first place. The following example works with gcc and cc on Sun 3's and 4's running SunOS 3.5, and should work on most UNIX machines with little change. As is frequently noted, timing routines are not very portable across OS's; sorry about that. On the systems I've tried ( Sun 3/75, 3/60, 3/140, 4/280 ) the register-variable loops run about 2.2 - 2.5 times faster than the memory-variable loops. The SPARC is about twice as fast as a 3/75, achieving about 1 million Hz for register loops. For the curious the register counts are: gcc : 10 registers on MC68020, 14 on SPARC. Sun cc : 6 registers on MC68020, 14 on SPARC. Counting address registers, and finding whether address and integer registers are separate or part of the same general register set, or being used in a slick way by the compiler, are left as exercises to the reader. The answers will have some surprises -- for example, the gcc MC68020 result of 10 integer registers is interesting, since an MC68020 only has 8 data registers. As Chris Torek says, the only way to be sure what's happening is to look at the assembler. /* showregs.c How many registers will your compiler give you? * * On a UNIX system: * * % cc showregs.c * _or_ % gcc -traditional showregs.c * _or_ % gcc -ansi showregs.c * * ( one flag or the other is needed to make PRINTLINE expand correctly) * * % a.out > regs * % graph -b -a -y 0 -g 1 < regs | plot */ #include <sys/time.h> #include <signal.h> #ifdef __STDC__ #define PRINTLINE(xx) (void) printf( "%8u %s\n", xx, #xx) #else old-style macro substitution inside quotes #define PRINTLINE(xx) (void) printf( "%8u xx\n", xx) #endif /* Execute a simple unrolled loop for a fixed time & count iterations. */ #define LOOPS(var) \ var = 0; \ timing = 1; \ if ( setitimer( ITIMER_VIRTUAL, &itval, 0) ) \ perror( "Starting timer"); \ while (timing) { var--; var++; var--; var++; var++; } \ PRINTLINE(var) unsigned timing; int vtalrmhandler() { timing = 0; return 0; } int main() { register unsigned r01, r02, r03, r04, r05, r06, r07, r08, r09, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20; struct itimerval itval; /* Set up to use the UNIX virtual timer for 200 milliseconds * of process time. */ itval.it_value.tv_sec = 0; itval.it_value.tv_usec = 200*1000; itval.it_interval.tv_sec = itval.it_interval.tv_usec = 0; (void) signal( SIGVTALRM, vtalrmhandler); LOOPS(r01); LOOPS(r02); LOOPS(r03); LOOPS(r04); LOOPS(r05); LOOPS(r06); LOOPS(r07); LOOPS(r08); LOOPS(r09); LOOPS(r10); LOOPS(r11); LOOPS(r12); LOOPS(r13); LOOPS(r14); LOOPS(r15); LOOPS(r16); LOOPS(r17); LOOPS(r18); LOOPS(r19); LOOPS(r20); } /* end main */ /* EOF */ John H. Martin harvard!ksr!johnm Kendall Square Research Corporation johnm@ksr.com 170 Tracer Lane Waltham, MA 02154 "A creative economy is the fuel of magnificence." -- Emerson Disclaimer: My only organizationally-endorsed position is prone.
tim@crackle.amd.com (Tim Olson) (07/20/89)
In article <547@ksr.UUCP> johnm@ksr.UUCP (John Martin) writes: | In article <18603@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: | > | >... the way to find out how many registers the compiler will | >use on your code is to compile your code and count how many registers | >were used. This is completely reliable, although not necessarily | >repeatable. It is also likely to be a useless statistic. | >-- | >In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) | >Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris | | All true. For amusement's sake though, here is a C program that finds | out what register variables one can have by looking at their effect on | execution time, which presumably is why one might want to know in the | first place. The following example works with gcc and cc on Sun | 3's and 4's running SunOS 3.5, and should work on most UNIX machines | with little change. As is frequently noted, timing routines are not | very portable across OS's; sorry about that. This method still doesn't work reliably, for a couple of reasons. First, the lifetimes of the declared register variables don't overlap, so a compiler with register coloring could see that they all could be replaced with a single shared register. Second, even if a variable was forced to memory, the LOOP macro test sequence: while (timing) { var--; var++; var--; var++; var++; } \ will not necessarily load and store the value to memory at each increment/decrement. Rather, the compiler is free to load the value into a register before entry to the while-loop, computing all values in that register, and storing the final result back to the variable's memory address at loop exit. -- Tim Olson Advanced Micro Devices (tim@amd.com)
johnm@occam.ksr.com (John Martin) (07/21/89)
In article <26423@amdcad.AMD.COM> tim@amd.com (Tim Olson) writes: >In article <547@ksr.UUCP> johnm@ksr.UUCP (John Martin) writes: >> >> [ A program to time variable access to look for registers.] >> >This method still doesn't work reliably, for a couple of reasons. >First, the lifetimes of the declared register variables don't overlap, >so a compiler with register coloring could see that they all could be >replaced with a single shared register. Second, even if a variable was >forced to memory, the LOOP macro test sequence: > > while (timing) { var--; var++; var--; var++; var++; } \ > >will not necessarily load and store the value to memory at each >increment/decrement. Rather, the compiler is free to load the value >into a register before entry to the while-loop, computing all values in >that register, and storing the final result back to the variable's >memory address at loop exit. If a compiler does coloring with optimization off, initialize the variables so their lifetimes will be coterminous. A compiler that doesn't always store memory variables to memory before their next read with optimization off would break this test -- and any standard debugger. Are there really compilers that do not regard coloring or dynamic transfer of a variable between register and memory as suppressable optimizations? I'd like a list, so I can mark them in my mental map with the label "Here be monsters." Unless they come with good debuggers that can understand them, they'd sure be obnoxious to use for program development -- the only reliable debugger would be assembly-level. John H. Martin harvard!ksr!johnm Kendall Square Research Corporation johnm@ksr.com 170 Tracer Lane Waltham, MA 02154 "A creative economy is the fuel of magnificence." -- Emerson Disclaimer: My only organizationally-endorsed position is prone.