| /****************************************************************************** |
| * |
| * Copyright © International Business Machines Corp., 2006, 2008 |
| * |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| * NAME |
| * async_handler.c |
| * |
| * DESCRIPTION |
| * Measure the latency involved in asynchronous event handlers. |
| * Specifically it measures the latency of the pthread_cond_signal |
| * call until the signalled thread is scheduled. |
| * |
| * USAGE: |
| * Use run_auto.sh script in current directory to build and run test. |
| * |
| * AUTHOR |
| * Darren Hart <dvhltc@us.ibm.com> |
| * |
| * HISTORY |
| * 2006-Oct-20: Initial version by Darren Hart <dvhltc@us.ibm.com> |
| * |
| * This line has to be added to avoid a stupid CVS problem |
| *****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <math.h> |
| #include <librttest.h> |
| #include <libstats.h> |
| #include <getopt.h> |
| |
| #define SIGNAL_PRIO 89 |
| #define HANDLER_PRIO 89 |
| #define DEFAULT_ITERATIONS 1000000 /* about 1 minute @ 2GHz */ |
| #define HIST_BUCKETS 100 |
| #define PASS_US 100 |
| |
| static nsec_t start; |
| static nsec_t end; |
| static int iterations = 0; |
| |
| #define CHILD_START 0 |
| #define CHILD_WAIT 1 |
| #define CHILD_HANDLED 2 |
| #define CHILD_QUIT 3 |
| atomic_t step; |
| |
| pthread_cond_t cond = PTHREAD_COND_INITIALIZER; |
| pthread_mutex_t mutex; |
| |
| static int ret = 0; |
| |
| void usage(void) |
| { |
| rt_help(); |
| printf("async_handler specific options:\n"); |
| printf(" -iITERATIONS number of iterations to calculate the average over\n"); |
| } |
| |
| int parse_args(int c, char *v) |
| { |
| |
| int handled = 1; |
| switch (c) { |
| case 'h': |
| usage(); |
| exit(0); |
| case 'i': |
| iterations = atoi(v); |
| break; |
| default: |
| handled = 0; |
| break; |
| } |
| return handled; |
| } |
| |
| void *handler_thread(void *arg) |
| { |
| |
| while (atomic_get(&step) != CHILD_QUIT) { |
| pthread_mutex_lock(&mutex); |
| atomic_set(CHILD_WAIT, &step); |
| if (pthread_cond_wait(&cond, &mutex) != 0) { |
| perror("pthead_cond_wait"); |
| break; |
| } |
| end = rt_gettime(); |
| atomic_set(CHILD_HANDLED, &step); |
| pthread_mutex_unlock(&mutex); |
| while (atomic_get(&step) == CHILD_HANDLED) |
| usleep(10); |
| } |
| printf("handler thread exiting\n"); |
| return 0; |
| } |
| |
| void *signal_thread(void *arg) |
| { |
| |
| int i; |
| long delta, max, min; |
| stats_container_t dat; |
| stats_container_t hist; |
| stats_record_t rec; |
| |
| stats_container_init(&dat, iterations); |
| stats_container_init(&hist, HIST_BUCKETS); |
| |
| min = max = 0; |
| for (i = 0; i < iterations; i++) { |
| /* wait for child to wait on cond, then signal the event */ |
| while (atomic_get(&step) != CHILD_WAIT) |
| usleep(10); |
| pthread_mutex_lock(&mutex); |
| start = rt_gettime(); |
| if (pthread_cond_signal(&cond) != 0) { |
| perror("pthread_cond_signal"); |
| atomic_set(CHILD_QUIT, &step); |
| break; |
| } |
| pthread_mutex_unlock(&mutex); |
| |
| /* wait for the event handler to schedule */ |
| while (atomic_get(&step) != CHILD_HANDLED) |
| usleep(10); |
| delta = (long)((end - start)/NS_PER_US); |
| if (delta > pass_criteria) |
| ret = 1; |
| rec.x = i; |
| rec.y = delta; |
| stats_container_append(&dat, rec); |
| if (i == 0) |
| min = max = delta; |
| else { |
| min = MIN(min, delta); |
| max = MAX(max, delta); |
| } |
| atomic_set((i == iterations-1) ? CHILD_QUIT : CHILD_START, &step); |
| } |
| printf("recording statistics...\n"); |
| printf("Min: %ld us\n", min); |
| printf("Max: %ld us\n", max); |
| printf("Avg: %.4f us\n", stats_avg(&dat)); |
| printf("StdDev: %.4f us\n", stats_stddev(&dat)); |
| stats_hist(&hist, &dat); |
| stats_container_save("samples", "Asynchronous Event Handling Latency Scatter Plot",\ |
| "Iteration", "Latency (us)", &dat, "points"); |
| stats_container_save("hist", "Asynchronous Event Handling Latency Histogram",\ |
| "Latency (us)", "Samples", &hist, "steps"); |
| printf("signal thread exiting\n"); |
| |
| return NULL; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int signal_id, handler_id; |
| setup(); |
| |
| printf("\n-----------------------------------\n"); |
| printf("Asynchronous Event Handling Latency\n"); |
| printf("-----------------------------------\n\n"); |
| |
| pass_criteria = PASS_US; |
| rt_init("i:h", parse_args, argc, argv); |
| |
| init_pi_mutex(&mutex); |
| |
| atomic_set(CHILD_START, &step); |
| |
| if (iterations == 0) |
| iterations = DEFAULT_ITERATIONS; |
| printf("Running %d iterations\n", iterations); |
| |
| handler_id = create_fifo_thread(handler_thread, (void*)0, HANDLER_PRIO); |
| signal_id = create_fifo_thread(signal_thread, (void*)0, SIGNAL_PRIO); |
| |
| join_threads(); |
| |
| printf("\nCriteria: latencies < %d\n", (int)pass_criteria); |
| printf("Result: %s\n", ret ? "FAIL" : "PASS"); |
| |
| return ret; |
| } |