| /* |
| * Copyright (c) International Business Machines Corp., 2001-2004 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| * the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| #include <pthread.h> |
| #include <sys/time.h> |
| #include <sys/times.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <pthread.h> |
| |
| #include <assert.h> |
| |
| #include "config.h" |
| |
| #include "ffsb.h" |
| #include "util.h" |
| #include "parser.h" |
| |
| /* State information for the polling function below */ |
| struct ffsb_time_poll { |
| struct timeval starttime; |
| int wait_time; |
| }; |
| |
| /* This is the polling function used by the threadgroups to check |
| * elapsed time, when it returns 1 they know it is time to stop |
| */ |
| static int ffsb_poll_fn(void *ptr) |
| { |
| struct ffsb_time_poll *data = (struct ffsb_time_poll *)ptr; |
| struct timeval curtime, difftime; |
| gettimeofday(&curtime, NULL); |
| |
| timersub(&curtime, &data->starttime, &difftime); |
| if (difftime.tv_sec >= data->wait_time) |
| return 1; |
| return 0; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int i; |
| ffsb_config_t fc; |
| ffsb_barrier_t thread_barrier, tg_barrier; |
| tg_run_params_t *params; |
| struct ffsb_time_poll pdata; |
| struct timeval starttime, endtime, difftime; |
| pthread_attr_t attr; |
| ffsb_op_results_t total_results; |
| double totaltime = 0.0f, usertime = 0.0f, systime = 0.0f; |
| struct rusage before_self, before_children, after_self, after_children; |
| pthread_t *fs_pts; /* threads to do filesystem creates in parallel */ |
| char *callout = NULL; |
| |
| char ctime_start_buf[32]; |
| char ctime_end_buf[32]; |
| |
| memset(&before_self, 0, sizeof(before_self)); |
| memset(&before_children, 0, sizeof(before_children)); |
| memset(&after_self, 0, sizeof(after_self)); |
| memset(&after_children, 0, sizeof(after_children)); |
| |
| ffsb_unbuffer_stdout(); |
| |
| if (argc < 2) { |
| fprintf(stderr, "usage: %s <config file>\n", argv[0]); |
| exit(1); |
| } |
| |
| /* VERSION comes from config.h (which is autogenerated by autoconf) */ |
| printf("FFSB version %s started\n\n", VERSION); |
| |
| ffsb_parse_newconfig(&fc, argv[1]); |
| pdata.wait_time = fc.time; |
| |
| if (fc.time) |
| printf("benchmark time = %u\n", fc.time); |
| else |
| printf("Only creating the fileset, not running benchmark.\n"); |
| |
| pthread_attr_init(&attr); |
| pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); |
| |
| for (i = 0; i < fc.num_threadgroups; i++) |
| tg_print_config(&fc.groups[i]); |
| |
| fs_pts = ffsb_malloc(sizeof(pthread_t) * fc.num_filesys); |
| |
| gettimeofday(&starttime, NULL); |
| for (i = 0; i < fc.num_filesys; i++) { |
| fs_print_config(&fc.filesystems[i]); |
| pthread_create(fs_pts + i, &attr, construct_ffsb_fs, |
| &fc.filesystems[i]); |
| } |
| |
| fflush(stdout); |
| for (i = 0; i < fc.num_filesys; i++) |
| pthread_join(fs_pts[i], NULL); |
| |
| gettimeofday(&endtime, NULL); |
| timersub(&endtime, &starttime, &difftime); |
| printf("fs setup took %ld secs\n", difftime.tv_sec); |
| free(fs_pts); |
| |
| if (fc.time == 0) { |
| printf("Setup complete, exiting\n"); |
| return 0; |
| } |
| |
| params = ffsb_malloc(sizeof(tg_run_params_t) * fc.num_threadgroups); |
| |
| init_ffsb_op_results(&total_results); |
| ffsb_barrier_init(&thread_barrier, fc.num_totalthreads); |
| ffsb_barrier_init(&tg_barrier, fc.num_threadgroups + 1); |
| |
| ffsb_sync(); |
| |
| /* Execute the callout if any and wait for it to return */ |
| callout = fc_get_callout(&fc); |
| if (callout) { |
| printf("executing callout: \n %s\n", callout); |
| if (ffsb_system(callout) < 0) { |
| perror("system"); |
| exit(1); |
| } |
| } |
| |
| /* Spawn all of the threadgroup master threads */ |
| for (i = 0; i < fc.num_threadgroups; i++) { |
| params[i].tg = &fc.groups[i]; |
| params[i].fc = &fc; |
| params[i].poll_fn = ffsb_poll_fn; |
| params[i].poll_data = &pdata; |
| params[i].wait_time = FFSB_TG_WAIT_TIME; |
| params[i].tg_barrier = &tg_barrier; |
| params[i].thread_barrier = &thread_barrier; |
| |
| pthread_create(¶ms[i].pt, &attr, tg_run, ¶ms[i]); |
| } |
| |
| ffsb_getrusage(&before_self, &before_children); |
| gettimeofday(&pdata.starttime, NULL); |
| |
| ffsb_barrier_wait(&tg_barrier); /* sync with tg's to start */ |
| printf("Starting Actual Benchmark At: %s\n", |
| ctime_r(&pdata.starttime.tv_sec, ctime_start_buf)); |
| fflush(stdout); |
| |
| /* Wait for all of the threadgroup master threads to finish */ |
| for (i = 0; i < fc.num_threadgroups; i++) |
| pthread_join(params[i].pt, NULL); |
| |
| ffsb_sync(); |
| gettimeofday(&endtime, NULL); |
| ffsb_getrusage(&after_self, &after_children); |
| |
| printf("FFSB benchmark finished at: %s\n", |
| ctime_r(&endtime.tv_sec, ctime_end_buf)); |
| printf("Results:\n"); |
| fflush(stdout); |
| |
| timersub(&endtime, &pdata.starttime, &difftime); |
| |
| totaltime = tvtodouble(&difftime); |
| |
| printf("Benchmark took %.2lf sec\n", totaltime); |
| printf("\n"); |
| |
| for (i = 0; i < fc.num_threadgroups; i++) { |
| struct ffsb_op_results tg_results; |
| ffsb_tg_t *tg = fc.groups + i; |
| |
| init_ffsb_op_results(&tg_results); |
| |
| /* Grab the individual tg results */ |
| tg_collect_results(tg, &tg_results); |
| |
| if (fc.num_threadgroups == 1) |
| printf("Total Results\n"); |
| else |
| printf("ThreadGroup %d\n", i); |
| |
| printf("===============\n"); |
| print_results(&tg_results, totaltime); |
| |
| if (tg_needs_stats(tg)) { |
| ffsb_statsd_t fsd; |
| tg_collect_stats(tg, &fsd); |
| ffsb_statsd_print(&fsd); |
| } |
| printf("\n"); |
| |
| /* Add the tg results to the total */ |
| tg_collect_results(&fc.groups[i], &total_results); |
| } |
| |
| if (fc.num_threadgroups > 1) { |
| printf("Total Results\n"); |
| printf("===============\n"); |
| print_results(&total_results, totaltime); |
| } |
| #define USEC_PER_SEC ((double)(1000000.0f)) |
| |
| /* sum up self and children after */ |
| usertime = (after_self.ru_utime.tv_sec + |
| ((after_self.ru_utime.tv_usec) / USEC_PER_SEC)) + |
| ((after_children.ru_utime.tv_sec + |
| ((after_children.ru_utime.tv_usec) / USEC_PER_SEC))); |
| |
| /* subtract away the before */ |
| usertime -= (before_self.ru_utime.tv_sec + |
| ((before_self.ru_utime.tv_usec) / USEC_PER_SEC)) + |
| ((before_children.ru_utime.tv_sec + |
| ((before_children.ru_utime.tv_usec) / USEC_PER_SEC))); |
| |
| /* sum up self and children after */ |
| systime = (after_self.ru_stime.tv_sec + |
| ((after_self.ru_stime.tv_usec) / USEC_PER_SEC)) + |
| ((after_children.ru_stime.tv_sec + |
| ((after_children.ru_stime.tv_usec) / USEC_PER_SEC))); |
| |
| /* subtract away the before */ |
| systime -= (before_self.ru_stime.tv_sec + |
| ((before_self.ru_stime.tv_usec) / USEC_PER_SEC)) + |
| ((before_children.ru_stime.tv_sec + |
| ((before_children.ru_stime.tv_usec) / USEC_PER_SEC))); |
| |
| printf("\n\n"); |
| printf("%.1lf%% User Time\n", 100 * usertime / totaltime); |
| printf("%.1lf%% System Time\n", 100 * systime / totaltime); |
| printf("%.1f%% CPU Utilization\n", 100 * (usertime + systime) / |
| totaltime); |
| free(params); |
| destroy_ffsb_config(&fc); |
| |
| return 0; |
| } |