2013-12-14 20:31:56 -08:00
/*
* Copyright ( C ) 2013 Davidlohr Bueso < davidlohr @ hp . com >
*
* futex - wake : Block a bunch of threads on a futex and wake ' em up , N at a time .
*
* This program is particularly useful to measure the latency of nthread wakeups
* in non - error situations : all waiters are queued and all wake calls wakeup
* one or more tasks , and thus the waitqueue is never empty .
*/
2016-07-06 12:14:56 -03:00
/* For the CLR_() macros */
perf bench futex: Fix build on musl + clang
When building with clang on a musl libc system, Alpine Linux, we end up
hitting a problem where memset() is used but its prototype is not
present, add it to avoid this:
bench/futex-wake.c:99:3: error: implicitly declaring library function 'memset' with type 'void *(void *, int, unsigned long)'
[-Werror,-Wimplicit-function-declaration]
CPU_ZERO(&cpu);
^
/usr/include/sched.h:127:23: note: expanded from macro 'CPU_ZERO'
#define CPU_ZERO(set) CPU_ZERO_S(sizeof(cpu_set_t),set)
^
/usr/include/sched.h:110:30: note: expanded from macro 'CPU_ZERO_S'
#define CPU_ZERO_S(size,set) memset(set,0,size)
^
bench/futex-wake.c:99:3: note: include the header <string.h> or explicitly provide a declaration for 'memset'
Found while updating my test build containers to build perf with clang in more
systems.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-jh10vaz2r98zl6gm5iau8prr@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-03-02 15:44:19 -03:00
# include <string.h>
2016-07-06 12:14:56 -03:00
# include <pthread.h>
2016-07-07 11:01:46 -03:00
# include <signal.h>
2013-12-14 20:31:56 -08:00
# include "../util/stat.h"
2015-12-15 09:39:39 -06:00
# include <subcmd/parse-options.h>
2016-07-11 10:05:52 -03:00
# include <linux/compiler.h>
2016-07-07 11:01:46 -03:00
# include <linux/kernel.h>
2016-08-08 15:35:21 -03:00
# include <linux/time64.h>
2016-07-07 11:01:46 -03:00
# include <errno.h>
2013-12-14 20:31:56 -08:00
# include "bench.h"
# include "futex.h"
# include <err.h>
# include <stdlib.h>
# include <sys/time.h>
/* all threads will block on the same futex */
static u_int32_t futex1 = 0 ;
/*
* How many wakeups to do at a time .
* Default to 1 in order to make the kernel work more .
*/
static unsigned int nwakes = 1 ;
pthread_t * worker ;
2014-09-29 09:41:07 -07:00
static bool done = false , silent = false , fshared = false ;
2013-12-14 20:31:56 -08:00
static pthread_mutex_t thread_lock ;
static pthread_cond_t thread_parent , thread_worker ;
static struct stats waketime_stats , wakeup_stats ;
static unsigned int ncpus , threads_starting , nthreads = 0 ;
2014-09-29 09:41:07 -07:00
static int futex_flag = 0 ;
2013-12-14 20:31:56 -08:00
static const struct option options [ ] = {
OPT_UINTEGER ( ' t ' , " threads " , & nthreads , " Specify amount of threads " ) ,
OPT_UINTEGER ( ' w ' , " nwakes " , & nwakes , " Specify amount of threads to wake at once " ) ,
OPT_BOOLEAN ( ' s ' , " silent " , & silent , " Silent mode: do not display data/details " ) ,
2014-09-29 09:41:07 -07:00
OPT_BOOLEAN ( ' S ' , " shared " , & fshared , " Use shared futexes instead of private ones " ) ,
2013-12-14 20:31:56 -08:00
OPT_END ( )
} ;
static const char * const bench_futex_wake_usage [ ] = {
" perf bench futex wake <options> " ,
NULL
} ;
static void * workerfn ( void * arg __maybe_unused )
{
pthread_mutex_lock ( & thread_lock ) ;
threads_starting - - ;
if ( ! threads_starting )
pthread_cond_signal ( & thread_parent ) ;
pthread_cond_wait ( & thread_worker , & thread_lock ) ;
pthread_mutex_unlock ( & thread_lock ) ;
2015-05-08 11:38:00 -07:00
while ( 1 ) {
if ( futex_wait ( & futex1 , 0 , NULL , futex_flag ) ! = EINTR )
break ;
}
pthread_exit ( NULL ) ;
2013-12-14 20:31:56 -08:00
return NULL ;
}
static void print_summary ( void )
{
double waketime_avg = avg_stats ( & waketime_stats ) ;
double waketime_stddev = stddev_stats ( & waketime_stats ) ;
unsigned int wakeup_avg = avg_stats ( & wakeup_stats ) ;
printf ( " Wokeup %d of %d threads in %.4f ms (+-%.2f%%) \n " ,
wakeup_avg ,
nthreads ,
2016-08-08 15:35:21 -03:00
waketime_avg / USEC_PER_MSEC ,
2013-12-14 20:31:56 -08:00
rel_stddev_stats ( waketime_stddev , waketime_avg ) ) ;
}
static void block_threads ( pthread_t * w ,
pthread_attr_t thread_attr )
{
cpu_set_t cpu ;
unsigned int i ;
threads_starting = nthreads ;
/* create and block all threads */
for ( i = 0 ; i < nthreads ; i + + ) {
CPU_ZERO ( & cpu ) ;
CPU_SET ( i % ncpus , & cpu ) ;
if ( pthread_attr_setaffinity_np ( & thread_attr , sizeof ( cpu_set_t ) , & cpu ) )
err ( EXIT_FAILURE , " pthread_attr_setaffinity_np " ) ;
if ( pthread_create ( & w [ i ] , & thread_attr , workerfn , NULL ) )
err ( EXIT_FAILURE , " pthread_create " ) ;
}
}
static void toggle_done ( int sig __maybe_unused ,
siginfo_t * info __maybe_unused ,
void * uc __maybe_unused )
{
done = true ;
}
2017-03-27 11:47:20 -03:00
int bench_futex_wake ( int argc , const char * * argv )
2013-12-14 20:31:56 -08:00
{
int ret = 0 ;
unsigned int i , j ;
struct sigaction act ;
pthread_attr_t thread_attr ;
argc = parse_options ( argc , argv , options , bench_futex_wake_usage , 0 ) ;
if ( argc ) {
usage_with_options ( bench_futex_wake_usage , options ) ;
exit ( EXIT_FAILURE ) ;
}
ncpus = sysconf ( _SC_NPROCESSORS_ONLN ) ;
sigfillset ( & act . sa_mask ) ;
act . sa_sigaction = toggle_done ;
sigaction ( SIGINT , & act , NULL ) ;
if ( ! nthreads )
nthreads = ncpus ;
worker = calloc ( nthreads , sizeof ( * worker ) ) ;
if ( ! worker )
err ( EXIT_FAILURE , " calloc " ) ;
2014-09-29 09:41:07 -07:00
if ( ! fshared )
futex_flag = FUTEX_PRIVATE_FLAG ;
printf ( " Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), "
2013-12-14 20:31:56 -08:00
" waking up %d at a time. \n \n " ,
2014-09-29 09:41:07 -07:00
getpid ( ) , nthreads , fshared ? " shared " : " private " , & futex1 , nwakes ) ;
2013-12-14 20:31:56 -08:00
init_stats ( & wakeup_stats ) ;
init_stats ( & waketime_stats ) ;
pthread_attr_init ( & thread_attr ) ;
pthread_mutex_init ( & thread_lock , NULL ) ;
pthread_cond_init ( & thread_parent , NULL ) ;
pthread_cond_init ( & thread_worker , NULL ) ;
2014-06-16 11:14:23 -07:00
for ( j = 0 ; j < bench_repeat & & ! done ; j + + ) {
2013-12-14 20:31:56 -08:00
unsigned int nwoken = 0 ;
struct timeval start , end , runtime ;
/* create, launch & block all threads */
block_threads ( worker , thread_attr ) ;
/* make sure all threads are already blocked */
pthread_mutex_lock ( & thread_lock ) ;
while ( threads_starting )
pthread_cond_wait ( & thread_parent , & thread_lock ) ;
pthread_cond_broadcast ( & thread_worker ) ;
pthread_mutex_unlock ( & thread_lock ) ;
usleep ( 100000 ) ;
/* Ok, all threads are patiently blocked, start waking folks up */
gettimeofday ( & start , NULL ) ;
while ( nwoken ! = nthreads )
2014-09-29 09:41:07 -07:00
nwoken + = futex_wake ( & futex1 , nwakes , futex_flag ) ;
2013-12-14 20:31:56 -08:00
gettimeofday ( & end , NULL ) ;
timersub ( & end , & start , & runtime ) ;
update_stats ( & wakeup_stats , nwoken ) ;
update_stats ( & waketime_stats , runtime . tv_usec ) ;
if ( ! silent ) {
printf ( " [Run %d]: Wokeup %d of %d threads in %.4f ms \n " ,
2016-08-08 15:35:21 -03:00
j + 1 , nwoken , nthreads , runtime . tv_usec / ( double ) USEC_PER_MSEC ) ;
2013-12-14 20:31:56 -08:00
}
for ( i = 0 ; i < nthreads ; i + + ) {
ret = pthread_join ( worker [ i ] , NULL ) ;
if ( ret )
err ( EXIT_FAILURE , " pthread_join " ) ;
}
}
/* cleanup & report results */
pthread_cond_destroy ( & thread_parent ) ;
pthread_cond_destroy ( & thread_worker ) ;
pthread_mutex_destroy ( & thread_lock ) ;
pthread_attr_destroy ( & thread_attr ) ;
print_summary ( ) ;
free ( worker ) ;
return ret ;
}