Not to be confused with an endless loop, an empty loop is simply a loop with no body -- a place where the CPU sits and burns cycles without doing anything significant. The C code for (a==0;a<10;a++) {} is an empty loop that cycles ten times.

Why might one want to do this, you ask? It was particularly popular back in the early days of computing. Back then, you knew the exact speed of the computer your program would be running on. By taking the clock speed of the computer, and dividing it into the number of cycles it took to execute one iteration of the loop, one could cause the system to pause for a specific duration of time.

As an example: say your program is running on a computer with a 5 MHz clock. In other words, the system goes through five million cycles per second. Now, let's assume that each iteration of the loop takes fifty cycles (a long time, but perhaps we're using a BASIC interpreter). Thus, one hundred thousand iterations of the loop can be executed in one second. Now we have a number to use; if we want to pause for a second, we simply use code such as FOR A=0 TO 100000:NEXT.

Programs that relied on this method of timing had problems, though, when they were run on systems with differing clock speeds -- since, obviously, a faster system could perform more iterations in one second. One solution to this problem was to "calibrate" the loop: the system's internal clock would be measured as a loop was performed, and the result of this operation stored. Thus, the program would know how long it took to execute a loop of a certain number of iterations, and through simple arithmetic could determine how many iterations would elapse in a second. However, This method relied on the target computer having a real-time clock; often this wasn't the case (the Apple II, the Commodore 64, the original IBM PC).

As hardware and software architecture has become more complex, empty loops have become somewhat less valuable as a timing tool. New processors can execute multiple instructions per clock cycle, or even multiple instructions at once (parallelism). Compiler efficiency has increased considerably; some smarter compilers can even realize that a loop was empty and optimize it right out of existence. Preemptive multitasking systems can also cause problems, since the operating system can intervene at any time and switch control over to another program. "Calibrating" the loop does not help in this case, as the time it takes to execute a specific number of iterations may vary.

However, there are still places where empty loops can serve as reliable timing tools -- specifically, where the execution time of one cycle of the loop can be determined and is guaranteed to be a constant. This is the case with some embedded systems, save for any interrupts that might occur. In fact, empty loops may be the most efficient method of "sitting and spinning" for systems that have no external interrupt triggers or real-time clock.

ariels also points out that the Linux kernel may use some extremely small calibrated empty loops to wait. BogoMIPS may have something to do with this. Anyone more versed in kernel architecture, feel free to clarify this.

arcanis noted that preemptive multitasking was hardly the "death knell" for timing loops; this caused me to go over the entire writeup to fix misleading and just plain incorrect information. Two years later, I've found that timing loops can still be extremely useful, especially at the assembly level!

Log in or register to write something here or to contact authors.