Re: Impact of CONFIG_PARAVIRT=y / mmap benchmark

From: Randy Dunlap
Date: Fri Mar 31 2017 - 13:02:20 EST


On 03/31/17 03:03, Radu Rendec wrote:
> On Wed, 2017-03-29 at 20:05 +0200, Ingo Molnar wrote:
>> * Randy Dunlap <rdunlap@xxxxxxxxxxxxx> wrote:
>>
>>> On 03/28/17 10:17, Radu Rendec wrote:
>>>> Does anyone still have a copy of Ingo Molnar's mmap-perf.c
>>>> program (the
>>>> old link is broken)? Would it still be relevant to use it for
>>>> measuring
>>>> performance in case of PARAVIRT?
>>>>
>>> I have mmap-perf.c that says:
>>> /* Copyright Ingo Molnar (c) 2006 */
>>>
>>> and no license info...
>>> Ingo?
>>
>> It's GPL v2, like the kernel.
>>
>
> Randy, now that Ingo has clarified the license of that code, can you
> please share it?
>
> Thank you both for the feedback!

Sure, it's attached.
Sorry for the delay.


--
~Randy
/* Copyright Ingo Molnar (c) 2006 */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/times.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <linux/unistd.h>
#include <unistd.h>
#include <sys/mman.h>

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/mman.h>

#define DEBUG 0

#if DEBUG
# define dprintf(x...) printf(x)
#else
# define dprintf(x...)
#endif

#define PAGE_SIZE 4096UL

#define MIN_WINDOW_SIZE (800 * 1024UL * 1024UL)

static unsigned long window_size;
static void *window;

enum {
OP_MMAP,
OP_MUNMAP,
OP_MREMAP,
NR_OPS
};

static void print_maps(void)
{
char buf[8192];

int fd, n;
if ((fd = open("/proc/self/maps", O_RDONLY)) < 0) {
perror("open");
exit(1);
}
if ((n = read(fd, &buf[0], sizeof(buf))) < 0) {
perror("read");
exit(2);
}
close(fd);
if (n > 0)
write(1, &buf[0], n);
}

unsigned long get_rand_size_idx(unsigned long start_idx)
{
unsigned long rnd1 = random(), rnd2 = random();
unsigned long max_idx, size_idx;
unsigned long factor, range;

max_idx = window_size / PAGE_SIZE;
/*
* limit the size of remapping to 1-256 pages, but still leave
* a (small) chance for larger remaps:
*/
size_idx = rnd1 & 0xf;
if (!size_idx) {
size_idx = rnd1 & 0xff;
if (!size_idx) {
size_idx = rnd1 & 0xfff;
if (!size_idx) {
factor = sqrt(rnd2 % max_idx) * 100 /
sqrt(max_idx) + 1;
range = (max_idx - start_idx) / factor + 1;
size_idx = rnd2 % range + 1;
} else
size_idx /= 32;
} else
size_idx /= 16;
} else
size_idx /= 2;
if (!size_idx)
size_idx = 1;

if (size_idx > max_idx - start_idx)
size_idx = max_idx - start_idx;

return size_idx;
}

static void
do_munmap_op(unsigned long start_idx, unsigned long size_idx)
{
void *start_addr = window + start_idx * PAGE_SIZE;
unsigned long size = size_idx * PAGE_SIZE;
int ret;

ret = munmap(start_addr, size);
assert(ret == 0);
dprintf("munmap: %p [%ld]\n", start_addr, size);
}

static void
do_mmap_op(unsigned long start_idx, unsigned long size_idx, unsigned long rnd)
{
void *start_addr = window + start_idx * PAGE_SIZE;
unsigned long size = size_idx * PAGE_SIZE;
int prot_rnd = (rnd/256) % 3, prot = 0, map_fixed = 0,
touch_it_read, touch_it_write;
void *addr;

if (!prot_rnd)
prot = PROT_NONE;
/*
* Both read and write mapped has a 50% chance to be set:
*/
else {
if (prot_rnd & 1)
prot |= PROT_READ;
if (prot_rnd & 2)
prot |= PROT_WRITE;
}
/*
* map it fixed, with a 50% chance:
*/
if (rnd & 8192)
map_fixed = MAP_FIXED;
/*
* Touch the mapping with a 25% chance:
*/
touch_it_read = 0;
if (((rnd >> 10) & 3) == 3)
touch_it_read = 1;

touch_it_write = 0;
if (((rnd >> 6) & 3) == 3)
touch_it_write = 1;

addr = mmap(start_addr, size, prot,
MAP_ANONYMOUS|MAP_PRIVATE|map_fixed, -1, 0);
dprintf("mmap: %d, %d, %p - %p => %p\n",
prot, map_fixed ? 1 : 0, start_addr, start_addr+size, addr);
assert(!map_fixed || (addr == start_addr));
if (addr != (void *)-1) {
volatile char data = 0xf;

if ((prot & PROT_READ) && touch_it_read)
data = *(char *)addr;
if ((prot & PROT_WRITE) && touch_it_write)
*(char *)addr = data;
}
/*
* If it's a non-fixed mmap then unmap it, if it's outside
* of the window (otherwise we'd slowly leak memory):
*/
if (addr < window || addr >= window + window_size) {
int ret = munmap(addr, size);
assert(ret == 0);
dprintf("mmap-munmap: %p [%ld]\n", addr, size);
}
}

static void
do_mremap_op(unsigned long start_idx, unsigned long size_idx, unsigned long rnd)
{
unsigned long old_size = size_idx * PAGE_SIZE, new_size, may_move;
void *start_addr = window + start_idx * PAGE_SIZE;
void *addr;

/*
* Resize or just plain move?
*/
may_move = 0;
if (rnd & 1024) {
new_size = get_rand_size_idx(start_idx);
if (rnd & 2048)
may_move = 1;
} else
new_size = old_size;

addr = (void *)mremap(start_addr, old_size, new_size, may_move);
dprintf("mremap: [%ld, %ld] => %p.\n", new_size, may_move, addr);
}


static void do_random_mem_op(void)
{
unsigned long rnd1 = random(), rnd2 = random();
unsigned long max_idx, start_idx, end_idx, size_idx;
int op;

max_idx = window_size / PAGE_SIZE;
start_idx = rnd1 % (max_idx-1);
size_idx = get_rand_size_idx(start_idx);

end_idx = start_idx + size_idx;

op = rnd2 % NR_OPS;

dprintf("op: %d, %6lu - %6lu [%lu, %.1f%%] [%p - %p]\n",
op, start_idx, end_idx, size_idx,
100.0*(double)size_idx/(double)max_idx,
window + start_idx*PAGE_SIZE, window + end_idx*PAGE_SIZE);

assert(end_idx <= max_idx);

switch (op) {
case OP_MMAP:
do_mmap_op(start_idx, size_idx, rnd2);
break;

case OP_MUNMAP:
do_munmap_op(start_idx, size_idx);
break;

case OP_MREMAP:
do_mremap_op(start_idx, size_idx, rnd2);
break;

default:
assert(0);
}
}

static void *threadfn(void *arg)
{
int thread = (int)(long)arg;
int i;

printf("thread %d started.\n", thread); fflush(stdout);

for (i = 0; i < 1000000; i++) {
do_random_mem_op();
}
// print_maps();

return NULL;
}

/*
* We may get segfaults, if one thread unmaps an area that we
* are touching. We ignore these segfaults:
*/
static void segfault_handler(int sig, siginfo_t *info, void *uc)
{
}

int main (int argc, char **argv)
{
unsigned int nr_threads = 0, i, ret;
struct sigaction act;
pthread_t *t;
char random_state[32];

initstate(0, random_state, sizeof(random_state));

sigfillset(&act.sa_mask);
act.sa_sigaction = segfault_handler;
sigaction(SIGSEGV, &act, NULL);
sigaction(SIGBUS, &act, NULL);

if (argc != 1 && argc != 2 && argc != 3) {
usage:
printf("usage: loop_print2 [<nr threads>] [<test window size [MB]>]\n");
exit(-1);
}
/*
* The default is to use # of CPUs threads:
*/
if (argc == 1) {
nr_threads = system("exit `grep -w processor /proc/cpuinfo | wc -l`");
nr_threads = WEXITSTATUS(nr_threads);
}
if (argc >= 2) {
nr_threads = atol(argv[1]);
if (!nr_threads)
goto usage;
}
if (argc >= 3)
window_size = atol(argv[2]) * 1024UL * 1024UL;
if (window_size < MIN_WINDOW_SIZE)
window_size = MIN_WINDOW_SIZE;

/*
* Create the mmap() playground:
*/
window = mmap(0, window_size, PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if ((long)window == -1) {
printf("test window mmap failed!\n");
return -1;
}

/*
* Create and start all threads:
*/
t = calloc(nr_threads, sizeof(*t));

for (i = 1; i < nr_threads; i++) {
ret = pthread_create (t+i, NULL, threadfn, (void *)(long)i);
if (ret)
exit(-1);
}
threadfn((void *)0);
for (i = 1; i < nr_threads; i++) {
ret = pthread_join (t[i], NULL);
if (ret)
exit(-1);
}

return 0;
}