为什么我的程序大部分时间都在睡眠中,但要花这么多 CPU 时间?
Why my program takes so much CPU time though most of the time in sleep?
我的程序需要一些计时器,所以我决定用 pthreads 来编写它。
我的计时器需要每 update_interval
个滴答声通过更新回调更新一些信息。
我是这样做的:
timer.h:
#include <pthread.h>
enum timer_messages
{
TIMER_START,
TIMER_STOP,
TIMER_PAUSE,
TIMER_EXIT
};
typedef void (*callback)(void *);
struct timer
{
pthread_t thread_id;
struct timeval *interval;
struct timeval *update_interval;
struct timeval *start;
int ls;
int wr;
int enabled;
int exit;
callback update;
callback on_time;
};
struct timer *my_timer_create();
void timer_destroy(struct timer *t);
void timer_set_update_interval(struct timer *t, int seconds, int microseconds);
void timer_set_interval(struct timer *t, int seconds, int microseconds);
void timer_set_update_func(struct timer *t, callback update);
void timer_set_ontime_func(struct timer *t, callback on_time);
void timer_stop(struct timer *t);
void timer_start(struct timer *t);
void timer_exit(struct timer *t);
void timer_pause(struct timer *t);
timer.c:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include "timer.h"
#define TIMEVAL_TO_MICROSECONDS(tv) ((long long)((tv).tv_sec * 1000000 + (tv).tv_usec))
#define GET_TIME_PASSED(start, now) ((TIMEVAL_TO_MICROSECONDS(now) - TIMEVAL_TO_MICROSECONDS(start)))
static int passed(struct timeval *start, struct timeval *interval);
static void fill_timeval(struct timeval *tv, int sec, int microsec);
static void timer_count(struct timer *t);
static void timer_message(struct timer *t);
static void *main_func(void *data);
static void timer_send_msg(struct timer *t, enum timer_messages message);
static struct timeval DEFAULT_TIMEOUT = { 0, 500000 };
static int passed(struct timeval *start, struct timeval *interval)
{
struct timeval cur, sub;
int check;
check = gettimeofday(&cur, NULL);
if(-1 == check)
{
perror("gettimeofday");
return 0;
}
if(GET_TIME_PASSED(*start, cur) < TIMEVAL_TO_MICROSECONDS(*interval))
return 0;
return 1;
}
static void fill_timeval(struct timeval *tv, int sec, int microsec)
{
tv->tv_sec = sec;
tv->tv_usec = microsec;
}
static void timer_count(struct timer *t)
{
int check;
fd_set readfds;
struct timeval timeout;
check = gettimeofday(t->start, NULL);
while(1)
{
if(!t->enabled)
return;
FD_ZERO(&readfds);
FD_SET(t->ls, &readfds);
if(t->update_interval)
memcpy(&timeout, t->update_interval, sizeof(*(t->update_interval)));
else
memcpy(&timeout, &DEFAULT_TIMEOUT, sizeof(DEFAULT_TIMEOUT));
check = select(t->ls + 1, &readfds, NULL, NULL, &timeout);
if(-1 == check)
{
perror("select");
return;
}
if(FD_ISSET(t->ls, &readfds))
timer_message(t);
else
if(t->update)
t->update(t);
if(passed(t->start, t->interval))
{
t->on_time(t);
break;
}
}
}
static void timer_message(struct timer *t)
{
int read_bytes;
char message;
read_bytes = read(t->ls, &message, sizeof(message));
if(-1 == read_bytes)
{
perror("timer_message read");
return;
}
switch(message)
{
case TIMER_START: t->enabled = 1; break;
case TIMER_STOP: t->enabled = 0; t->interval = NULL; t->start = NULL; break;
case TIMER_EXIT: t->enabled = 0; t->exit = 1; break;
case TIMER_PAUSE: break;
default: break;
}
}
static void *main_func(void *data)
{
struct timer *t = data;
fd_set readfds;
int check;
while(!t->exit)
{
if(t->enabled)
{
timer_count(t);
}
else
{
FD_ZERO(&readfds);
FD_SET(t->ls, &readfds);
check = select(t->ls + 1, &readfds, NULL, NULL, NULL);
if(-1 == check)
{
perror("select");
return NULL;
}
if(FD_ISSET(t->ls, &readfds))
timer_message(t);
}
}
return NULL;
}
static void timer_send_msg(struct timer *t, enum timer_messages message)
{
int check;
char msg;
msg = message;
check = write(t->wr, &msg, sizeof(msg));
if(-1 == check)
{
perror("timer_send_msg write");
}
}
struct timer *my_timer_create()
{
int check;
struct timer *t;
int fd[2];
t = malloc(sizeof(*t));
t->interval = malloc(sizeof(*(t->interval)));
t->update_interval = malloc(sizeof(*(t->update_interval)));
t->start = malloc(sizeof(*(t->start)));
check = pipe(fd);
if(-1 == check)
{
perror("pipe");
return NULL;
}
t->ls = fd[0];
t->wr = fd[1];
t->enabled = 0;
t->exit = 0;
t->update = NULL;
t->on_time = NULL;
check = pthread_create(&(t->thread_id), NULL, main_func, t);
if(-1 == check)
{
perror("pthread_create");
return NULL;
}
return t;
}
void timer_destroy(struct timer *t)
{
free(t->interval);
free(t->update_interval);
free(t->start);
close(t->ls);
close(t->wr);
free(t);
}
void timer_set_update_interval(struct timer *t, int seconds, int microseconds)
{
fill_timeval(t->update_interval, seconds, microseconds);
}
void timer_set_interval(struct timer *t, int seconds, int microseconds)
{
fill_timeval(t->interval, seconds, microseconds);
}
void timer_set_update_func(struct timer *t, callback update)
{
t->update = update;
}
void timer_set_ontime_func(struct timer *t, callback on_time)
{
t->on_time = on_time;
}
void timer_stop(struct timer *t)
{
timer_send_msg(t, TIMER_STOP);
}
void timer_start(struct timer *t)
{
timer_send_msg(t, TIMER_START);
}
void timer_exit(struct timer *t)
{
timer_send_msg(t, TIMER_EXIT);
}
void timer_pause(struct timer *t)
{
timer_send_msg(t, TIMER_PAUSE);
}
然后在主文件中这样调用它:
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/time.h>
#include "../timer.h"
#define BUF_SIZE 4096
#define TIMEVAL_TO_MICROSECONDS(tv) ((long long)((tv).tv_sec * 1000000 + (tv).tv_usec))
#define GET_TIME_PASSED(start, now) ((TIMEVAL_TO_MICROSECONDS(now) - TIMEVAL_TO_MICROSECONDS(start)))
void progress_bar(int percent, int bar_len)
{
char buf[BUF_SIZE];
int inside = bar_len - 2;
int filled = inside * percent / 100;
int not_filled = inside - filled;
assert(percent <= 100);
assert(bar_len < BUF_SIZE);
buf[0] = '[';
memset(buf + 1, '#', filled);
memset(buf + 1 + filled, '-', not_filled);
buf[bar_len - 1] = ']';
buf[bar_len] = 0;
printf("\r%s %d%%", buf, percent);
fflush(stdout);
}
void timer_ontime(void *data)
{
struct timer *t = data;
puts("");
puts("That's all folks!");
timer_exit(t);
}
void timer_update(void *data)
{
struct timer *t = data;
struct timeval now;
long long passed;
int percent;
gettimeofday(&now, NULL);
passed = GET_TIME_PASSED(*(t->start), now);
percent = passed * 100 / (t->interval->tv_sec * 1000000);
progress_bar(percent, 50);
}
int main(int argc, char **argv)
{
struct timer *t;
int seconds;
int check;
if(argc != 2)
{
fprintf(stderr, "Usage: %s <seconds>\n", argv[0]);
return 1;
}
check = sscanf(argv[1], "%d", &seconds);
if(check != 1)
{
fprintf(stderr, "Couldn't parse number of seconds\n");
return 1;
}
t = my_timer_create();
if(t == NULL)
{
fprintf(stderr, "Couldn't create timer\n");
return 1;
}
timer_set_interval(t, seconds, 0);
timer_set_ontime_func(t, timer_ontime);
timer_set_update_func(t, timer_update);
timer_start(t);
printf("Started timer(%d seconds)\n", seconds);
pthread_join(t->thread_id, NULL);
}
然后我 运行 它与:
[udalny@bulba test]$ time ./timer_check 3
Started timer(3 seconds)
[###############################################-] 99%
That's all folks!
./timer_check 3 0.48s user 1.22s system 56% cpu 3.002 total
如您所见,它需要 56% CPU 的时间。为什么这么多?
它每秒只更新两次(DEFAULT_CALLBACK 是 500000 微秒)。和所有
其他时间它在睡觉。
我怎样才能改变它以减少花费?
此外,如果您对代码有任何提示,我将不胜感激。
您的程序大部分时间都花在 timer_count
上,忙着循环 - 如果您在 select
:
之前添加一个简单的 printf
printf("?\n");
check = select(t->ls + 1, &readfds, NULL, NULL, &timeout);
和 运行 ./timer_check 3 | wc -l
你应该得到 百万 行 - 这意味着此循环上的 CPU 硬循环。这是因为您初始化 timeout
:
的方式
if(t->update_interval)
memcpy(&timeout, t->update_interval, sizeof(*(t->update_interval)));
这实际上将您的 timeout
设置为零 - 因为您从未在 main
中初始化您的 t->update_interval
。这有效地将您的循环变成了一个繁忙的循环。
将以下行添加到您的主函数以解决此问题:
timer_set_update_interval(t, seconds, 0);
之后你会得到你想要的行为:
Started timer(3 seconds)
[################################################] 100%
That's all folks!
0.00user 0.00system 0:03.00elapsed 0%CPU (0avgtext+0avgdata 1932maxresident)k
0inputs+0outputs (0major+77minor)pagefaults 0swaps
我的程序需要一些计时器,所以我决定用 pthreads 来编写它。
我的计时器需要每 update_interval
个滴答声通过更新回调更新一些信息。
我是这样做的:
timer.h:
#include <pthread.h>
enum timer_messages
{
TIMER_START,
TIMER_STOP,
TIMER_PAUSE,
TIMER_EXIT
};
typedef void (*callback)(void *);
struct timer
{
pthread_t thread_id;
struct timeval *interval;
struct timeval *update_interval;
struct timeval *start;
int ls;
int wr;
int enabled;
int exit;
callback update;
callback on_time;
};
struct timer *my_timer_create();
void timer_destroy(struct timer *t);
void timer_set_update_interval(struct timer *t, int seconds, int microseconds);
void timer_set_interval(struct timer *t, int seconds, int microseconds);
void timer_set_update_func(struct timer *t, callback update);
void timer_set_ontime_func(struct timer *t, callback on_time);
void timer_stop(struct timer *t);
void timer_start(struct timer *t);
void timer_exit(struct timer *t);
void timer_pause(struct timer *t);
timer.c:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include "timer.h"
#define TIMEVAL_TO_MICROSECONDS(tv) ((long long)((tv).tv_sec * 1000000 + (tv).tv_usec))
#define GET_TIME_PASSED(start, now) ((TIMEVAL_TO_MICROSECONDS(now) - TIMEVAL_TO_MICROSECONDS(start)))
static int passed(struct timeval *start, struct timeval *interval);
static void fill_timeval(struct timeval *tv, int sec, int microsec);
static void timer_count(struct timer *t);
static void timer_message(struct timer *t);
static void *main_func(void *data);
static void timer_send_msg(struct timer *t, enum timer_messages message);
static struct timeval DEFAULT_TIMEOUT = { 0, 500000 };
static int passed(struct timeval *start, struct timeval *interval)
{
struct timeval cur, sub;
int check;
check = gettimeofday(&cur, NULL);
if(-1 == check)
{
perror("gettimeofday");
return 0;
}
if(GET_TIME_PASSED(*start, cur) < TIMEVAL_TO_MICROSECONDS(*interval))
return 0;
return 1;
}
static void fill_timeval(struct timeval *tv, int sec, int microsec)
{
tv->tv_sec = sec;
tv->tv_usec = microsec;
}
static void timer_count(struct timer *t)
{
int check;
fd_set readfds;
struct timeval timeout;
check = gettimeofday(t->start, NULL);
while(1)
{
if(!t->enabled)
return;
FD_ZERO(&readfds);
FD_SET(t->ls, &readfds);
if(t->update_interval)
memcpy(&timeout, t->update_interval, sizeof(*(t->update_interval)));
else
memcpy(&timeout, &DEFAULT_TIMEOUT, sizeof(DEFAULT_TIMEOUT));
check = select(t->ls + 1, &readfds, NULL, NULL, &timeout);
if(-1 == check)
{
perror("select");
return;
}
if(FD_ISSET(t->ls, &readfds))
timer_message(t);
else
if(t->update)
t->update(t);
if(passed(t->start, t->interval))
{
t->on_time(t);
break;
}
}
}
static void timer_message(struct timer *t)
{
int read_bytes;
char message;
read_bytes = read(t->ls, &message, sizeof(message));
if(-1 == read_bytes)
{
perror("timer_message read");
return;
}
switch(message)
{
case TIMER_START: t->enabled = 1; break;
case TIMER_STOP: t->enabled = 0; t->interval = NULL; t->start = NULL; break;
case TIMER_EXIT: t->enabled = 0; t->exit = 1; break;
case TIMER_PAUSE: break;
default: break;
}
}
static void *main_func(void *data)
{
struct timer *t = data;
fd_set readfds;
int check;
while(!t->exit)
{
if(t->enabled)
{
timer_count(t);
}
else
{
FD_ZERO(&readfds);
FD_SET(t->ls, &readfds);
check = select(t->ls + 1, &readfds, NULL, NULL, NULL);
if(-1 == check)
{
perror("select");
return NULL;
}
if(FD_ISSET(t->ls, &readfds))
timer_message(t);
}
}
return NULL;
}
static void timer_send_msg(struct timer *t, enum timer_messages message)
{
int check;
char msg;
msg = message;
check = write(t->wr, &msg, sizeof(msg));
if(-1 == check)
{
perror("timer_send_msg write");
}
}
struct timer *my_timer_create()
{
int check;
struct timer *t;
int fd[2];
t = malloc(sizeof(*t));
t->interval = malloc(sizeof(*(t->interval)));
t->update_interval = malloc(sizeof(*(t->update_interval)));
t->start = malloc(sizeof(*(t->start)));
check = pipe(fd);
if(-1 == check)
{
perror("pipe");
return NULL;
}
t->ls = fd[0];
t->wr = fd[1];
t->enabled = 0;
t->exit = 0;
t->update = NULL;
t->on_time = NULL;
check = pthread_create(&(t->thread_id), NULL, main_func, t);
if(-1 == check)
{
perror("pthread_create");
return NULL;
}
return t;
}
void timer_destroy(struct timer *t)
{
free(t->interval);
free(t->update_interval);
free(t->start);
close(t->ls);
close(t->wr);
free(t);
}
void timer_set_update_interval(struct timer *t, int seconds, int microseconds)
{
fill_timeval(t->update_interval, seconds, microseconds);
}
void timer_set_interval(struct timer *t, int seconds, int microseconds)
{
fill_timeval(t->interval, seconds, microseconds);
}
void timer_set_update_func(struct timer *t, callback update)
{
t->update = update;
}
void timer_set_ontime_func(struct timer *t, callback on_time)
{
t->on_time = on_time;
}
void timer_stop(struct timer *t)
{
timer_send_msg(t, TIMER_STOP);
}
void timer_start(struct timer *t)
{
timer_send_msg(t, TIMER_START);
}
void timer_exit(struct timer *t)
{
timer_send_msg(t, TIMER_EXIT);
}
void timer_pause(struct timer *t)
{
timer_send_msg(t, TIMER_PAUSE);
}
然后在主文件中这样调用它:
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/time.h>
#include "../timer.h"
#define BUF_SIZE 4096
#define TIMEVAL_TO_MICROSECONDS(tv) ((long long)((tv).tv_sec * 1000000 + (tv).tv_usec))
#define GET_TIME_PASSED(start, now) ((TIMEVAL_TO_MICROSECONDS(now) - TIMEVAL_TO_MICROSECONDS(start)))
void progress_bar(int percent, int bar_len)
{
char buf[BUF_SIZE];
int inside = bar_len - 2;
int filled = inside * percent / 100;
int not_filled = inside - filled;
assert(percent <= 100);
assert(bar_len < BUF_SIZE);
buf[0] = '[';
memset(buf + 1, '#', filled);
memset(buf + 1 + filled, '-', not_filled);
buf[bar_len - 1] = ']';
buf[bar_len] = 0;
printf("\r%s %d%%", buf, percent);
fflush(stdout);
}
void timer_ontime(void *data)
{
struct timer *t = data;
puts("");
puts("That's all folks!");
timer_exit(t);
}
void timer_update(void *data)
{
struct timer *t = data;
struct timeval now;
long long passed;
int percent;
gettimeofday(&now, NULL);
passed = GET_TIME_PASSED(*(t->start), now);
percent = passed * 100 / (t->interval->tv_sec * 1000000);
progress_bar(percent, 50);
}
int main(int argc, char **argv)
{
struct timer *t;
int seconds;
int check;
if(argc != 2)
{
fprintf(stderr, "Usage: %s <seconds>\n", argv[0]);
return 1;
}
check = sscanf(argv[1], "%d", &seconds);
if(check != 1)
{
fprintf(stderr, "Couldn't parse number of seconds\n");
return 1;
}
t = my_timer_create();
if(t == NULL)
{
fprintf(stderr, "Couldn't create timer\n");
return 1;
}
timer_set_interval(t, seconds, 0);
timer_set_ontime_func(t, timer_ontime);
timer_set_update_func(t, timer_update);
timer_start(t);
printf("Started timer(%d seconds)\n", seconds);
pthread_join(t->thread_id, NULL);
}
然后我 运行 它与:
[udalny@bulba test]$ time ./timer_check 3
Started timer(3 seconds)
[###############################################-] 99%
That's all folks!
./timer_check 3 0.48s user 1.22s system 56% cpu 3.002 total
如您所见,它需要 56% CPU 的时间。为什么这么多? 它每秒只更新两次(DEFAULT_CALLBACK 是 500000 微秒)。和所有 其他时间它在睡觉。
我怎样才能改变它以减少花费?
此外,如果您对代码有任何提示,我将不胜感激。
您的程序大部分时间都花在 timer_count
上,忙着循环 - 如果您在 select
:
printf
printf("?\n");
check = select(t->ls + 1, &readfds, NULL, NULL, &timeout);
和 运行 ./timer_check 3 | wc -l
你应该得到 百万 行 - 这意味着此循环上的 CPU 硬循环。这是因为您初始化 timeout
:
if(t->update_interval)
memcpy(&timeout, t->update_interval, sizeof(*(t->update_interval)));
这实际上将您的 timeout
设置为零 - 因为您从未在 main
中初始化您的 t->update_interval
。这有效地将您的循环变成了一个繁忙的循环。
将以下行添加到您的主函数以解决此问题:
timer_set_update_interval(t, seconds, 0);
之后你会得到你想要的行为:
Started timer(3 seconds)
[################################################] 100%
That's all folks!
0.00user 0.00system 0:03.00elapsed 0%CPU (0avgtext+0avgdata 1932maxresident)k
0inputs+0outputs (0major+77minor)pagefaults 0swaps