Created
November 1, 2012 18:58
-
-
Save jimblandy/3995727 to your computer and use it in GitHub Desktop.
Microbenchmark for bump allocators with guard pages or limit pointer checks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE // for gregset_t indices | |
#include <errno.h> | |
#include <signal.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/mman.h> | |
#include <unistd.h> | |
#include <setjmp.h> | |
#if defined(LIMIT_PTR) == defined(GUARD_PAGE) | |
#error Define one of LIMIT_PTR or GUARD_PAGE. | |
#endif | |
void | |
syscall_failed(char *when) | |
{ | |
fprintf(stderr, "%s: error: %s\n", when, strerror(errno)); | |
exit(40); | |
} | |
const char *progname; | |
void usage() | |
{ | |
fprintf(stderr, "usage: %s NUM_BLOCKS PAGES_PER_BLOCK BYTES_PER_OBJECT\n", progname); | |
exit(1); | |
} | |
// One allocated block of memory. | |
struct region { | |
char *base; // start of the memory we've allocated so far | |
char *end; // end of allocated memory | |
char *next; // next available byte | |
}; | |
// Try to force the compiler to fetch the limit address from a structure, | |
// to make the cost of the check a bit more realistic. | |
static volatile struct region r; | |
#ifdef GUARD_PAGE | |
sigjmp_buf guard_resume; | |
// Handler for SIGSEGVs caused by touching the guard page. | |
void | |
handler(int signum) | |
{ | |
siglongjmp(guard_resume, 1); | |
} | |
#endif | |
int main(int argc, char **argv) | |
{ | |
progname = argv[0]; | |
if (argc != 4) | |
usage(); | |
volatile int blocks_left = atoi(argv[1]); | |
if (blocks_left <= 0) | |
usage(); | |
int pages_per_block = atoi(argv[2]); | |
if (pages_per_block <= 0) | |
usage(); | |
int object_size = atoi(argv[3]); | |
if (object_size <= 0) | |
usage(); | |
size_t bytes_per_page = sysconf(_SC_PAGESIZE); | |
size_t bytes_per_block = pages_per_block * bytes_per_page; | |
size_t bytes_per_guard_page; | |
#ifdef GUARD_PAGE | |
bytes_per_guard_page = bytes_per_page; | |
#else | |
bytes_per_guard_page = 0; | |
#endif | |
/* Allocate the block. */ | |
r.base = mmap(NULL, bytes_per_block + bytes_per_guard_page, | |
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); | |
if (r.base == MAP_FAILED) syscall_failed("mmap a block"); | |
r.end = r.base + bytes_per_block; | |
#ifdef GUARD_PAGE | |
// Re-protect the last page to be a guard page. | |
if (mprotect(r.end, bytes_per_guard_page, PROT_NONE) < 0) | |
syscall_failed("mprotecting guard page"); | |
// Install the SIGSEGV handler. | |
struct sigaction sa; | |
sa.sa_handler = handler; | |
sigemptyset(&sa.sa_mask); | |
sa.sa_flags = 0; | |
if (sigaction(SIGSEGV, &sa, NULL) < 0) | |
syscall_failed("setting SIGSEGV handler"); | |
#endif | |
// Each iteration of this loop "uses" the entire block once. | |
for (;;) { | |
#ifdef GUARD_PAGE | |
sigsetjmp(guard_resume, 1); | |
#endif | |
r.next = r.base; | |
if (blocks_left-- <= 0) | |
exit(0); | |
// Each iteration of this loop "allocates" one "object" in the block. | |
while ( | |
#ifdef LIMIT_PTR | |
r.next < r.end | |
#endif | |
#ifdef GUARD_PAGE | |
1 | |
#endif | |
) { | |
*r.next = 'x'; | |
r.next += object_size; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
define _GNU_SOURCE // for gregset_t indices
include <errno.h>
include <signal.h>
include <stdio.h>
include <stdlib.h>
include <string.h>
include <sys/mman.h>
include <unistd.h>
include <setjmp.h>
if defined(LIMIT_PTR) == defined(GUARD_PAGE)
error Define one of LIMIT_PTR or GUARD_PAGE.
endif
void
syscall_failed(char *when)
{
fprintf(stderr, "%s: error: %s\n", when, strerror(errno));
exit(40);
}
const char *progname;
void usage()
{
fprintf(stderr, "usage: %s NUM_BLOCKS PAGES_PER_BLOCK BYTES_PER_OBJECT\n", progname);
exit(1);
}
// One allocated block of memory.
struct region {
char *base; // start of the memory we've allocated so far
char *end; // end of allocated memory
char *next; // next available byte
};
// Try to force the compiler to fetch the limit address from a structure,
// to make the cost of the check a bit more realistic.
static volatile struct region r;
ifdef GUARD_PAGE
sigjmp_buf guard_resume;
// Handler for SIGSEGVs caused by touching the guard page.
void
handler(int signum)
{
siglongjmp(guard_resume, 1);
}
endif
int main(int argc, char **argv)
{
progname = argv[0];
if (argc != 4)
usage();
volatile int blocks_left = atoi(argv[1]);
if (blocks_left <= 0)
usage();
int pages_per_block = atoi(argv[2]);
if (pages_per_block <= 0)
usage();
int object_size = atoi(argv[3]);
if (object_size <= 0)
usage();
size_t bytes_per_page = sysconf(_SC_PAGESIZE);
size_t bytes_per_block = pages_per_block * bytes_per_page;
size_t bytes_per_guard_page;
ifdef GUARD_PAGE
bytes_per_guard_page = bytes_per_page;
else
bytes_per_guard_page = 0;
endif
/* Allocate the block. */
r.base = mmap(NULL, bytes_per_block + bytes_per_guard_page,
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (r.base == MAP_FAILED) syscall_failed("mmap a block");
r.end = r.base + bytes_per_block;
ifdef GUARD_PAGE
// Re-protect the last page to be a guard page.
if (mprotect(r.end, bytes_per_guard_page, PROT_NONE) < 0)
syscall_failed("mprotecting guard page");
// Install the SIGSEGV handler.
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGSEGV, &sa, NULL) < 0)
syscall_failed("setting SIGSEGV handler");
endif
// Each iteration of this loop "uses" the entire block once.
for (;;) {
ifdef GUARD_PAGE
endif
ifdef LIMIT_PTR
endif
ifdef GUARD_PAGE
endif
}
}