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
2009-11-20 06:37:17 +03:00
static const char * length_str = " 1MB " ;
2015-10-19 11:04:18 +03:00
static const char * routine = " all " ;
2012-01-18 17:29:59 +04:00
static int iterations = 1 ;
2012-07-02 17:46:17 +04:00
static bool use_cycle ;
static int cycle_fd ;
2009-11-17 18:20:09 +03:00
static const struct option options [ ] = {
OPT_STRING ( ' l ' , " length " , & length_str , " 1MB " ,
" Specify length of memory to copy. "
2012-06-20 10:08:06 +04:00
" Available units: B, KB, MB, GB and TB (upper and lower) " ) ,
2015-10-19 11:04:18 +03:00
OPT_STRING ( ' r ' , " routine " , & routine , " all " ,
2015-02-26 21:02:43 +03:00
" Specify routine to copy, \" all \" runs all available routines " ) ,
2012-01-18 17:29:59 +04:00
OPT_INTEGER ( ' i ' , " iterations " , & iterations ,
" repeat memcpy() invocation this number of times " ) ,
2012-07-02 17:46:17 +04:00
OPT_BOOLEAN ( ' c ' , " cycle " , & use_cycle ,
2012-06-20 10:08:06 +04:00
" Use cycles event instead of gettimeofday() for measuring " ) ,
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
2009-11-17 18:20:09 +03:00
struct routine {
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
} ;
2014-12-02 18:50:39 +03:00
struct routine memcpy_routines [ ] = {
2015-10-19 11:04:17 +03:00
{ . name = " default " ,
. desc = " Default memcpy() provided by glibc " ,
. fn . memcpy = memcpy } ,
2010-11-25 10:04:52 +03:00
2015-10-19 11:04:17 +03:00
# 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
2010-11-25 10:04:52 +03:00
# endif
2015-10-19 11:04:17 +03:00
{ NULL , }
2009-11-17 18:20:09 +03:00
} ;
static const char * const bench_mem_memcpy_usage [ ] = {
" perf bench mem memcpy <options> " ,
NULL
} ;
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
} ;
2012-07-02 17:46:17 +04:00
static void init_cycle ( void )
2009-11-17 18:20:09 +03:00
{
2015-10-19 11:04:17 +03:00
cycle_fd = sys_perf_event_open ( & cycle_attr , getpid ( ) , - 1 , - 1 , perf_event_open_cloexec_flag ( ) ) ;
2009-11-20 06:37:17 +03:00
2012-07-02 17:46:17 +04:00
if ( cycle_fd < 0 & & errno = = ENOSYS )
2009-11-20 06:37:17 +03:00
die ( " No CONFIG_PERF_EVENTS=y kernel support configured? \n " ) ;
else
2012-07-02 17:46:17 +04:00
BUG_ON ( cycle_fd < 0 ) ;
2009-11-17 18:20:09 +03:00
}
2012-07-02 17:46:17 +04:00
static u64 get_cycle ( void )
2009-11-17 18:20:09 +03:00
{
int ret ;
u64 clk ;
2012-07-02 17:46:17 +04:00
ret = read ( cycle_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 ) \
printf ( " %14lf B/Sec \n " , x ) ; \
else if ( x < K * K ) \
printf ( " %14lfd KB/Sec \n " , x / K ) ; \
else if ( x < K * K * K ) \
printf ( " %14lf MB/Sec \n " , x / K / K ) ; \
else \
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 {
const struct routine * routines ;
2015-10-19 11:04:21 +03:00
u64 ( * do_cycle ) ( const struct routine * r , size_t len ) ;
double ( * do_gettimeofday ) ( const struct routine * r , size_t len ) ;
2014-12-02 18:50:39 +03:00
const char * const * usage ;
} ;
2015-02-26 20:51:37 +03:00
static void __bench_mem_routine ( struct bench_mem_info * info , int r_idx , size_t len , double totallen )
2009-11-17 18:20:09 +03:00
{
2015-02-26 20:51:37 +03:00
const struct routine * r = & info - > routines [ r_idx ] ;
2015-10-19 11:04:21 +03:00
double result_bps = 0.0 ;
u64 result_cycle = 0 ;
2010-11-25 10:04:52 +03:00
2015-02-26 21:02:43 +03:00
printf ( " Routine %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 )
printf ( " # Copying %s Bytes ... \n \n " , length_str ) ;
2009-11-17 18:20:09 +03:00
2015-10-19 11:04:21 +03:00
if ( use_cycle ) {
result_cycle = info - > do_cycle ( r , len ) ;
2009-11-17 18:20:09 +03:00
} else {
2015-10-19 11:04:21 +03:00
result_bps = info - > do_gettimeofday ( r , len ) ;
2009-11-17 18:20:09 +03:00
}
switch ( bench_format ) {
case BENCH_FORMAT_DEFAULT :
2015-10-19 11:04:21 +03:00
if ( use_cycle ) {
printf ( " %14lf Cycle/Byte \n " , ( double ) result_cycle / totallen ) ;
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:21 +03:00
if ( use_cycle ) {
printf ( " %lf \n " , ( double ) result_cycle / totallen ) ;
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 ;
size_t len ;
double totallen ;
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
if ( use_cycle )
init_cycle ( ) ;
len = ( size_t ) perf_atoll ( ( char * ) length_str ) ;
totallen = ( double ) len * iterations ;
if ( ( s64 ) len < = 0 ) {
fprintf ( stderr , " Invalid length:%s \n " , length_str ) ;
return 1 ;
}
2015-02-26 21:02:43 +03:00
if ( ! strncmp ( routine , " all " , 3 ) ) {
for ( i = 0 ; info - > routines [ i ] . name ; i + + )
__bench_mem_routine ( info , i , len , totallen ) ;
return 0 ;
}
2015-02-26 20:51:37 +03:00
for ( i = 0 ; info - > routines [ i ] . name ; i + + ) {
if ( ! strcmp ( info - > routines [ i ] . name , routine ) )
break ;
}
if ( ! info - > routines [ i ] . name ) {
printf ( " Unknown routine:%s \n " , routine ) ;
printf ( " Available routines... \n " ) ;
for ( i = 0 ; info - > routines [ i ] . name ; i + + ) {
printf ( " \t %s ... %s \n " ,
info - > routines [ i ] . name , info - > routines [ i ] . desc ) ;
}
return 1 ;
}
__bench_mem_routine ( info , i , len , totallen ) ;
2009-11-17 18:20:09 +03:00
return 0 ;
}
2014-12-02 18:50:39 +03:00
static void memcpy_alloc_mem ( void * * dst , void * * src , size_t length )
{
* dst = zalloc ( length ) ;
if ( ! * dst )
die ( " memory allocation failed - maybe length is too large? \n " ) ;
* src = zalloc ( length ) ;
if ( ! * src )
die ( " memory allocation failed - maybe length 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: */
2014-12-02 18:50:39 +03:00
memset ( * src , 0 , length ) ;
}
2015-10-19 11:04:21 +03:00
static u64 do_memcpy_cycle ( const struct routine * r , size_t len )
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-01-15 12:20:22 +03:00
memcpy_alloc_mem ( & dst , & src , len ) ;
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 :
*/
fn ( dst , src , len ) ;
2014-12-02 18:50:39 +03:00
cycle_start = get_cycle ( ) ;
for ( i = 0 ; i < iterations ; + + i )
fn ( dst , src , len ) ;
cycle_end = get_cycle ( ) ;
free ( src ) ;
free ( dst ) ;
return cycle_end - cycle_start ;
}
2015-10-19 11:04:21 +03:00
static double do_memcpy_gettimeofday ( const struct routine * r , size_t len )
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-01-15 12:20:22 +03:00
memcpy_alloc_mem ( & dst , & src , len ) ;
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 :
*/
fn ( dst , src , len ) ;
2014-12-02 18:50:39 +03:00
BUG_ON ( gettimeofday ( & tv_start , NULL ) ) ;
for ( i = 0 ; i < iterations ; + + i )
fn ( dst , src , len ) ;
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
2014-12-02 18:50:41 +03:00
return ( double ) ( ( ( double ) len * iterations ) / timeval2double ( & tv_diff ) ) ;
2014-12-02 18:50:39 +03:00
}
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:17 +03:00
. routines = memcpy_routines ,
. do_cycle = do_memcpy_cycle ,
. 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
static void memset_alloc_mem ( void * * dst , size_t length )
{
* dst = zalloc ( length ) ;
if ( ! * dst )
die ( " memory allocation failed - maybe length is too large? \n " ) ;
}
2015-10-19 11:04:21 +03:00
static u64 do_memset_cycle ( const struct routine * r , size_t len )
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 ;
memset_alloc_mem ( & dst , len ) ;
2015-10-19 11:04:21 +03:00
/*
* We prefault the freshly allocated memory range here ,
* to not measure page fault overhead :
*/
fn ( dst , - 1 , len ) ;
2014-12-02 18:50:40 +03:00
cycle_start = get_cycle ( ) ;
for ( i = 0 ; i < iterations ; + + i )
fn ( dst , i , len ) ;
cycle_end = get_cycle ( ) ;
free ( dst ) ;
return cycle_end - cycle_start ;
}
2015-10-19 11:04:21 +03:00
static double do_memset_gettimeofday ( const struct routine * r , size_t len )
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 ;
memset_alloc_mem ( & dst , len ) ;
2015-10-19 11:04:21 +03:00
/*
* We prefault the freshly allocated memory range here ,
* to not measure page fault overhead :
*/
fn ( dst , - 1 , len ) ;
2014-12-02 18:50:40 +03:00
BUG_ON ( gettimeofday ( & tv_start , NULL ) ) ;
for ( i = 0 ; i < iterations ; + + i )
fn ( dst , i , len ) ;
BUG_ON ( gettimeofday ( & tv_end , NULL ) ) ;
timersub ( & tv_end , & tv_start , & tv_diff ) ;
free ( dst ) ;
2014-12-02 18:50:41 +03:00
return ( double ) ( ( ( double ) len * iterations ) / 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
} ;
static const struct routine memset_routines [ ] = {
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-19 11:04:17 +03:00
{ 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:17 +03:00
. routines = memset_routines ,
. do_cycle = do_memset_cycle ,
. 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
}