使用 setitimer() 和 ITIMER_VIRTUAL 时,是什么导致虚拟 运行 时间变慢?
What causes the virtual run time to go slow when using setitimer() and ITIMER_VIRTUAL?
为了研究ITMER_REAL
和ITIMER_VIRTUAL
之间的差异,我编写了以下程序。
#include <stdlib.h> // exit(), EXIT_FAILURE, EXIT_SUCCESS
#include <signal.h> // sigaction()
#include <stdio.h> // printf(), fprintf(), stdout, stderr, perror(), _IOLBF
#include <string.h> // memset()
#include <sys/time.h> // ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF, struct itimerval, setitimer()
#include <stdbool.h> // true, false
#include <limits.h> // INT_MAX
#define TIMEOUT 50 // ms
#define TIMER_TYPE ITIMER_VIRTUAL // Type of timer.
/* The three types of timers causes different signals.
type: type of timer, one of ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF.
return value: the signal generated by the timer.
*/
int timer_signal(int timer_type) {
int sig;
switch (timer_type) {
case ITIMER_REAL:
sig = SIGALRM;
break;
case ITIMER_VIRTUAL:
sig = SIGVTALRM;
break;
case ITIMER_PROF:
sig = SIGPROF;
break;
default:
fprintf(stderr, "ERROR: unknown timer type %d!\n", timer_type);
exit(EXIT_FAILURE);
}
return sig;
}
/* Set a timer and a handler for the timer.
Arguments
type: type of timer, one of ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF.
handler: timer signal handler.
ms: time in ms for the timer.
*/
void set_timer(int type, void (*handler) (int), int ms) {
struct itimerval timer;
struct sigaction sa;
/* Install signal handler for the timer. */
memset (&sa, 0, sizeof (sa));
sa.sa_handler = handler;
sigaction (timer_signal(type), &sa, NULL);
/* Configure the timer to expire after ms msec... */
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = ms*1000;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 0;
if (setitimer (type, &timer, NULL) < 0) {
perror("Setting timer");
exit(EXIT_FAILURE);
};
}
/* Timer signal handler. */
void timer_handler (int signum) {
static int count = 0;
fprintf (stderr, "======> timer (%03d)\n", count++);
set_timer(TIMER_TYPE, timer_handler, TIMEOUT);
}
/* Calculate the nth Fibonacci number using recursion. */
int fib(int n) {
switch (n) {
case 0:
return 0;
case 1:
return 1;
default:
return fib(n-1) + fib(n-2);
}
}
/* Print the Fibonacci number sequence over and over again.
This is deliberately an unnecessary slow and CPU intensive
implementation where each number in the sequence is calculated recursively
from scratch.
*/
void fibonacci_slow() {
int n = 0;
while (true) {
printf(" fib(%d) = %d\n", n, fib(n));
n = (n + 1) % INT_MAX;
}
}
/* Print the Fibonacci number sequence over and over again.
This implementation is much faster than fibonacci_slow().
*/
void fibonacci_fast() {
int a = 0;
int b = 1;
int n = 0;
int next = a + b;
while(true) {
printf(" fib(%d) = %d\n", n, a);
next = a + b;
a = b;
b = next;
n++;
if (next < 0) {
a = 0;
b = 1;
n = 0;
}
}
}
int main () {
/* Flush each printf() as it happens. */
setvbuf(stdout, 0, _IOLBF, 0);
setvbuf(stderr, 0, _IOLBF, 0);
set_timer(TIMER_TYPE, timer_handler, TIMEOUT);
// Call fibonacci_fast() or fibonacci_fast()
fibonacci_fast();
// fibonacci_slow();
}
从 main()
我呼叫 fibonacci_fast()
或 fibonacci_slow()
。
正如预期的那样,当使用 ITMER_REAL
从 main()
调用 fibonacci_fast()
或 fibonacci_slow()
时,计时器滴答之间的挂钟时间没有差异。
当使用 ITIMER_VIRTUAL
和 main()
调用 fibonacci_fast()
设置计时器时,每个计时器滴答之间的挂钟时间非常长,但如果 main()
调用 fibonacci_slow()
每个计时器滴答之间的挂钟时间要小得多。
我想了解为什么 fibonacci_fast()
使虚拟运行时比 fibonacci_slow()
慢得多。与 fibonacci_slow()
相比,CPU 调度程序在使用 fibonacci_fast()
时给进程的 CPU 时间少得多吗?如果是,为什么?如果不是,还有什么可以解释差异?
ITIMER_VIRTUAL
只有 运行s 当进程处于用户模式 运行ning 时。在您的两个斐波那契例程中,都有一个 printf
调用涉及同步写入数据的系统调用,这不计入虚拟时间。 fibonacci_fast
实际上大部分时间都花在 printf
和这个系统调用上,所以计时器似乎 运行 慢得多。
为了研究ITMER_REAL
和ITIMER_VIRTUAL
之间的差异,我编写了以下程序。
#include <stdlib.h> // exit(), EXIT_FAILURE, EXIT_SUCCESS
#include <signal.h> // sigaction()
#include <stdio.h> // printf(), fprintf(), stdout, stderr, perror(), _IOLBF
#include <string.h> // memset()
#include <sys/time.h> // ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF, struct itimerval, setitimer()
#include <stdbool.h> // true, false
#include <limits.h> // INT_MAX
#define TIMEOUT 50 // ms
#define TIMER_TYPE ITIMER_VIRTUAL // Type of timer.
/* The three types of timers causes different signals.
type: type of timer, one of ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF.
return value: the signal generated by the timer.
*/
int timer_signal(int timer_type) {
int sig;
switch (timer_type) {
case ITIMER_REAL:
sig = SIGALRM;
break;
case ITIMER_VIRTUAL:
sig = SIGVTALRM;
break;
case ITIMER_PROF:
sig = SIGPROF;
break;
default:
fprintf(stderr, "ERROR: unknown timer type %d!\n", timer_type);
exit(EXIT_FAILURE);
}
return sig;
}
/* Set a timer and a handler for the timer.
Arguments
type: type of timer, one of ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF.
handler: timer signal handler.
ms: time in ms for the timer.
*/
void set_timer(int type, void (*handler) (int), int ms) {
struct itimerval timer;
struct sigaction sa;
/* Install signal handler for the timer. */
memset (&sa, 0, sizeof (sa));
sa.sa_handler = handler;
sigaction (timer_signal(type), &sa, NULL);
/* Configure the timer to expire after ms msec... */
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = ms*1000;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 0;
if (setitimer (type, &timer, NULL) < 0) {
perror("Setting timer");
exit(EXIT_FAILURE);
};
}
/* Timer signal handler. */
void timer_handler (int signum) {
static int count = 0;
fprintf (stderr, "======> timer (%03d)\n", count++);
set_timer(TIMER_TYPE, timer_handler, TIMEOUT);
}
/* Calculate the nth Fibonacci number using recursion. */
int fib(int n) {
switch (n) {
case 0:
return 0;
case 1:
return 1;
default:
return fib(n-1) + fib(n-2);
}
}
/* Print the Fibonacci number sequence over and over again.
This is deliberately an unnecessary slow and CPU intensive
implementation where each number in the sequence is calculated recursively
from scratch.
*/
void fibonacci_slow() {
int n = 0;
while (true) {
printf(" fib(%d) = %d\n", n, fib(n));
n = (n + 1) % INT_MAX;
}
}
/* Print the Fibonacci number sequence over and over again.
This implementation is much faster than fibonacci_slow().
*/
void fibonacci_fast() {
int a = 0;
int b = 1;
int n = 0;
int next = a + b;
while(true) {
printf(" fib(%d) = %d\n", n, a);
next = a + b;
a = b;
b = next;
n++;
if (next < 0) {
a = 0;
b = 1;
n = 0;
}
}
}
int main () {
/* Flush each printf() as it happens. */
setvbuf(stdout, 0, _IOLBF, 0);
setvbuf(stderr, 0, _IOLBF, 0);
set_timer(TIMER_TYPE, timer_handler, TIMEOUT);
// Call fibonacci_fast() or fibonacci_fast()
fibonacci_fast();
// fibonacci_slow();
}
从 main()
我呼叫 fibonacci_fast()
或 fibonacci_slow()
。
正如预期的那样,当使用 ITMER_REAL
从 main()
调用 fibonacci_fast()
或 fibonacci_slow()
时,计时器滴答之间的挂钟时间没有差异。
当使用 ITIMER_VIRTUAL
和 main()
调用 fibonacci_fast()
设置计时器时,每个计时器滴答之间的挂钟时间非常长,但如果 main()
调用 fibonacci_slow()
每个计时器滴答之间的挂钟时间要小得多。
我想了解为什么 fibonacci_fast()
使虚拟运行时比 fibonacci_slow()
慢得多。与 fibonacci_slow()
相比,CPU 调度程序在使用 fibonacci_fast()
时给进程的 CPU 时间少得多吗?如果是,为什么?如果不是,还有什么可以解释差异?
ITIMER_VIRTUAL
只有 运行s 当进程处于用户模式 运行ning 时。在您的两个斐波那契例程中,都有一个 printf
调用涉及同步写入数据的系统调用,这不计入虚拟时间。 fibonacci_fast
实际上大部分时间都花在 printf
和这个系统调用上,所以计时器似乎 运行 慢得多。