...
 
Commits (2)
  • Kirill Smelkov's avatar
    virtmem: Benchmarks for pagefault handling · f3c8b71f
    Kirill Smelkov authored
    Benchmark the time it takes for virtmem to handle pagefault with noop
    loadblk for loadblk both implemented in C and in Python.
    
    On my computer it is:
    
    	name          µs/op
    	PagefaultC    269 ± 0%
    	pagefault_py  291 ± 0%
    
    Quite a big time in other words, and it will be investigated.
    f3c8b71f
  • Kirill Smelkov's avatar
    X on tracking pagefault latency · 46c9ff4e
    Kirill Smelkov authored
    e.g. it is
    
    	.. on_pagefault_start   0.954 µs
    	.. vma_on_pagefault_pre 0.954 µs
    	.. ramh_alloc_page_pre  0.954 µs
    	.. ramh_alloc_page      169.992 µs
    	.. vma_on_pagefault     172.853 µs
    	.. vma_on_pagefault_pre 172.853 µs
    	.. vma_on_pagefault     174.046 µs
    	.. on_pagefault_end     174.046 µs
    	.. whole:               171.900 µs
    
    so almost all in ramh_alloc_page.
    46c9ff4e
......@@ -192,5 +192,10 @@ test.fault : $(FAULTS:%=%.tfault)
# -*- benchmarking -*-
bench : bigfile/_bigfile.so
BENCHV.C:= $(patsubst %.c,%,$(wildcard bigfile/tests/bench_*.c))
bench : bench.t bench.py
bench.t : $(BENCHV.C:%=%.trun)
bench.py: bigfile/_bigfile.so
$(PYBENCH) $(PYTEST_IGNORE)
......@@ -37,6 +37,7 @@
#include <errno.h>
#include <stdint.h>
#include "../t/t_utils.h"
/* "before us" previously installed SIGSEGV sigaction */
static struct sigaction prev_segv_act;
......@@ -45,6 +46,10 @@ static int segv_act_installed;
static int faulted_by(const struct ucontext *uc);
volatile double t_on_pagefault_start, t_on_pagefault_end;
volatile double t_vma_on_pagefault_pre[10], t_vma_on_pagefault[10];
volatile int n_vma_on_pagefault;
/* SIGSEGV handler */
static void on_pagefault(int sig, siginfo_t *si, void *_uc)
{
......@@ -55,6 +60,9 @@ static void on_pagefault(int sig, siginfo_t *si, void *_uc)
BUG_ON(sig != SIGSEGV);
BUG_ON(si->si_signo != SIGSEGV);
//t_on_pagefault_start = microtime();
trace("on_pagefault_start");
/* determine what client wants - read or write */
write = faulted_by(uc);
......@@ -93,6 +101,8 @@ static void on_pagefault(int sig, siginfo_t *si, void *_uc)
BUG_ON(in_on_pagefault);
++in_on_pagefault;
n_vma_on_pagefault = 0;
/* vma_on_pagefault() can tell us to retry handling the fault, e.g. after a
* page has been loaded. Loop until pagefault is handled */
while (1) {
......@@ -106,10 +116,17 @@ static void on_pagefault(int sig, siginfo_t *si, void *_uc)
goto dont_handle; /* fault outside registered file slices */
}
//t_vma_on_pagefault_pre[n_vma_on_pagefault] = microtime();
trace("vma_on_pagefault_pre");
/* now, since we found faulting address in registered memory areas, we know
* we should serve this pagefault. */
vmres = vma_on_pagefault(vma, (uintptr_t)si->si_addr, write);
//t_vma_on_pagefault[n_vma_on_pagefault] = microtime();
trace("vma_on_pagefault");
n_vma_on_pagefault++;
/* see if pagefault handled or should be retried */
if (vmres == VM_HANDLED)
break;
......@@ -123,6 +140,9 @@ static void on_pagefault(int sig, siginfo_t *si, void *_uc)
virt_unlock();
errno = save_errno;
//t_on_pagefault_end = microtime();
trace("on_pagefault_end");
return;
......
......@@ -29,6 +29,7 @@
#include <wendelin/utils.h>
#include <wendelin/bug.h>
#include "../t/t_utils.h"
/* ramh_ops */
......@@ -37,6 +38,8 @@ Page *ramh_alloc_page(RAMH *ramh, pgoff_t pgoffset_hint)
struct Page *page;
pgoff_t ramh_pgoffset;
trace("ramh_alloc_page_pre");
// XXX ok to malloc, or better do more structured allocations?
page = zalloc(sizeof(*page));
if (!page)
......@@ -56,6 +59,7 @@ Page *ramh_alloc_page(RAMH *ramh, pgoff_t pgoffset_hint)
INIT_LIST_HEAD(&page->in_dirty); /* initially not in dirty list */
page->refcnt = 0;
trace("ramh_alloc_page");
return page;
}
......
......@@ -29,6 +29,7 @@ from io import FileIO
from wendelin.bigfile.file_file import BigFile_File
from wendelin.bigfile import WRITEOUT_STORE, WRITEOUT_MARKSTORED
from wendelin.lib.testing import Adler32, nulladler32_bysize, ffadler32_bysize
from wendelin.bigarray.tests.test_basic import BigFile_Zero
from wendelin.lib.mem import bzero, memset
from tempfile import NamedTemporaryFile
......@@ -69,6 +70,47 @@ def teardown_module():
unlink(tmpf.name)
# BigFile that reads as zeros and tracks last loadblk request
class BigFile_ZeroTrack(BigFile_Zero):
def loadblk(self, blk, buf):
#print('zload #%d' % blk)
self.last_load = blk
super(BigFile_ZeroTrack, self).loadblk(blk, buf)
# benchmark the time it takes for virtmem to handle pagefault with noop loadblk
# implemented in Python.
def bench_pagefault_py(b):
npage = b.N
PS = blksize # XXX assumes blksize = pagesize
f = BigFile_ZeroTrack(PS)
fh = f.fileh_open()
vma = fh.mmap(0, npage)
m = memoryview(vma)
b.reset_timer()
for p in xrange(npage):
m[p*PS]
assert f.last_load == p
del m
del vma # vma.close()
del fh # fh.close()
del f # f.close()
def bench_iter(b):
for _ in xrange(b.N):
pass
def f():
pass
def bench_call(b):
for _ in xrange(b.N):
f()
# compute hash via mmaping the file at OS-level
def _bench_file_mmapread(hasher, expect):
fd = os.open(tmpf.name, O_RDONLY)
......
/* Wendelin.bigfile | virtual memory benchmarks
* Copyright (C) 2017 Nexedi SA and Contributors.
* Kirill Smelkov <kirr@nexedi.com>
*
* This program is free software: you can Use, Study, Modify and Redistribute
* it under the terms of the GNU General Public License version 3, or (at your
* option) any later version, as published by the Free Software Foundation.
*
* You can also Link and Combine this program with other software covered by
* the terms of any of the Free Software licenses or any of the Open Source
* Initiative approved licenses and Convey the resulting work. Corresponding
* source of such a combination shall include the source code for all other
* software used.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See COPYING file for full licensing terms.
* See https://www.nexedi.com/licensing for rationale and options.
*/
// XXX better link with it
#include "../virtmem.c"
#include "../pagemap.c"
#include "../ram.c"
#include "../ram_shmfs.c"
#include "../pagefault.c"
#include <ccan/tap/tap.h>
#include "../../t/t_utils.h"
#include "../../t/t_utils.c"
/* file that reads as zeros and tracks last loadblk request */
struct BigFile_ZeroTrack {
BigFile;
blk_t last_load;
double t_last_load;
};
typedef struct BigFile_ZeroTrack BigFile_ZeroTrack;
int zero_loadblk(BigFile *file0, blk_t blk, void *buf)
{
BigFile_ZeroTrack *file = upcast(BigFile_ZeroTrack *, file0);
//diag("zload #%ld", blk);
// Nothing to do here - the memory buf obtained from OS comes pre-cleared
// XXX reenable once/if memory comes uninitialized here
file->last_load = blk;
file->t_last_load = microtime();
return 0;
}
static const struct bigfile_ops filez_ops = {
.loadblk = zero_loadblk,
.storeblk = NULL, // XXX
.release = NULL, // XXX
};
extern volatile double t_on_pagefault_start, t_on_pagefault_end;
extern volatile double t_vma_on_pagefault_pre[10], t_vma_on_pagefault[10];
extern volatile int n_vma_on_pagefault;
/* benchmark the time it takes for virtmem to handle pagefault with noop loadblk */
void bench_pagefault() {
RAM *ram;
//BigFileH fh_struct, *fh = &fh_struct;
//VMA vma_struct, *vma = &vma_struct;
BigFileH *fh = malloc(sizeof(*fh));
VMA *vma = malloc(sizeof(*vma));
//pgoff_t p, npage = 10000;
pgoff_t p, npage = 1500;
size_t PS;
int err;
int i;
double Tstart, Tend;
ok1(!pagefault_init());
ram = ram_new(NULL,NULL);
ok1(ram);
PS = ram->pagesize;
/* setup zero file */
BigFile_ZeroTrack f = {
.blksize = ram->pagesize, /* artificially blksize = pagesize */
.file_ops = &filez_ops,
};
/* setup f mapping */
err = fileh_open(fh, &f, ram);
ok1(!err);
err = fileh_mmap(vma, fh, 0, npage);
ok1(!err);
Tstart = microtime();
// access first byte of every page
for (p = 0; p < npage; p++) {
trace_reset();
double t = microtime();
#define Dus(tt) (((tt) - t) * 1E6)
b(vma, p * PS);
if (f.last_load != p)
fail("accessed page #%ld but last loadblk was for block #%ld", p, f.last_load);
printf("\n");
#if 0
printf(".. onpagefault_start:\t%.3lf µs\n", Dus(t_on_pagefault_start));
printf(".. #vma_on_pagefault:\t%d\n", n_vma_on_pagefault);
for (i = 0; i < n_vma_on_pagefault; i++)
printf(".. vma_on_pagefault[%d]:\t%.3lf (<- %.3lf)\n", i,
Dus(t_vma_on_pagefault[i]), Dus(t_vma_on_pagefault_pre[i]));
printf(".. onpagefault_end:\t%.3lf µs\n", Dus(t_on_pagefault_end));
#endif
for (i = 0; i < tracev_len; i++)
printf(".. %s\t%.3lf µs\n", tracev[i].name, Dus(tracev[i].t));
printf(".. whole:\t\t%.3lf µs\n", Dus(f.t_last_load));
}
Tend = microtime();
printf("BenchmarkPagefaultC\t%ld\t%.3lf µs/op\n", npage, (Tend - Tstart) * 1E6 / npage);
// vma_unmap(vma);
// fileh_close(fh);
// ram_close(ram);
}
int main()
{
int i, nrun=1;
tap_fail_callback = abort; // XXX to catch failure immediately
for (i=0; i<nrun; i++)
bench_pagefault();
return 0;
}
......@@ -37,6 +37,8 @@
#include <stdio.h>
#include <unistd.h>
#include "../t/t_utils.h"
static size_t page_size(const Page *page);
static void page_drop_memory(Page *page);
static void *vma_page_addr(VMA *vma, Page *page);
......@@ -787,6 +789,7 @@ static int __ram_reclaim(RAM *ram)
int batch = RECLAIM_BATCH, scanned = 0;
TRACE("RAM_RECLAIM\n");
trace("ram_reclaim");
hlru = lru_list->next;
while (batch && hlru != lru_list) {
......
......@@ -21,6 +21,8 @@
#include <wendelin/utils.h>
#include <sys/time.h>
static const struct ram_ops ram_limited_ops;
static const struct ramh_ops ramh_limited_ops;
......@@ -155,3 +157,33 @@ static const struct ramh_ops ramh_limited_ops = {
.mmap_page = ramh_limited_mmap_page,
.close = ramh_limited_close,
};
double microtime() {
int err;
struct timeval tv;
err = gettimeofday(&tv, NULL);
if (err == -1) {
perror("gettimeofday");
abort();
}
return tv.tv_sec + 1E-6 * tv.tv_usec;
}
struct trace_event tracev[100];
int tracev_len;
void trace_reset() {
tracev_len = 0;
}
void trace(const char *event_name) {
BUG_ON(tracev_len >= ARRAY_SIZE(tracev));
tracev[tracev_len].name = event_name;
tracev[tracev_len].t = microtime();
tracev_len++;
}
......@@ -43,4 +43,18 @@ typedef struct RAMLimited RAMLimited;
RAMLimited *ram_limited_new(RAM *backend, size_t alloc_max);
/* current time as float */
double microtime();
// trace events
struct trace_event {
const char *name;
double t;
};
extern struct trace_event tracev[100];
extern int tracev_len;
void trace_reset();
void trace(const char *event_name);
#endif