2013-12-14 20:31:55 -08:00
/*
* Copyright ( C ) 2013 Davidlohr Bueso < davidlohr @ hp . com >
*
* futex - hash : Stress the hell out of the Linux kernel futex uaddr hashing .
*
* This program is particularly useful for measuring the kernel ' s futex hash
* table / function implementation . In order for it to make sense , use with as
* many threads and futexes as possible .
*/
2016-07-06 12:14:56 -03:00
/* For the CLR_() macros */
# include <pthread.h>
2016-07-07 11:01:46 -03:00
# include <errno.h>
# include <signal.h>
# include <stdlib.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>
# include <sys/time.h>
2013-12-14 20:31:55 -08:00
# include "../util/stat.h"
2015-12-15 09:39:39 -06:00
# include <subcmd/parse-options.h>
2013-12-14 20:31:55 -08:00
# include "bench.h"
# include "futex.h"
# include <err.h>
# include <sys/time.h>
static unsigned int nthreads = 0 ;
static unsigned int nsecs = 10 ;
/* amount of futexes per thread */
static unsigned int nfutexes = 1024 ;
static bool fshared = false , done = false , silent = false ;
2014-09-29 09:41:07 -07:00
static int futex_flag = 0 ;
2013-12-14 20:31:55 -08:00
struct timeval start , end , runtime ;
static pthread_mutex_t thread_lock ;
static unsigned int threads_starting ;
static struct stats throughput_stats ;
static pthread_cond_t thread_parent , thread_worker ;
struct worker {
int tid ;
u_int32_t * futex ;
pthread_t thread ;
unsigned long ops ;
2016-10-24 13:56:52 -07:00
} ;
2013-12-14 20:31:55 -08:00
static const struct option options [ ] = {
OPT_UINTEGER ( ' t ' , " threads " , & nthreads , " Specify amount of threads " ) ,
OPT_UINTEGER ( ' r ' , " runtime " , & nsecs , " Specify runtime (in seconds) " ) ,
OPT_UINTEGER ( ' f ' , " futexes " , & nfutexes , " Specify amount of futexes per threads " ) ,
OPT_BOOLEAN ( ' s ' , " silent " , & silent , " Silent mode: do not display data/details " ) ,
OPT_BOOLEAN ( ' S ' , " shared " , & fshared , " Use shared futexes instead of private ones " ) ,
OPT_END ( )
} ;
static const char * const bench_futex_hash_usage [ ] = {
" perf bench futex hash <options> " ,
NULL
} ;
static void * workerfn ( void * arg )
{
int ret ;
struct worker * w = ( struct worker * ) arg ;
2016-10-24 13:56:52 -07:00
unsigned int i ;
unsigned long ops = w - > ops ; /* avoid cacheline bouncing */
2013-12-14 20:31:55 -08:00
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 ) ;
do {
2016-10-24 13:56:52 -07:00
for ( i = 0 ; i < nfutexes ; i + + , ops + + ) {
2013-12-14 20:31:55 -08:00
/*
* We want the futex calls to fail in order to stress
* the hashing of uaddr and not measure other steps ,
* such as internal waitqueue handling , thus enlarging
* the critical region protected by hb - > lock .
*/
2014-09-29 09:41:07 -07:00
ret = futex_wait ( & w - > futex [ i ] , 1234 , NULL , futex_flag ) ;
2013-12-14 20:31:55 -08:00
if ( ! silent & &
( ! ret | | errno ! = EAGAIN | | errno ! = EWOULDBLOCK ) )
warn ( " Non-expected futex return call " ) ;
}
} while ( ! done ) ;
2016-10-24 13:56:52 -07:00
w - > ops = ops ;
2013-12-14 20:31:55 -08:00
return NULL ;
}
static void toggle_done ( int sig __maybe_unused ,
siginfo_t * info __maybe_unused ,
void * uc __maybe_unused )
{
/* inform all threads that we're done for the day */
done = true ;
gettimeofday ( & end , NULL ) ;
timersub ( & end , & start , & runtime ) ;
}
static void print_summary ( void )
{
unsigned long avg = avg_stats ( & throughput_stats ) ;
double stddev = stddev_stats ( & throughput_stats ) ;
printf ( " %sAveraged %ld operations/sec (+- %.2f%%), total secs = %d \n " ,
! silent ? " \n " : " " , avg , rel_stddev_stats ( stddev , avg ) ,
( int ) runtime . tv_sec ) ;
}
int bench_futex_hash ( int argc , const char * * argv ,
const char * prefix __maybe_unused )
{
int ret = 0 ;
cpu_set_t cpu ;
struct sigaction act ;
unsigned int i , ncpus ;
pthread_attr_t thread_attr ;
struct worker * worker = NULL ;
argc = parse_options ( argc , argv , options , bench_futex_hash_usage , 0 ) ;
if ( argc ) {
usage_with_options ( bench_futex_hash_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 ) /* default to the number of CPUs */
nthreads = ncpus ;
worker = calloc ( nthreads , sizeof ( * worker ) ) ;
if ( ! worker )
goto errmem ;
2014-09-29 09:41:07 -07:00
if ( ! fshared )
futex_flag = FUTEX_PRIVATE_FLAG ;
2013-12-14 20:31:55 -08:00
printf ( " Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs. \n \n " ,
getpid ( ) , nthreads , nfutexes , fshared ? " shared " : " private " , nsecs ) ;
init_stats ( & throughput_stats ) ;
pthread_mutex_init ( & thread_lock , NULL ) ;
pthread_cond_init ( & thread_parent , NULL ) ;
pthread_cond_init ( & thread_worker , NULL ) ;
threads_starting = nthreads ;
pthread_attr_init ( & thread_attr ) ;
gettimeofday ( & start , NULL ) ;
for ( i = 0 ; i < nthreads ; i + + ) {
worker [ i ] . tid = i ;
worker [ i ] . futex = calloc ( nfutexes , sizeof ( * worker [ i ] . futex ) ) ;
if ( ! worker [ i ] . futex )
goto errmem ;
CPU_ZERO ( & cpu ) ;
CPU_SET ( i % ncpus , & cpu ) ;
ret = pthread_attr_setaffinity_np ( & thread_attr , sizeof ( cpu_set_t ) , & cpu ) ;
if ( ret )
err ( EXIT_FAILURE , " pthread_attr_setaffinity_np " ) ;
ret = pthread_create ( & worker [ i ] . thread , & thread_attr , workerfn ,
( void * ) ( struct worker * ) & worker [ i ] ) ;
if ( ret )
err ( EXIT_FAILURE , " pthread_create " ) ;
}
pthread_attr_destroy ( & thread_attr ) ;
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 ) ;
sleep ( nsecs ) ;
toggle_done ( 0 , NULL , NULL ) ;
for ( i = 0 ; i < nthreads ; i + + ) {
ret = pthread_join ( worker [ i ] . thread , 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 ) ;
for ( i = 0 ; i < nthreads ; i + + ) {
unsigned long t = worker [ i ] . ops / runtime . tv_sec ;
update_stats ( & throughput_stats , t ) ;
if ( ! silent ) {
if ( nfutexes = = 1 )
printf ( " [thread %2d] futex: %p [ %ld ops/sec ] \n " ,
worker [ i ] . tid , & worker [ i ] . futex [ 0 ] , t ) ;
else
printf ( " [thread %2d] futexes: %p ... %p [ %ld ops/sec ] \n " ,
worker [ i ] . tid , & worker [ i ] . futex [ 0 ] ,
& worker [ i ] . futex [ nfutexes - 1 ] , t ) ;
}
free ( worker [ i ] . futex ) ;
}
print_summary ( ) ;
free ( worker ) ;
return ret ;
errmem :
err ( EXIT_FAILURE , " calloc " ) ;
}