2009-11-17 18:20:09 +03:00
/*
* mem - memcpy . c
*
* memcpy : Simple memory copy in various ways
*
* 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"
# include "bench.h"
2010-11-25 10:04:52 +03:00
# include "mem-memcpy-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 " ;
static const char * routine = " default " ;
2012-01-18 17:29:59 +04:00
static int iterations = 1 ;
2010-11-25 10:04:52 +03:00
static bool use_clock ;
2009-11-20 06:37:17 +03:00
static int clock_fd ;
2010-11-25 10:04:52 +03:00
static bool only_prefault ;
static bool no_prefault ;
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. "
" available unit: B, MB, GB (upper and lower) " ) ,
OPT_STRING ( ' r ' , " routine " , & routine , " default " ,
" Specify routine to copy " ) ,
2012-01-18 17:29:59 +04:00
OPT_INTEGER ( ' i ' , " iterations " , & iterations ,
" repeat memcpy() invocation this number of times " ) ,
2009-11-17 18:20:09 +03:00
OPT_BOOLEAN ( ' c ' , " clock " , & use_clock ,
" Use CPU clock for measuring " ) ,
2010-11-25 10:04:52 +03:00
OPT_BOOLEAN ( ' o ' , " only-prefault " , & only_prefault ,
" Show only the result with page faults before memcpy() " ) ,
OPT_BOOLEAN ( ' n ' , " no-prefault " , & no_prefault ,
" Show only the result without page faults before memcpy() " ) ,
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 ) ;
2009-11-17 18:20:09 +03:00
struct routine {
const char * name ;
const char * desc ;
2010-11-25 10:04:52 +03:00
memcpy_t fn ;
2009-11-17 18:20:09 +03:00
} ;
struct routine routines [ ] = {
{ " default " ,
" Default memcpy() provided by glibc " ,
memcpy } ,
2010-11-25 10:04:52 +03:00
# ifdef ARCH_X86_64
# define MEMCPY_FN(fn, name, desc) { name, desc, fn },
# include "mem-memcpy-x86-64-asm-def.h"
# undef MEMCPY_FN
# endif
2009-11-17 18:20:09 +03:00
{ NULL ,
NULL ,
NULL }
} ;
static const char * const bench_mem_memcpy_usage [ ] = {
" perf bench mem memcpy <options> " ,
NULL
} ;
static struct perf_event_attr clock_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
} ;
static void init_clock ( void )
{
clock_fd = sys_perf_event_open ( & clock_attr , getpid ( ) , - 1 , - 1 , 0 ) ;
2009-11-20 06:37:17 +03:00
if ( clock_fd < 0 & & errno = = ENOSYS )
die ( " No CONFIG_PERF_EVENTS=y kernel support configured? \n " ) ;
else
BUG_ON ( clock_fd < 0 ) ;
2009-11-17 18:20:09 +03:00
}
static u64 get_clock ( void )
{
int ret ;
u64 clk ;
ret = read ( clock_fd , & clk , sizeof ( u64 ) ) ;
BUG_ON ( ret ! = sizeof ( u64 ) ) ;
return clk ;
}
static double timeval2double ( struct timeval * ts )
{
return ( double ) ts - > tv_sec +
( double ) ts - > tv_usec / ( double ) 1000000 ;
}
2010-11-25 10:04:52 +03:00
static void 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 " ) ;
}
static u64 do_memcpy_clock ( memcpy_t fn , size_t len , bool prefault )
{
u64 clock_start = 0ULL , clock_end = 0ULL ;
void * src = NULL , * dst = NULL ;
2012-01-18 17:29:59 +04:00
int i ;
2010-11-25 10:04:52 +03:00
alloc_mem ( & src , & dst , len ) ;
if ( prefault )
fn ( dst , src , len ) ;
clock_start = get_clock ( ) ;
2012-01-18 17:29:59 +04:00
for ( i = 0 ; i < iterations ; + + i )
fn ( dst , src , len ) ;
2010-11-25 10:04:52 +03:00
clock_end = get_clock ( ) ;
free ( src ) ;
free ( dst ) ;
return clock_end - clock_start ;
}
static double do_memcpy_gettimeofday ( memcpy_t fn , size_t len , bool prefault )
{
struct timeval tv_start , tv_end , tv_diff ;
void * src = NULL , * dst = NULL ;
2012-01-18 17:29:59 +04:00
int i ;
2010-11-25 10:04:52 +03:00
alloc_mem ( & src , & dst , len ) ;
if ( prefault )
fn ( dst , src , len ) ;
BUG_ON ( gettimeofday ( & tv_start , NULL ) ) ;
2012-01-18 17:29:59 +04:00
for ( i = 0 ; i < iterations ; + + i )
fn ( dst , src , len ) ;
2010-11-25 10:04:52 +03:00
BUG_ON ( gettimeofday ( & tv_end , NULL ) ) ;
timersub ( & tv_end , & tv_start , & tv_diff ) ;
free ( src ) ;
free ( dst ) ;
return ( double ) ( ( double ) len / timeval2double ( & tv_diff ) ) ;
}
# define pf (no_prefault ? 0 : 1)
# define print_bps(x) do { \
if ( x < K ) \
printf ( " %14lf B/Sec " , x ) ; \
else if ( x < K * K ) \
printf ( " %14lfd KB/Sec " , x / K ) ; \
else if ( x < K * K * K ) \
printf ( " %14lf MB/Sec " , x / K / K ) ; \
else \
printf ( " %14lf GB/Sec " , x / K / K / K ) ; \
} while ( 0 )
2009-11-17 18:20:09 +03:00
int bench_mem_memcpy ( int argc , const char * * argv ,
const char * prefix __used )
{
int i ;
2010-11-25 10:04:52 +03:00
size_t len ;
double result_bps [ 2 ] ;
u64 result_clock [ 2 ] ;
2009-11-17 18:20:09 +03:00
argc = parse_options ( argc , argv , options ,
bench_mem_memcpy_usage , 0 ) ;
2010-11-25 10:04:52 +03:00
if ( use_clock )
init_clock ( ) ;
len = ( size_t ) perf_atoll ( ( char * ) length_str ) ;
2009-11-20 06:37:17 +03:00
2010-11-25 10:04:52 +03:00
result_clock [ 0 ] = result_clock [ 1 ] = 0ULL ;
result_bps [ 0 ] = result_bps [ 1 ] = 0.0 ;
if ( ( s64 ) len < = 0 ) {
2009-11-17 18:20:09 +03:00
fprintf ( stderr , " Invalid length:%s \n " , length_str ) ;
return 1 ;
}
2010-11-25 10:04:52 +03:00
/* same to without specifying either of prefault and no-prefault */
if ( only_prefault & & no_prefault )
only_prefault = no_prefault = false ;
2009-11-17 18:20:09 +03:00
for ( i = 0 ; routines [ i ] . name ; i + + ) {
if ( ! strcmp ( routines [ i ] . name , routine ) )
break ;
}
if ( ! routines [ i ] . name ) {
printf ( " Unknown routine:%s \n " , routine ) ;
printf ( " Available routines... \n " ) ;
for ( i = 0 ; routines [ i ] . name ; i + + ) {
printf ( " \t %s ... %s \n " ,
routines [ i ] . name , routines [ i ] . desc ) ;
}
return 1 ;
}
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
2010-11-25 10:04:52 +03:00
if ( ! only_prefault & & ! no_prefault ) {
/* show both of results */
if ( use_clock ) {
result_clock [ 0 ] =
do_memcpy_clock ( routines [ i ] . fn , len , false ) ;
result_clock [ 1 ] =
do_memcpy_clock ( routines [ i ] . fn , len , true ) ;
} else {
result_bps [ 0 ] =
do_memcpy_gettimeofday ( routines [ i ] . fn ,
len , false ) ;
result_bps [ 1 ] =
do_memcpy_gettimeofday ( routines [ i ] . fn ,
len , true ) ;
}
2009-11-17 18:20:09 +03:00
} else {
2010-11-25 10:04:52 +03:00
if ( use_clock ) {
result_clock [ pf ] =
do_memcpy_clock ( routines [ i ] . fn ,
len , only_prefault ) ;
} else {
result_bps [ pf ] =
do_memcpy_gettimeofday ( routines [ i ] . fn ,
len , only_prefault ) ;
}
2009-11-17 18:20:09 +03:00
}
switch ( bench_format ) {
case BENCH_FORMAT_DEFAULT :
2010-11-25 10:04:52 +03:00
if ( ! only_prefault & & ! no_prefault ) {
if ( use_clock ) {
printf ( " %14lf Clock/Byte \n " ,
( double ) result_clock [ 0 ]
/ ( double ) len ) ;
printf ( " %14lf Clock/Byte (with prefault) \n " ,
( double ) result_clock [ 1 ]
/ ( double ) len ) ;
} else {
print_bps ( result_bps [ 0 ] ) ;
printf ( " \n " ) ;
print_bps ( result_bps [ 1 ] ) ;
printf ( " (with prefault) \n " ) ;
2009-11-17 18:20:09 +03:00
}
2010-11-25 10:04:52 +03:00
} else {
if ( use_clock ) {
printf ( " %14lf Clock/Byte " ,
( double ) result_clock [ pf ]
/ ( double ) len ) ;
} else
print_bps ( result_bps [ pf ] ) ;
printf ( " %s \n " , only_prefault ? " (with prefault) " : " " ) ;
2009-11-17 18:20:09 +03:00
}
break ;
case BENCH_FORMAT_SIMPLE :
2010-11-25 10:04:52 +03:00
if ( ! only_prefault & & ! no_prefault ) {
if ( use_clock ) {
printf ( " %lf %lf \n " ,
( double ) result_clock [ 0 ] / ( double ) len ,
( double ) result_clock [ 1 ] / ( double ) len ) ;
} else {
printf ( " %lf %lf \n " ,
result_bps [ 0 ] , result_bps [ 1 ] ) ;
}
} else {
if ( use_clock ) {
printf ( " %lf \n " , ( double ) result_clock [ pf ]
/ ( double ) len ) ;
} else
printf ( " %lf \n " , result_bps [ pf ] ) ;
}
2009-11-17 18:20:09 +03:00
break ;
default :
2009-11-20 06:37:17 +03:00
/* reaching this means there's some disaster: */
die ( " unknown format: %d \n " , bench_format ) ;
2009-11-17 18:20:09 +03:00
break ;
}
return 0 ;
}