From 263e529f7d90892a610a5b26a519116fe3a675a6 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 18 Oct 2006 15:37:01 +0200 Subject: [PATCH] [PATCH] Split status/eta code out of fio.c All in the name of making the code base easier to go through. Signed-off-by: Jens Axboe --- Makefile | 2 +- eta.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fio.c | 242 +------------------------------------------------------ fio.h | 25 ++++++ time.c | 12 +++ 5 files changed, 272 insertions(+), 240 deletions(-) create mode 100644 eta.c diff --git a/Makefile b/Makefile index b1a49863..80a03975 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ CFLAGS += '-D_INST_PREFIX="$(FIO_INST_DIR)"' all: depend $(PROGS) $(SCRIPTS) $(MAKE) -C engines -fio: fio.o ioengines.o init.o stat.o log.o time.o md5.o crc32.o filesetup.o +fio: fio.o ioengines.o init.o stat.o log.o time.o md5.o crc32.o filesetup.o eta.o $(CC) $(CFLAGS) -o $@ $(filter %.o,$^) -lpthread -laio -lm -lrt -ldl clean: diff --git a/eta.c b/eta.c new file mode 100644 index 00000000..9ad0b49b --- /dev/null +++ b/eta.c @@ -0,0 +1,231 @@ +/* + * Status and ETA code + */ +#include +#include +#include + +#include "fio.h" +#include "os.h" + +static char run_str[MAX_JOBS + 1]; + +/* + * Sets the status of the 'td' in the printed status map. + */ +static void check_str_update(struct thread_data *td) +{ + char c = run_str[td->thread_number - 1]; + + switch (td->runstate) { + case TD_REAPED: + c = '_'; + break; + case TD_EXITED: + c = 'E'; + break; + case TD_RUNNING: + if (td_rw(td)) { + if (td->sequential) + c = 'M'; + else + c = 'm'; + } else if (td_read(td)) { + if (td->sequential) + c = 'R'; + else + c = 'r'; + } else { + if (td->sequential) + c = 'W'; + else + c = 'w'; + } + break; + case TD_VERIFYING: + c = 'V'; + break; + case TD_FSYNCING: + c = 'F'; + break; + case TD_CREATED: + c = 'C'; + break; + case TD_INITIALIZED: + c = 'I'; + break; + case TD_NOT_CREATED: + c = 'P'; + break; + default: + log_err("state %d\n", td->runstate); + } + + run_str[td->thread_number - 1] = c; +} + +/* + * Convert seconds to a printable string. + */ +static void eta_to_str(char *str, int eta_sec) +{ + unsigned int d, h, m, s; + static int always_d, always_h; + + d = h = m = s = 0; + + s = eta_sec % 60; + eta_sec /= 60; + m = eta_sec % 60; + eta_sec /= 60; + h = eta_sec % 24; + eta_sec /= 24; + d = eta_sec; + + if (d || always_d) { + always_d = 1; + str += sprintf(str, "%02dd:", d); + } + if (h || always_h) { + always_h = 1; + str += sprintf(str, "%02dh:", h); + } + + str += sprintf(str, "%02dm:", m); + str += sprintf(str, "%02ds", s); +} + +/* + * Best effort calculation of the estimated pending runtime of a job. + */ +static int thread_eta(struct thread_data *td, unsigned long elapsed) +{ + unsigned long long bytes_total, bytes_done; + unsigned int eta_sec = 0; + + bytes_total = td->total_io_size; + + if (td->zone_size && td->zone_skip) + bytes_total /= (td->zone_skip / td->zone_size); + + if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING) { + double perc; + + bytes_done = td->io_bytes[DDIR_READ] + td->io_bytes[DDIR_WRITE]; + perc = (double) bytes_done / (double) bytes_total; + if (perc > 1.0) + perc = 1.0; + + eta_sec = (elapsed * (1.0 / perc)) - elapsed; + + if (td->timeout && eta_sec > (td->timeout - elapsed)) + eta_sec = td->timeout - elapsed; + } else if (td->runstate == TD_NOT_CREATED || td->runstate == TD_CREATED + || td->runstate == TD_INITIALIZED) { + int t_eta = 0, r_eta = 0; + + /* + * We can only guess - assume it'll run the full timeout + * if given, otherwise assume it'll run at the specified rate. + */ + if (td->timeout) + t_eta = td->timeout + td->start_delay - elapsed; + if (td->rate) { + r_eta = (bytes_total / 1024) / td->rate; + r_eta += td->start_delay - elapsed; + } + + if (r_eta && t_eta) + eta_sec = min(r_eta, t_eta); + else if (r_eta) + eta_sec = r_eta; + else if (t_eta) + eta_sec = t_eta; + else + eta_sec = 0; + } else { + /* + * thread is already done or waiting for fsync + */ + eta_sec = 0; + } + + return eta_sec; +} + +/* + * Print status of the jobs we know about. This includes rate estimates, + * ETA, thread state, etc. + */ +void print_thread_status(void) +{ + unsigned long elapsed = mtime_since_genesis() / 1000; + int i, nr_running, nr_pending, t_rate, m_rate, *eta_secs, eta_sec; + char eta_str[32]; + double perc = 0.0; + + if (temp_stall_ts || terse_output) + return; + + eta_secs = malloc(thread_number * sizeof(int)); + memset(eta_secs, 0, thread_number * sizeof(int)); + + nr_pending = nr_running = t_rate = m_rate = 0; + for (i = 0; i < thread_number; i++) { + struct thread_data *td = &threads[i]; + + if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING|| + td->runstate == TD_FSYNCING) { + nr_running++; + t_rate += td->rate; + m_rate += td->ratemin; + } else if (td->runstate < TD_RUNNING) + nr_pending++; + + if (elapsed >= 3) + eta_secs[i] = thread_eta(td, elapsed); + else + eta_secs[i] = INT_MAX; + + check_str_update(td); + } + + if (exitall_on_terminate) + eta_sec = INT_MAX; + else + eta_sec = 0; + + for (i = 0; i < thread_number; i++) { + if (exitall_on_terminate) { + if (eta_secs[i] < eta_sec) + eta_sec = eta_secs[i]; + } else { + if (eta_secs[i] > eta_sec) + eta_sec = eta_secs[i]; + } + } + + if (eta_sec != INT_MAX && elapsed) { + perc = (double) elapsed / (double) (elapsed + eta_sec); + eta_to_str(eta_str, eta_sec); + } + + if (!nr_running && !nr_pending) + return; + + printf("Threads running: %d", nr_running); + if (m_rate || t_rate) + printf(", commitrate %d/%dKiB/sec", t_rate, m_rate); + if (eta_sec != INT_MAX && nr_running) { + perc *= 100.0; + printf(": [%s] [%3.2f%% done] [eta %s]", run_str, perc,eta_str); + } + printf("\r"); + fflush(stdout); + free(eta_secs); +} + +void print_status_init(int thread_number) +{ + run_str[thread_number] = 'P'; +} diff --git a/fio.c b/fio.c index 1fb35fb1..a146f035 100644 --- a/fio.c +++ b/fio.c @@ -41,33 +41,12 @@ int groupid = 0; int thread_number = 0; -static char run_str[MAX_JOBS + 1]; int shm_id = 0; -static struct timeval genesis; int temp_stall_ts; char *fio_inst_prefix = _INST_PREFIX; -static void print_thread_status(void); - extern unsigned long long mlock_size; -/* - * Thread life cycle. Once a thread has a runstate beyond TD_INITIALIZED, it - * will never back again. It may cycle between running/verififying/fsyncing. - * Once the thread reaches TD_EXITED, it is just waiting for the core to - * reap it. - */ -enum { - TD_NOT_CREATED = 0, - TD_CREATED, - TD_INITIALIZED, - TD_RUNNING, - TD_VERIFYING, - TD_FSYNCING, - TD_EXITED, - TD_REAPED, -}; - #define should_fsync(td) ((td_write(td) || td_rw(td)) && (!(td)->odirect || (td)->override_sync)) static volatile int startup_sem; @@ -1256,221 +1235,6 @@ static void *fork_main(int shmid, int offset) return NULL; } -/* - * Sets the status of the 'td' in the printed status map. - */ -static void check_str_update(struct thread_data *td) -{ - char c = run_str[td->thread_number - 1]; - - switch (td->runstate) { - case TD_REAPED: - c = '_'; - break; - case TD_EXITED: - c = 'E'; - break; - case TD_RUNNING: - if (td_rw(td)) { - if (td->sequential) - c = 'M'; - else - c = 'm'; - } else if (td_read(td)) { - if (td->sequential) - c = 'R'; - else - c = 'r'; - } else { - if (td->sequential) - c = 'W'; - else - c = 'w'; - } - break; - case TD_VERIFYING: - c = 'V'; - break; - case TD_FSYNCING: - c = 'F'; - break; - case TD_CREATED: - c = 'C'; - break; - case TD_INITIALIZED: - c = 'I'; - break; - case TD_NOT_CREATED: - c = 'P'; - break; - default: - log_err("state %d\n", td->runstate); - } - - run_str[td->thread_number - 1] = c; -} - -/* - * Convert seconds to a printable string. - */ -static void eta_to_str(char *str, int eta_sec) -{ - unsigned int d, h, m, s; - static int always_d, always_h; - - d = h = m = s = 0; - - s = eta_sec % 60; - eta_sec /= 60; - m = eta_sec % 60; - eta_sec /= 60; - h = eta_sec % 24; - eta_sec /= 24; - d = eta_sec; - - if (d || always_d) { - always_d = 1; - str += sprintf(str, "%02dd:", d); - } - if (h || always_h) { - always_h = 1; - str += sprintf(str, "%02dh:", h); - } - - str += sprintf(str, "%02dm:", m); - str += sprintf(str, "%02ds", s); -} - -/* - * Best effort calculation of the estimated pending runtime of a job. - */ -static int thread_eta(struct thread_data *td, unsigned long elapsed) -{ - unsigned long long bytes_total, bytes_done; - unsigned int eta_sec = 0; - - bytes_total = td->total_io_size; - - if (td->zone_size && td->zone_skip) - bytes_total /= (td->zone_skip / td->zone_size); - - if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING) { - double perc; - - bytes_done = td->io_bytes[DDIR_READ] + td->io_bytes[DDIR_WRITE]; - perc = (double) bytes_done / (double) bytes_total; - if (perc > 1.0) - perc = 1.0; - - eta_sec = (elapsed * (1.0 / perc)) - elapsed; - - if (td->timeout && eta_sec > (td->timeout - elapsed)) - eta_sec = td->timeout - elapsed; - } else if (td->runstate == TD_NOT_CREATED || td->runstate == TD_CREATED - || td->runstate == TD_INITIALIZED) { - int t_eta = 0, r_eta = 0; - - /* - * We can only guess - assume it'll run the full timeout - * if given, otherwise assume it'll run at the specified rate. - */ - if (td->timeout) - t_eta = td->timeout + td->start_delay - elapsed; - if (td->rate) { - r_eta = (bytes_total / 1024) / td->rate; - r_eta += td->start_delay - elapsed; - } - - if (r_eta && t_eta) - eta_sec = min(r_eta, t_eta); - else if (r_eta) - eta_sec = r_eta; - else if (t_eta) - eta_sec = t_eta; - else - eta_sec = 0; - } else { - /* - * thread is already done or waiting for fsync - */ - eta_sec = 0; - } - - return eta_sec; -} - -/* - * Print status of the jobs we know about. This includes rate estimates, - * ETA, thread state, etc. - */ -static void print_thread_status(void) -{ - unsigned long elapsed = time_since_now(&genesis); - int i, nr_running, nr_pending, t_rate, m_rate, *eta_secs, eta_sec; - char eta_str[32]; - double perc = 0.0; - - if (temp_stall_ts || terse_output) - return; - - eta_secs = malloc(thread_number * sizeof(int)); - memset(eta_secs, 0, thread_number * sizeof(int)); - - nr_pending = nr_running = t_rate = m_rate = 0; - for (i = 0; i < thread_number; i++) { - struct thread_data *td = &threads[i]; - - if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING|| - td->runstate == TD_FSYNCING) { - nr_running++; - t_rate += td->rate; - m_rate += td->ratemin; - } else if (td->runstate < TD_RUNNING) - nr_pending++; - - if (elapsed >= 3) - eta_secs[i] = thread_eta(td, elapsed); - else - eta_secs[i] = INT_MAX; - - check_str_update(td); - } - - if (exitall_on_terminate) - eta_sec = INT_MAX; - else - eta_sec = 0; - - for (i = 0; i < thread_number; i++) { - if (exitall_on_terminate) { - if (eta_secs[i] < eta_sec) - eta_sec = eta_secs[i]; - } else { - if (eta_secs[i] > eta_sec) - eta_sec = eta_secs[i]; - } - } - - if (eta_sec != INT_MAX && elapsed) { - perc = (double) elapsed / (double) (elapsed + eta_sec); - eta_to_str(eta_str, eta_sec); - } - - if (!nr_running && !nr_pending) - return; - - printf("Threads running: %d", nr_running); - if (m_rate || t_rate) - printf(", commitrate %d/%dKiB/sec", t_rate, m_rate); - if (eta_sec != INT_MAX && nr_running) { - perc *= 100.0; - printf(": [%s] [%3.2f%% done] [eta %s]", run_str, perc,eta_str); - } - printf("\r"); - fflush(stdout); - free(eta_secs); -} - /* * Run over the job map and reap the threads that have exited, if any. */ @@ -1579,7 +1343,7 @@ static void run_threads(void) for (i = 0; i < thread_number; i++) { td = &threads[i]; - run_str[td->thread_number - 1] = 'P'; + print_status_init(td->thread_number - 1); init_disk_util(td); @@ -1597,7 +1361,7 @@ static void run_threads(void) } } - gettimeofday(&genesis, NULL); + time_init(); while (todo) { struct thread_data *map[MAX_JOBS]; @@ -1623,7 +1387,7 @@ static void run_threads(void) } if (td->start_delay) { - spent = mtime_since_now(&genesis); + spent = mtime_since_genesis(); if (td->start_delay * 1000 > spent) continue; diff --git a/fio.h b/fio.h index c778206b..9471666b 100644 --- a/fio.h +++ b/fio.h @@ -427,10 +427,12 @@ extern int setup_rate(struct thread_data *); /* * Time functions */ +extern void time_init(void); extern unsigned long utime_since(struct timeval *, struct timeval *); extern unsigned long mtime_since(struct timeval *, struct timeval *); extern unsigned long mtime_since_now(struct timeval *); extern unsigned long time_since_now(struct timeval *); +extern unsigned long mtime_since_genesis(void); extern void __usec_sleep(unsigned int); extern void usec_sleep(struct thread_data *, unsigned long); extern void rate_throttle(struct thread_data *, unsigned long, unsigned int); @@ -447,6 +449,29 @@ extern int init_random_state(struct thread_data *); extern void close_files(struct thread_data *); extern int setup_files(struct thread_data *); +/* + * ETA/status stuff + */ +extern void print_thread_status(void); +extern void print_status_init(int); + +/* + * Thread life cycle. Once a thread has a runstate beyond TD_INITIALIZED, it + * will never back again. It may cycle between running/verififying/fsyncing. + * Once the thread reaches TD_EXITED, it is just waiting for the core to + * reap it. + */ +enum { + TD_NOT_CREATED = 0, + TD_CREATED, + TD_INITIALIZED, + TD_RUNNING, + TD_VERIFYING, + TD_FSYNCING, + TD_EXITED, + TD_REAPED, +}; + /* * This is a pretty crappy semaphore implementation, but with the use that fio * has (just signalling start/go conditions), it doesn't have to be better. diff --git a/time.c b/time.c index c7f898be..ad5ee3fe 100644 --- a/time.c +++ b/time.c @@ -3,6 +3,8 @@ #include "fio.h" +static struct timeval genesis; + unsigned long utime_since(struct timeval *s, struct timeval *e) { double sec, usec; @@ -120,3 +122,13 @@ void rate_throttle(struct thread_data *td, unsigned long time_spent, td->rate_pending_usleep -= overtime; } } + +unsigned long mtime_since_genesis(void) +{ + return mtime_since_now(&genesis); +} + +void time_init(void) +{ + gettimeofday(&genesis, NULL); +} -- 2.25.1