2009-11-17 18:20:09 +03:00
/*
* mem - memcpy . c
*
2015-10-19 11:04:17 +03:00
* Simple memcpy ( ) and memset ( ) benchmarks
2009-11-17 18:20:09 +03:00
*
* Written by Hitoshi Mitake < mitake @ dcl . info . waseda . ac . jp >
*/
# include "../perf.h"
# include "../util/util.h"
# include "../util/parse-options.h"
# include "../util/header.h"
2014-07-01 00:28:47 +04:00
# include "../util/cloexec.h"
2009-11-17 18:20:09 +03:00
# include "bench.h"
2010-11-25 10:04:52 +03:00
# include "mem-memcpy-arch.h"
2014-12-02 18:50:40 +03:00
# include "mem-memset-arch.h"
2009-11-17 18:20:09 +03:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/time.h>
# include <errno.h>
# define K 1024
2015-10-19 11:04:25 +03:00
static const char * size_str = " 1MB " ;
2015-10-19 11:04:29 +03:00
static const char * function_str = " all " ;
2015-10-19 11:04:28 +03:00
static int nr_loops = 1 ;
2015-10-19 11:04:23 +03:00
static bool use_cycles ;
static int cycles_fd ;
2009-11-17 18:20:09 +03:00
static const struct option options [ ] = {
2015-10-19 11:04:28 +03:00
OPT_STRING ( ' s ' , " size " , & size_str , " 1MB " ,
2015-10-19 11:04:25 +03:00
" Specify the size of the memory buffers. "
2015-10-19 11:04:26 +03:00
" Available units: B, KB, MB, GB and TB (case insensitive) " ) ,
2015-10-19 11:04:29 +03:00
OPT_STRING ( ' f ' , " function " , & function_str , " all " ,
" Specify the function to run, \" all \" runs all available functions, \" help \" lists them " ) ,
2015-10-19 11:04:26 +03:00
2015-10-19 11:04:28 +03:00
OPT_INTEGER ( ' l ' , " nr_loops " , & nr_loops ,
" Specify the number of loops to run. (default: 1) " ) ,
2015-10-19 11:04:26 +03:00
2015-10-19 11:04:23 +03:00
OPT_BOOLEAN ( ' c ' , " cycles " , & use_cycles ,
" Use a cycles event instead of gettimeofday() to measure performance " ) ,
2015-10-19 11:04:26 +03:00
2009-11-17 18:20:09 +03:00
OPT_END ( )
} ;
2010-11-25 10:04:52 +03:00
typedef void * ( * memcpy_t ) ( void * , const void * , size_t ) ;
2014-12-02 18:50:40 +03:00
typedef void * ( * memset_t ) ( void * , int , size_t ) ;
2010-11-25 10:04:52 +03:00
2015-10-19 11:04:29 +03:00
struct function {
2009-11-17 18:20:09 +03:00
const char * name ;
const char * desc ;
2014-12-02 18:50:39 +03:00
union {
memcpy_t memcpy ;
2014-12-02 18:50:40 +03:00
memset_t memset ;
2014-12-02 18:50:39 +03:00
} fn ;
2009-11-17 18:20:09 +03:00
} ;
2012-07-02 17:46:17 +04:00
static struct perf_event_attr cycle_attr = {
2009-11-20 06:37:17 +03:00
. type = PERF_TYPE_HARDWARE ,
. config = PERF_COUNT_HW_CPU_CYCLES
2009-11-17 18:20:09 +03:00
} ;
2015-10-19 11:04:23 +03:00
static void init_cycles ( void )
2009-11-17 18:20:09 +03:00
{
2015-10-19 11:04:23 +03:00
cycles_fd = sys_perf_event_open ( & cycle_attr , getpid ( ) , - 1 , - 1 , perf_event_open_cloexec_flag ( ) ) ;
2009-11-20 06:37:17 +03:00
2015-10-19 11:04:23 +03:00
if ( cycles_fd < 0 & & errno = = ENOSYS )
2009-11-20 06:37:17 +03:00
die ( " No CONFIG_PERF_EVENTS=y kernel support configured? \n " ) ;
else
2015-10-19 11:04:23 +03:00
BUG_ON ( cycles_fd < 0 ) ;
2009-11-17 18:20:09 +03:00
}
2015-10-19 11:04:23 +03:00
static u64 get_cycles ( void )
2009-11-17 18:20:09 +03:00
{
int ret ;
u64 clk ;
2015-10-19 11:04:23 +03:00
ret = read ( cycles_fd , & clk , sizeof ( u64 ) ) ;
2009-11-17 18:20:09 +03:00
BUG_ON ( ret ! = sizeof ( u64 ) ) ;
return clk ;
}
static double timeval2double ( struct timeval * ts )
{
2015-10-19 11:04:17 +03:00
return ( double ) ts - > tv_sec + ( double ) ts - > tv_usec / ( double ) 1000000 ;
2009-11-17 18:20:09 +03:00
}
2015-10-19 11:04:21 +03:00
# define print_bps(x) do { \
if ( x < K ) \
2015-10-19 11:04:26 +03:00
printf ( " %14lf bytes/sec \n " , x ) ; \
2015-10-19 11:04:21 +03:00
else if ( x < K * K ) \
2015-10-19 11:04:26 +03:00
printf ( " %14lfd KB/sec \n " , x / K ) ; \
2015-10-19 11:04:21 +03:00
else if ( x < K * K * K ) \
2015-10-19 11:04:26 +03:00
printf ( " %14lf MB/sec \n " , x / K / K ) ; \
2015-10-19 11:04:21 +03:00
else \
2015-10-19 11:04:26 +03:00
printf ( " %14lf GB/sec \n " , x / K / K / K ) ; \
2010-11-25 10:04:52 +03:00
} while ( 0 )
2014-12-02 18:50:39 +03:00
struct bench_mem_info {
2015-10-19 11:04:29 +03:00
const struct function * functions ;
u64 ( * do_cycles ) ( const struct function * r , size_t size ) ;
double ( * do_gettimeofday ) ( const struct function * r , size_t size ) ;
2014-12-02 18:50:39 +03:00
const char * const * usage ;
} ;
2015-10-19 11:04:29 +03:00
static void __bench_mem_function ( struct bench_mem_info * info , int r_idx , size_t size , double size_total )
2009-11-17 18:20:09 +03:00
{
2015-10-19 11:04:29 +03:00
const struct function * r = & info - > functions [ r_idx ] ;
2015-10-19 11:04:21 +03:00
double result_bps = 0.0 ;
2015-10-19 11:04:23 +03:00
u64 result_cycles = 0 ;
2010-11-25 10:04:52 +03:00
2015-10-19 11:04:29 +03:00
printf ( " # function '%s' (%s) \n " , r - > name , r - > desc ) ;
2009-11-17 18:20:09 +03:00
2010-11-25 10:04:52 +03:00
if ( bench_format = = BENCH_FORMAT_DEFAULT )
2015-10-19 11:04:26 +03:00
printf ( " # Copying %s bytes ... \n \n " , size_str ) ;
2009-11-17 18:20:09 +03:00
2015-10-19 11:04:23 +03:00
if ( use_cycles ) {
2015-10-19 11:04:25 +03:00
result_cycles = info - > do_cycles ( r , size ) ;
2009-11-17 18:20:09 +03:00
} else {
2015-10-19 11:04:25 +03:00
result_bps = info - > do_gettimeofday ( r , size ) ;
2009-11-17 18:20:09 +03:00
}
switch ( bench_format ) {
case BENCH_FORMAT_DEFAULT :
2015-10-19 11:04:23 +03:00
if ( use_cycles ) {
2015-10-19 11:04:26 +03:00
printf ( " %14lf cycles/byte \n " , ( double ) result_cycles / size_total ) ;
2010-11-25 10:04:52 +03:00
} else {
2015-10-19 11:04:21 +03:00
print_bps ( result_bps ) ;
2009-11-17 18:20:09 +03:00
}
break ;
2015-10-19 11:04:21 +03:00
2009-11-17 18:20:09 +03:00
case BENCH_FORMAT_SIMPLE :
2015-10-19 11:04:23 +03:00
if ( use_cycles ) {
2015-10-19 11:04:25 +03:00
printf ( " %lf \n " , ( double ) result_cycles / size_total ) ;
2010-11-25 10:04:52 +03:00
} else {
2015-10-19 11:04:21 +03:00
printf ( " %lf \n " , result_bps ) ;
2010-11-25 10:04:52 +03:00
}
2009-11-17 18:20:09 +03:00
break ;
2015-10-19 11:04:21 +03:00
2009-11-17 18:20:09 +03:00
default :
2015-10-19 11:04:21 +03:00
BUG_ON ( 1 ) ;
2009-11-17 18:20:09 +03:00
break ;
}
2015-02-26 20:51:37 +03:00
}
2015-10-19 11:04:19 +03:00
static int bench_mem_common ( int argc , const char * * argv , struct bench_mem_info * info )
2015-02-26 20:51:37 +03:00
{
int i ;
2015-10-19 11:04:25 +03:00
size_t size ;
double size_total ;
2015-02-26 20:51:37 +03:00
2015-10-19 11:04:17 +03:00
argc = parse_options ( argc , argv , options , info - > usage , 0 ) ;
2015-02-26 20:51:37 +03:00
2015-10-19 11:04:23 +03:00
if ( use_cycles )
init_cycles ( ) ;
2015-02-26 20:51:37 +03:00
2015-10-19 11:04:25 +03:00
size = ( size_t ) perf_atoll ( ( char * ) size_str ) ;
2015-10-19 11:04:28 +03:00
size_total = ( double ) size * nr_loops ;
2015-02-26 20:51:37 +03:00
2015-10-19 11:04:25 +03:00
if ( ( s64 ) size < = 0 ) {
fprintf ( stderr , " Invalid size:%s \n " , size_str ) ;
2015-02-26 20:51:37 +03:00
return 1 ;
}
2015-10-19 11:04:29 +03:00
if ( ! strncmp ( function_str , " all " , 3 ) ) {
for ( i = 0 ; info - > functions [ i ] . name ; i + + )
__bench_mem_function ( info , i , size , size_total ) ;
2015-02-26 21:02:43 +03:00
return 0 ;
}
2015-10-19 11:04:29 +03:00
for ( i = 0 ; info - > functions [ i ] . name ; i + + ) {
if ( ! strcmp ( info - > functions [ i ] . name , function_str ) )
2015-02-26 20:51:37 +03:00
break ;
}
2015-10-19 11:04:29 +03:00
if ( ! info - > functions [ i ] . name ) {
if ( strcmp ( function_str , " help " ) & & strcmp ( function_str , " h " ) )
printf ( " Unknown function: %s \n " , function_str ) ;
printf ( " Available functions: \n " ) ;
for ( i = 0 ; info - > functions [ i ] . name ; i + + ) {
2015-02-26 20:51:37 +03:00
printf ( " \t %s ... %s \n " ,
2015-10-19 11:04:29 +03:00
info - > functions [ i ] . name , info - > functions [ i ] . desc ) ;
2015-02-26 20:51:37 +03:00
}
return 1 ;
}
2015-10-19 11:04:29 +03:00
__bench_mem_function ( info , i , size , size_total ) ;
2009-11-17 18:20:09 +03:00
return 0 ;
}
2014-12-02 18:50:39 +03:00
2015-10-19 11:04:25 +03:00
static void memcpy_alloc_mem ( void * * dst , void * * src , size_t size )
2014-12-02 18:50:39 +03:00
{
2015-10-19 11:04:25 +03:00
* dst = zalloc ( size ) ;
2014-12-02 18:50:39 +03:00
if ( ! * dst )
2015-10-19 11:04:25 +03:00
die ( " memory allocation failed - maybe size is too large? \n " ) ;
2014-12-02 18:50:39 +03:00
2015-10-19 11:04:25 +03:00
* src = zalloc ( size ) ;
2014-12-02 18:50:39 +03:00
if ( ! * src )
2015-10-19 11:04:25 +03:00
die ( " memory allocation failed - maybe size is too large? \n " ) ;
2015-10-19 11:04:17 +03:00
/* Make sure to always prefault zero pages even if MMAP_THRESH is crossed: */
2015-10-19 11:04:25 +03:00
memset ( * src , 0 , size ) ;
2014-12-02 18:50:39 +03:00
}
2015-10-19 11:04:29 +03:00
static u64 do_memcpy_cycles ( const struct function * r , size_t size )
2014-12-02 18:50:39 +03:00
{
u64 cycle_start = 0ULL , cycle_end = 0ULL ;
void * src = NULL , * dst = NULL ;
memcpy_t fn = r - > fn . memcpy ;
int i ;
2015-10-19 11:04:25 +03:00
memcpy_alloc_mem ( & dst , & src , size ) ;
2014-12-02 18:50:39 +03:00
2015-10-19 11:04:21 +03:00
/*
* We prefault the freshly allocated memory range here ,
* to not measure page fault overhead :
*/
2015-10-19 11:04:25 +03:00
fn ( dst , src , size ) ;
2014-12-02 18:50:39 +03:00
2015-10-19 11:04:23 +03:00
cycle_start = get_cycles ( ) ;
2015-10-19 11:04:28 +03:00
for ( i = 0 ; i < nr_loops ; + + i )
2015-10-19 11:04:25 +03:00
fn ( dst , src , size ) ;
2015-10-19 11:04:23 +03:00
cycle_end = get_cycles ( ) ;
2014-12-02 18:50:39 +03:00
free ( src ) ;
free ( dst ) ;
return cycle_end - cycle_start ;
}
2015-10-19 11:04:29 +03:00
static double do_memcpy_gettimeofday ( const struct function * r , size_t size )
2014-12-02 18:50:39 +03:00
{
struct timeval tv_start , tv_end , tv_diff ;
memcpy_t fn = r - > fn . memcpy ;
void * src = NULL , * dst = NULL ;
int i ;
2015-10-19 11:04:25 +03:00
memcpy_alloc_mem ( & dst , & src , size ) ;
2014-12-02 18:50:39 +03:00
2015-10-19 11:04:21 +03:00
/*
* We prefault the freshly allocated memory range here ,
* to not measure page fault overhead :
*/
2015-10-19 11:04:25 +03:00
fn ( dst , src , size ) ;
2014-12-02 18:50:39 +03:00
BUG_ON ( gettimeofday ( & tv_start , NULL ) ) ;
2015-10-19 11:04:28 +03:00
for ( i = 0 ; i < nr_loops ; + + i )
2015-10-19 11:04:25 +03:00
fn ( dst , src , size ) ;
2014-12-02 18:50:39 +03:00
BUG_ON ( gettimeofday ( & tv_end , NULL ) ) ;
timersub ( & tv_end , & tv_start , & tv_diff ) ;
free ( src ) ;
free ( dst ) ;
2015-10-19 11:04:21 +03:00
2015-10-19 11:04:28 +03:00
return ( double ) ( ( ( double ) size * nr_loops ) / timeval2double ( & tv_diff ) ) ;
2014-12-02 18:50:39 +03:00
}
2015-10-19 11:04:29 +03:00
struct function memcpy_functions [ ] = {
2015-10-19 11:04:27 +03:00
{ . name = " default " ,
. desc = " Default memcpy() provided by glibc " ,
. fn . memcpy = memcpy } ,
# ifdef HAVE_ARCH_X86_64_SUPPORT
# define MEMCPY_FN(_fn, _name, _desc) {.name = _name, .desc = _desc, .fn.memcpy = _fn},
# include "mem-memcpy-x86-64-asm-def.h"
# undef MEMCPY_FN
# endif
2015-10-20 00:17:25 +03:00
{ . name = NULL , }
2015-10-19 11:04:27 +03:00
} ;
static const char * const bench_mem_memcpy_usage [ ] = {
" perf bench mem memcpy <options> " ,
NULL
} ;
2015-10-19 11:04:19 +03:00
int bench_mem_memcpy ( int argc , const char * * argv , const char * prefix __maybe_unused )
2014-12-02 18:50:39 +03:00
{
struct bench_mem_info info = {
2015-10-19 11:04:29 +03:00
. functions = memcpy_functions ,
2015-10-19 11:04:23 +03:00
. do_cycles = do_memcpy_cycles ,
2015-10-19 11:04:17 +03:00
. do_gettimeofday = do_memcpy_gettimeofday ,
. usage = bench_mem_memcpy_usage ,
2014-12-02 18:50:39 +03:00
} ;
2015-10-19 11:04:19 +03:00
return bench_mem_common ( argc , argv , & info ) ;
2014-12-02 18:50:39 +03:00
}
2014-12-02 18:50:40 +03:00
2015-10-19 11:04:25 +03:00
static void memset_alloc_mem ( void * * dst , size_t size )
2014-12-02 18:50:40 +03:00
{
2015-10-19 11:04:25 +03:00
* dst = zalloc ( size ) ;
2014-12-02 18:50:40 +03:00
if ( ! * dst )
2015-10-19 11:04:25 +03:00
die ( " memory allocation failed - maybe size is too large? \n " ) ;
2014-12-02 18:50:40 +03:00
}
2015-10-19 11:04:29 +03:00
static u64 do_memset_cycles ( const struct function * r , size_t size )
2014-12-02 18:50:40 +03:00
{
u64 cycle_start = 0ULL , cycle_end = 0ULL ;
memset_t fn = r - > fn . memset ;
void * dst = NULL ;
int i ;
2015-10-19 11:04:25 +03:00
memset_alloc_mem ( & dst , size ) ;
2014-12-02 18:50:40 +03:00
2015-10-19 11:04:21 +03:00
/*
* We prefault the freshly allocated memory range here ,
* to not measure page fault overhead :
*/
2015-10-19 11:04:25 +03:00
fn ( dst , - 1 , size ) ;
2014-12-02 18:50:40 +03:00
2015-10-19 11:04:23 +03:00
cycle_start = get_cycles ( ) ;
2015-10-19 11:04:28 +03:00
for ( i = 0 ; i < nr_loops ; + + i )
2015-10-19 11:04:25 +03:00
fn ( dst , i , size ) ;
2015-10-19 11:04:23 +03:00
cycle_end = get_cycles ( ) ;
2014-12-02 18:50:40 +03:00
free ( dst ) ;
return cycle_end - cycle_start ;
}
2015-10-19 11:04:29 +03:00
static double do_memset_gettimeofday ( const struct function * r , size_t size )
2014-12-02 18:50:40 +03:00
{
struct timeval tv_start , tv_end , tv_diff ;
memset_t fn = r - > fn . memset ;
void * dst = NULL ;
int i ;
2015-10-19 11:04:25 +03:00
memset_alloc_mem ( & dst , size ) ;
2014-12-02 18:50:40 +03:00
2015-10-19 11:04:21 +03:00
/*
* We prefault the freshly allocated memory range here ,
* to not measure page fault overhead :
*/
2015-10-19 11:04:25 +03:00
fn ( dst , - 1 , size ) ;
2014-12-02 18:50:40 +03:00
BUG_ON ( gettimeofday ( & tv_start , NULL ) ) ;
2015-10-19 11:04:28 +03:00
for ( i = 0 ; i < nr_loops ; + + i )
2015-10-19 11:04:25 +03:00
fn ( dst , i , size ) ;
2014-12-02 18:50:40 +03:00
BUG_ON ( gettimeofday ( & tv_end , NULL ) ) ;
timersub ( & tv_end , & tv_start , & tv_diff ) ;
free ( dst ) ;
2015-10-19 11:04:28 +03:00
return ( double ) ( ( ( double ) size * nr_loops ) / timeval2double ( & tv_diff ) ) ;
2014-12-02 18:50:40 +03:00
}
static const char * const bench_mem_memset_usage [ ] = {
" perf bench mem memset <options> " ,
NULL
} ;
2015-10-19 11:04:29 +03:00
static const struct function memset_functions [ ] = {
2015-10-19 11:04:17 +03:00
{ . name = " default " ,
. desc = " Default memset() provided by glibc " ,
. fn . memset = memset } ,
2014-12-02 18:50:40 +03:00
2015-10-19 11:04:17 +03:00
# ifdef HAVE_ARCH_X86_64_SUPPORT
# define MEMSET_FN(_fn, _name, _desc) { .name = _name, .desc = _desc, .fn.memset = _fn },
# include "mem-memset-x86-64-asm-def.h"
# undef MEMSET_FN
2014-12-02 18:50:40 +03:00
# endif
2015-10-20 00:17:25 +03:00
{ . name = NULL , }
2014-12-02 18:50:40 +03:00
} ;
2015-10-19 11:04:17 +03:00
int bench_mem_memset ( int argc , const char * * argv , const char * prefix __maybe_unused )
2014-12-02 18:50:40 +03:00
{
struct bench_mem_info info = {
2015-10-19 11:04:29 +03:00
. functions = memset_functions ,
2015-10-19 11:04:23 +03:00
. do_cycles = do_memset_cycles ,
2015-10-19 11:04:17 +03:00
. do_gettimeofday = do_memset_gettimeofday ,
. usage = bench_mem_memset_usage ,
2014-12-02 18:50:40 +03:00
} ;
2015-10-19 11:04:19 +03:00
return bench_mem_common ( argc , argv , & info ) ;
2014-12-02 18:50:40 +03:00
}