diff --git a/.gitignore b/.gitignore deleted file mode 100644 index f6c9362e..00000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.o -uffd_sort -uffd_test diff --git a/CMakeLists.txt b/CMakeLists.txt index 2de79216..9c76cc80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,20 @@ -cmake_minimum_required (VERSION 3.5.2) -project(umap VERSION 0.0.1) +cmake_minimum_required (VERSION 3.5.1) +project(umap VERSION 0.0.2) + +configure_file( + "${PROJECT_SOURCE_DIR}/config/config.h.in" + "${PROJECT_BINARY_DIR}/config/config.h" + ) if (NOT CMAKE_BUILD_TYPE) message(STATUS "No build type selected, default to Debug") set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type (default Debug)" FORCE) endif() +OPTION (BUILD_FITS "Build FITS-based Tests (requires qfits library)" FALSE) + set (FLAGS_ALL "-Wall") -set (FLAGS_DEBUG_ALL "-g -O0") +set (FLAGS_DEBUG_ALL "-g -O0 -DDEBUG") set (FLAGS_RELEASE_ALL "-O3") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 ${FLAGS_ALL}") diff --git a/README.md b/README.md index 0488c193..ff16d1f9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# UMAP v0.0.1 (alpha) +# UMAP v0.0.2 (alpha) Umap is a library that provides an mmap()-like interface to a simple, user- space page fault handler based on the userfaultfd Linux feature (starting with @@ -26,13 +26,22 @@ cmake -DCMAKE_INSTALL_PREFIX = .. make install ``` +The default for cmake is to build a Debug version of the software. If you would like to build an optimized (-O3) +version, simply run +```bash +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX = .. +``` + ## umap() Interface -The interface is currently a work in progress (see [umap.h](src/umap.h)). +The interface is currently a work in progress (see [umap.h](include/umap.h)). + +## License -## Contact/Legal +- The license is [LGPL](/LICENSE). +- [thirdparty_licenses.md](/thirdparty_licenses.md) -The license is [LGPL](/LICENSE). +## Contact Primary contact/Lead developer diff --git a/config/config.h.in b/config/config.h.in new file mode 100644 index 00000000..415b12ec --- /dev/null +++ b/config/config.h.in @@ -0,0 +1,7 @@ +// the (cmake) configured options and settings for umap +#ifndef _UMAP_UMAPCONFIG_H +#define _UMAP_UMAPCONFIG_H +#define UMAP_VERSION_MAJOR @umap_VERSION_MAJOR@ +#define UMAP_VERSION_MINOR @umap_VERSION_MINOR@ +#define UMAP_VERSION_PATCH @umap_VERSION_PATCH@ +#endif diff --git a/include/umap.h b/include/umap.h new file mode 100644 index 00000000..7253bbca --- /dev/null +++ b/include/umap.h @@ -0,0 +1,64 @@ +/* + * This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at + * https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) + * version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms + * and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the + * GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _UMAP_H_ +#define _UMAP_H_ +#include +#include + +typedef struct umap_backing_file { + int fd; + off_t data_size; + off_t data_offset; /* Offset of data portion in file */ +} umap_backing_file; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * umap() is a wrapper around mmap(2) and userfaultfd(2) to allow for creating a mapping of pages managed in user-space. + */ +void* umap( void* addr, /* See mmap(2) */ + size_t length, /* See mmap(2) */ + int prot, /* See mmap(2) */ + int flags, /* See below, see mmap(2) for general notes */ + int fd, /* See mmap(2) */ + off_t offset /* See mmap(2) */ + ); +int uunmap( void* addr, /* See mmap(2) */ + size_t length /* See mmap(2) */ + ); + +void* umap_mf(void* addr, + size_t length, + int prot, + int flags, + int num_backing_files, + umap_backing_file* backing_files + ); + +uint64_t umap_cfg_get_bufsize( void ); +void umap_cfg_set_bufsize( uint64_t page_bufsize ); +#ifdef __cplusplus +} +#endif + +/* + * flags + */ +#define UMAP_PRIVATE MAP_PRIVATE // Note - UMAP_SHARED not currently supported +#define UMAP_FIXED MAP_FIXED // See mmap(2) - This flag is currently then only flag supported. + +/* + * Return codes + */ +#define UMAP_FAILED (void *)-1 +#endif // _UMAP_H_ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b3cb0976..e634ee81 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,19 +1,24 @@ project(umap_libraries) -add_library(libumap SHARED umap.cpp) -add_library(libumap_static STATIC umap.cpp) +add_library(libumap SHARED umap.cpp umaplog.cpp) +add_library(libumap_static STATIC umap.cpp umaplog.cpp) set_target_properties(libumap_static PROPERTIES OUTPUT_NAME libumap) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") -include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) +include_directories( + BEFORE "${CMAKE_CURRENT_SOURCE_DIR}" + "${PROJECT_BINARY_DIR}/../config" + "${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_SOURCE_DIR}/../sysincludes" + ) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include) add_custom_command ( TARGET libumap POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/umap.h ${CMAKE_BINARY_DIR}/include/umap.h + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../include/umap.h ${CMAKE_BINARY_DIR}/include/umap.h ) install(TARGETS libumap libumap_static diff --git a/src/results/latencies-madv-handler-ap.png b/src/results/latencies-madv-handler-ap.png deleted file mode 100644 index 15e6179f..00000000 Binary files a/src/results/latencies-madv-handler-ap.png and /dev/null differ diff --git a/src/results/threads-timings-madv-in-app.txt b/src/results/threads-timings-madv-in-app.txt deleted file mode 100644 index 5ef2e3a4..00000000 --- a/src/results/threads-timings-madv-in-app.txt +++ /dev/null @@ -1,11 +0,0 @@ -1,2,4,8 -9531,11499,16505,32238 -10063,11700,16686,11879 -10500,11680,12143,32238 -10228,12033,16513,11877 -10254,11660,16682,32239 -10760,11501,12142,32238 -10642,11700,16513,32239 -10149,11680,16683,32238 -9916,12026,16513,32244 -10222,11661,16674,32244 diff --git a/src/results/threads-timings-madv-in-handler.txt b/src/results/threads-timings-madv-in-handler.txt deleted file mode 100644 index c37c34f7..00000000 --- a/src/results/threads-timings-madv-in-handler.txt +++ /dev/null @@ -1,11 +0,0 @@ -1 2 3 4 -9705 10388 22698 44446 -10109 10400 22489 10360 -9702 10345 10654 44446 -10526 10668 22699 10360 -10287 10767 22489 44446 -9907 10388 10654 44446 -10111 10400 22699 44446 -10507 10345 22489 44446 -10310 10668 22698 44446 -10419 10767 22490 44446 diff --git a/src/results/threads-timings.numbers b/src/results/threads-timings.numbers deleted file mode 100644 index c6231559..00000000 Binary files a/src/results/threads-timings.numbers and /dev/null differ diff --git a/src/umap.cpp b/src/umap.cpp index f4e8d5c8..1df28cfb 100644 --- a/src/umap.cpp +++ b/src/umap.cpp @@ -1,410 +1,882 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -// handler of userfaultfd +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include #include #include -#include -#include -#include "umap.h" - -// data structures related to page buffer -static volatile int stop_uffd_handler = 0; -static char* tmppagebuf; -static pagebuffer_t* pagebuffer; -static int startix=0; - -#ifdef ENABLE_FAULT_TRACE_BUFFER -static page_activity_trace_t* trace_buf; -static int trace_bufsize = 1000; -static int trace_idx = 0; -static int trace_seq = 1; -#endif // ENABLE_FAULT_TRACE_BUFFER - -// end data structures related to page buffer - -int uffd_init(void* region, long pagesize, long num_pages) -{ - stop_uffd_handler = 0; - // open the userfault fd - int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); - if (uffd < 0) { - perror("userfaultfd syscall not available in this kernel"); - exit(1); - } - // enable for api version and check features - struct uffdio_api uffdio_api; - uffdio_api.api = UFFD_API; - uffdio_api.features = 0; - if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) { - perror("ioctl/uffdio_api"); - exit(1); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // open/close +#include // sysconf() +#include // syscall() +#include // mmap() +#include // poll() +#include +#include +#include // sched_getcpu() +#include +#include +#include +#include "umap.h" // API to library +#include "umaplog.h" // umap_log() +#include "config.h" + +using namespace std; + +const int umap_Version_Major = UMAP_VERSION_MAJOR; +const int umap_Version_Minor = UMAP_VERSION_MINOR; +const int umap_Version_Patch = UMAP_VERSION_PATCH; + +static const int UMAP_UFFD_MAX_MESSAGES = 256; +static unsigned int uffd_threads; +const uint64_t UMAP_DEFAULT_PAGES_PER_UFFD_HANDLER = 1024; // Separate Page Buffer per Thread + +static const uint64_t UMAP_PAGES_PER_BLOCK = 1024; +static uint64_t umap_pages_per_uffd_handler = UMAP_DEFAULT_PAGES_PER_UFFD_HANDLER; + +static long page_size; + +class umap_page; +struct umap_PageBlock; +class umap_page_buffer; +class umap_stats; +class __umap; +class UserFaultHandler; + +// +// |------------------------- umap() provided Region ----------------------------| +// |------------------------- umap() provided backing file(s) -------------------| +// |- Page Block 1 -|- Page Block 2 -|- ... -|- Page Block N-1 -|- Page Block N -| +// +// _umap organizes a region of memory into a set of blocks of pages. The blocks +// of pages are then distributed evenly to a set of UserFaultHandler objects. +// +class _umap { + friend UserFaultHandler; + public: + _umap(void* _region, uint64_t _rsize, int num_backing_file, umap_backing_file* backing_files); + ~_umap(); + + static inline void* UMAP_PAGE_BEGIN(const void* a) { + return (void*)((uint64_t)a & ~(page_size-1)); } + vector ufault_handlers; + + private: + void* region; + uint64_t region_size; + int backingfile_fd; + vector bk_files; + bool uffd_time_to_stop_working; +}; + +class UserFaultHandler { + friend _umap; + public: + UserFaultHandler(_umap* _um, const vector& _pblks, uint64_t _pbuf_size); + ~UserFaultHandler(void); + void stop_uffd_worker( void ) noexcept { + _u->uffd_time_to_stop_working = true; + uffd_worker->join(); + }; + bool page_is_in_umap(const void* page_begin); + umap_page_buffer* get_pagebuffer() { return pagebuffer; } + + umap_stats* stat; + private: + _umap* _u; + vector PageBlocks; + uint64_t pbuf_size; + umap_page_buffer* pagebuffer; + vector umessages; + + int userfault_fd; + char* tmppagebuf; + thread* uffd_worker; + + void evict_page(umap_page* page); + void uffd_handler(void); + void pagefault_event(const struct uffd_msg& msg); + void enable_wp_on_pages_and_wake(uint64_t, int64_t); + void disable_wp_on_pages(uint64_t, int64_t, bool); +}; + +class umap_stats { + public: + umap_stats(): + stat_faults{0}, + dirty_evicts{0}, + clean_evicts{0}, + wp_messages{0}, + read_faults{0}, + write_faults{0}, + sigbus{0}, + stuck_wp{0}, + dropped_dups{0} + {}; + + void print_stats(void); + + uint64_t stat_faults; + uint64_t dirty_evicts; + uint64_t clean_evicts; + uint64_t wp_messages; + uint64_t read_faults; + uint64_t write_faults; + uint64_t sigbus; + uint64_t stuck_wp; + uint64_t dropped_dups; +}; + +struct umap_PageBlock { + void* base; + uint64_t length; +}; + +class umap_page_buffer { + /* + * TODO: Make the single page buffer threadsafe + */ + public: + umap_page_buffer(uint64_t pbuffersize); + ~umap_page_buffer(); + umap_page* alloc_page_desc(void* page); + void dealloc_page_desc(umap_page* page_desc); + + void add_page_desc_to_inmem(umap_page* page_desc); + umap_page* get_page_desc_to_evict(); + umap_page* find_inmem_page_desc(void* page_addr); // Finds page_desc for page_addr in inmem_page_descriptors + + private: + uint64_t page_buffer_size; + deque free_page_descriptors; + deque inmem_page_descriptors; + unordered_map inmem_page_map; +}; + +class umap_page { + public: + umap_page(): page{nullptr}, dirty{false} {} + bool page_is_dirty() { return dirty; } + void mark_page_dirty() { dirty = true; } + void mark_page_clean() { dirty = false; } + void* get_page(void) { return page; } + + void set_page(void* _p); + private: + void* page; + bool dirty; +}; + +static unordered_map active_umaps; - if (uffdio_api.api != UFFD_API) { - fprintf(stderr, "unsupported userfaultfd api\n"); - exit(1); - } - fprintf(stdout, "Feature bitmap %llx\n", uffdio_api.features); +// +// Library Interface Entry +// +static int check_uffd_compatibility( void ) +{ + int fd; - struct uffdio_register uffdio_register; - // register the pages in the region for missing callbacks - uffdio_register.range.start = (uint64_t)region; - uffdio_register.range.len = pagesize * num_pages; - uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING | - UFFDIO_REGISTER_MODE_WP; - fprintf(stdout, "uffdio region=%p - %p\n", - region, - (void*)(uffdio_register.range.start+uffdio_register.range.len)); - - if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) { - perror("ioctl/uffdio_register"); - exit(1); - } + if ((fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK)) < 0) { + perror("UFFD Compatibilty Check - Unable to open userfaultfd: "); + exit(1); + } - enable_wp_on_pages_and_wake(uffd, (uint64_t)region, pagesize, num_pages); + struct uffdio_api uffdio_api = { .api = UFFD_API, .features = UFFD_FEATURE_PAGEFAULT_FLAG_WP }; - if ((uffdio_register.ioctls & UFFD_API_RANGE_IOCTLS) != - UFFD_API_RANGE_IOCTLS) { - fprintf(stderr, "unexpected userfaultfd ioctl set\n"); - exit(1); + if (ioctl(fd, UFFDIO_API, &uffdio_api) == -1) { + cerr << "UFFD Compatibilty Check - userfaultfd WP Not Available\n"; + exit(1); + } + + if (!(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) { + cerr << "UFFD Compatibilty Check - unsupported userfaultfd WP\n"; + exit(1); + } + + close(fd); + + return 0; +} + +void* umap(void* addr, uint64_t length, int prot, int flags, int fd, off_t offset) +{ + struct stat file; + + if (check_uffd_compatibility() < 0) + return NULL; + + fstat(fd,&file); + struct umap_backing_file file1={.fd = fd, .data_size = file.st_size, .data_offset = offset}; + return umap_mf(addr, length, prot, flags, 1, &file1); +} + +void* umap_mf(void* bass_addr, uint64_t region_size, int prot, int flags, int num_backing_file, umap_backing_file* backing_files) +{ + assert((region_size % page_size) == 0); + + if (!(flags & UMAP_PRIVATE) || flags & ~(UMAP_PRIVATE|UMAP_FIXED)) { + cerr << "umap: Invalid flags: " << hex << flags << endl; + return UMAP_FAILED; + } + + void* region = mmap(bass_addr, region_size, prot, flags | (MAP_ANONYMOUS | MAP_NORESERVE), -1, 0); + + if (region == MAP_FAILED) { + perror("ERROR: mmap failed: "); + return UMAP_FAILED; + } + + try { + active_umaps[region] = new _umap{region, region_size, num_backing_file, backing_files}; + } catch(const std::exception& e) { + cerr << __FUNCTION__ << " Failed to launch _umap: " << e.what() << endl; + return UMAP_FAILED; + } catch(...) { + cerr << "umap failed to instantiate _umap object\n"; + return UMAP_FAILED; + } + return region; +} + +int uunmap(void* addr, uint64_t length) +{ + auto it = active_umaps.find(addr); + + if (it != active_umaps.end()) { + delete it->second; + active_umaps.erase(it); + } + return 0; +} + +uint64_t umap_cfg_get_bufsize( void ) +{ + return (umap_pages_per_uffd_handler * uffd_threads); +} + +void umap_cfg_set_bufsize( uint64_t page_bufsize ) +{ + umap_pages_per_uffd_handler = (page_bufsize / uffd_threads); + + if (umap_pages_per_uffd_handler == 0) + umap_pages_per_uffd_handler = 1; +} + +// +// Signal Handlers +// +static struct sigaction saved_sa; + +void sighandler(int signum, siginfo_t *info, void* buf) +{ + if (signum != SIGBUS) { + cerr << "Unexpected signal: " << signum << " received\n"; + exit(1); + } + + void* page_begin = _umap::UMAP_PAGE_BEGIN(info->si_addr); + + for (auto it : active_umaps) { + for (auto ufh : it.second->ufault_handlers) { + if (ufh->page_is_in_umap(page_begin)) { + ufh->stat->sigbus++; + + if (ufh->get_pagebuffer()->find_inmem_page_desc(page_begin) != nullptr) + umapdbg("SIGBUS %p (page=%p) ALREADY IN UMAP PAGE BUFFER!\n", info->si_addr, page_begin); + else + umapdbg("SIGBUS %p (page=%p) Not currently in umap page buffer\n", info->si_addr, page_begin); + return; + } } + } + umapdbg("SIGBUS %p (page=%p) ADDRESS OUTSIDE OF UMAP RANGE\n", info->si_addr, page_begin); + assert(0); +} + +void __attribute ((constructor)) init_umap_lib( void ) +{ + struct sigaction act; + + umaplog_init(); + + if ((page_size = sysconf(_SC_PAGESIZE)) == -1) { + perror("ERROR: sysconf(_SC_PAGESIZE)"); + throw -1; + } + + unsigned int n = std::thread::hardware_concurrency(); + uffd_threads = (n == 0) ? 16 : n; - fprintf(stdout, "mode %llu\n", (unsigned long long)uffdio_register.mode); - return uffd; + act.sa_handler = NULL; + act.sa_sigaction = sighandler; + if (sigemptyset(&act.sa_mask) == -1) { + perror("ERROR: sigemptyset: "); + exit(1); + } + + act.sa_flags = SA_NODEFER | SA_SIGINFO; + + if (sigaction(SIGBUS, &act, &saved_sa) == -1) { + perror("ERROR: sigaction: "); + exit(1); + } } -void *uffd_handler(void *arg) +void __attribute ((destructor)) fine_umap_lib( void ) { - params_t *p = (params_t *) arg; - long pagesize = p->pagesize; + if (sigaction(SIGBUS, &saved_sa, NULL) == -1) { + perror("ERROR: sigaction restore: "); + exit(1); + } + + for (auto it : active_umaps) { + delete it.second; + } +} - p->faultnum=0; - posix_memalign((void**)&tmppagebuf, (size_t)512, pagesize); +// +// _umap class implementation +// +_umap::_umap(void* _region, uint64_t _rsize, int num_backing_file, umap_backing_file* backing_files) + : region{_region}, region_size{_rsize}, uffd_time_to_stop_working{false} +{ + for (int i=0;ibufsize, sizeof(pagebuffer_t)); -#ifdef ENABLE_FAULT_TRACE_BUFFER - trace_buf = (page_activity_trace_t *)calloc(trace_bufsize, sizeof(*trace_buf)); -#endif // ENABLE_FAULT_TRACE_BUFFER + uint64_t pages_in_region = region_size / page_size; + uint64_t pages_per_block = pages_in_region < UMAP_PAGES_PER_BLOCK ? pages_in_region : UMAP_PAGES_PER_BLOCK; + uint64_t page_blocks = pages_in_region / pages_per_block; + uint64_t remainder_of_pages_in_last_block = pages_in_region % pages_per_block; - for (;;) { - struct uffd_msg msg; + if (remainder_of_pages_in_last_block) + page_blocks++; // Account for extra block - struct pollfd pollfd[1]; - pollfd[0].fd = p->uffd; - pollfd[0].events = POLLIN; + uint64_t num_workers = page_blocks < uffd_threads ? page_blocks : uffd_threads; + uint64_t page_blocks_per_worker = page_blocks / num_workers; - // wait for a userfaultfd event to occur - int pollres = poll(pollfd, 1, 2000); + // cout << "umap " << num_workers << " workers for: " << region << " - " << (void*)((char*)region+region_size) << endl; - if (stop_uffd_handler) { - fprintf(stdout, "%s: Stop seen, exit\n", __FUNCTION__); - return NULL; - } + try { + for (uint64_t worker = 0; worker < num_workers; ++worker) { + umap_PageBlock pb; - switch (pollres) { - case -1: - perror("poll/userfaultfd"); - continue; - case 0: - continue; - case 1: - break; - default: - fprintf(stderr, "unexpected poll result\n"); - exit(1); - } + pb.base = (void*)((uint64_t)region + (worker * page_blocks_per_worker * pages_per_block * page_size)); + pb.length = page_blocks_per_worker * pages_per_block * page_size; - if (pollfd[0].revents & POLLERR) { - fprintf(stderr, "pollerr\n"); - exit(1); - } + // If I am the last worker and we have residual pages in last block + if ((worker == num_workers-1) && remainder_of_pages_in_last_block) + pb.length -= ((pages_per_block - remainder_of_pages_in_last_block)) * page_size; - if (!pollfd[0].revents & POLLIN) - continue; + vector segs{ pb }; - int readres = read(p->uffd, &msg, sizeof(msg)); - if (readres == -1) { - if (errno == EAGAIN) - continue; - perror("read/userfaultfd"); - exit(1); - } + ufault_handlers.push_back( new UserFaultHandler{this, segs, umap_pages_per_uffd_handler} ); + } + } catch(const std::exception& e) { + cerr << __FUNCTION__ << " Failed to launch _umap: " << e.what() << endl; + throw -1; + } catch(...) { + cerr << "umap failed to instantiate _umap object\n"; + throw -1; + } +} - if (readres != sizeof(msg)) { - fprintf(stderr, "invalid msg size\n"); - exit(1); - } +_umap::~_umap(void) +{ + umap_stats t; + + for ( auto handler : ufault_handlers ) { + handler->stop_uffd_worker(); + t.stat_faults += handler->stat->stat_faults; + t.dirty_evicts += handler->stat->dirty_evicts; + t.clean_evicts += handler->stat->clean_evicts; + t.wp_messages += handler->stat->wp_messages; + t.read_faults += handler->stat->read_faults; + t.write_faults += handler->stat->write_faults; + t.sigbus += handler->stat->sigbus; + t.stuck_wp += handler->stat->stuck_wp; + t.dropped_dups += handler->stat->dropped_dups; + } + + t.print_stats(); + + for ( auto handler : ufault_handlers ) + delete handler; +} - if (msg.event != UFFD_EVENT_PAGEFAULT) { - printf("Unexpected event %x\n", msg.event); - continue; - } - - // - // At this point, we know we have had a page fault. Let's handle it. - // -#define PAGE_BEGIN(a) (void*)((uint64_t)a & ~(pagesize-1)); - - p->faultnum = p->faultnum + 1;; - void* fault_addr = (void*)msg.arg.pagefault.address; - void* page_begin = PAGE_BEGIN(fault_addr); - -#ifdef ENABLE_FAULT_TRACE_BUFFER - if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) { - TRACE(page_begin, ft_wp, et_NA); - } - else if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) { - TRACE(page_begin, ft_write, et_NA); - } - else { - TRACE(page_begin, ft_read, et_NA); - } -#endif // ENABLE_FAULT_TRACE_BUFFER - - // - // Check to see if the faulting page is already in memory. This can - // happen if more than one thread causes a fault for the same page. - // - // TODO(MJM) - Implement better container to get rid of linear - // search. - // - bool page_in_memory = false; - int bufidx; - for (bufidx = 0; bufidx < p->bufsize; bufidx++) { - if (pagebuffer[bufidx].page == page_begin) { - page_in_memory = true; - break; - } - } +UserFaultHandler::UserFaultHandler(_umap* _um, const vector& _pblks, uint64_t _pbuf_size) + : + stat{ new umap_stats }, + _u{_um}, + PageBlocks{_pblks}, + pbuf_size{_pbuf_size}, + pagebuffer{ new umap_page_buffer{_pbuf_size} } +{ + umessages.resize(UMAP_UFFD_MAX_MESSAGES); + + if (posix_memalign((void**)&tmppagebuf, (uint64_t)512, page_size)) { + cerr << "ERROR: posix_memalign: failed\n"; + exit(1); + } + + if (tmppagebuf == nullptr) { + cerr << "Unable to allocate 512 bytes for temporary buffer\n"; + exit(1); + } + + if ((userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK)) < 0) { + perror("ERROR: userfaultfd syscall not available in this kernel"); + throw -1; + } + + struct uffdio_api uffdio_api = { .api = UFFD_API, .features = UFFD_FEATURE_PAGEFAULT_FLAG_WP }; + + if (ioctl(userfault_fd, UFFDIO_API, &uffdio_api) == -1) { + perror("ERROR: UFFDIO_API Failed: "); + exit(1); + } + + if (!(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) { + perror("ERROR: userfaultfd WP: "); + exit(1); + } + + for ( auto seg : PageBlocks ) { + struct uffdio_register uffdio_register = { + .range = {.start = (uint64_t)seg.base, .len = seg.length}, + .mode = UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP + }; + + umapdbg("Register %p\n", seg.base); + + if (ioctl(userfault_fd, UFFDIO_REGISTER, &uffdio_register) == -1) { + perror("ERROR: ioctl/uffdio_register"); + close(userfault_fd); + throw -1; + } - if (page_in_memory) { - if (msg.arg.pagefault.flags & - (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) { - pagebuffer[bufidx].dirty = true; - disable_wp_on_pages(p->uffd, (uint64_t)page_begin, pagesize, 1); - } - - struct uffdio_range wake; - wake.start = (uint64_t)page_begin; - wake.len = pagesize; - - if (ioctl(p->uffd, UFFDIO_WAKE, &wake) == -1) { - perror("ioctl(UFFDIO_WAKE)"); - exit(1); - } - continue; - } + if ((uffdio_register.ioctls & UFFD_API_RANGE_IOCTLS) != UFFD_API_RANGE_IOCTLS) { + cerr << "unexpected userfaultfd ioctl set\n"; + close(userfault_fd); + throw -1; + } + } + + _u->backingfile_fd=_u->bk_files[0].fd; + uffd_worker = new thread{&UserFaultHandler::uffd_handler, this}; +} - // - // Page not in memory, read it in and evict someone - // - ssize_t pread_ret = pread(p->fd, tmppagebuf, pagesize, - (off_t)((uint64_t)page_begin - (uint64_t)p->base_addr)); +UserFaultHandler::~UserFaultHandler(void) +{ + // + // Now that all of our worker threads have stopped, we can flush everything + // + for ( auto seg : PageBlocks ) { + struct uffdio_register uffdio_register; + uffdio_register.range.start = (uint64_t)seg.base; + uffdio_register.range.len = seg.length; - if (pread_ret == -1) { - perror("pread failed"); - exit(1); - } + if (ioctl(userfault_fd, UFFDIO_UNREGISTER, &uffdio_register.range)) { + perror("ERROR: UFFDIO_UNREGISTER"); + exit(1); + } + } - if (pagebuffer[startix].page) - evict_page(p, &pagebuffer[startix]); + free(tmppagebuf); + delete pagebuffer; + delete stat; + delete uffd_worker; +} - pagebuffer[startix].page = page_begin; +#if 0 +static string uffd_pf_reason(const struct uffd_msg& msg) +{ + if ((msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) == (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) + return "UFFD_PAGEFAULT_FLAG_WP UFFD_PAGEFAULT_FLAG_WRITE"; + else if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) + return "UFFD_PAGEFAULT_FLAG_WP"; + else if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) + return "UFFD_PAGEFAULT_FLAG_WRITE"; + else + return "UFFD_PAGEFAULT_READ"; +} +#endif - if (msg.arg.pagefault.flags & - (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) { - disable_wp_on_pages(p->uffd, (uint64_t)page_begin, pagesize, 1); - pagebuffer[startix].dirty = true; - } - else { - pagebuffer[startix].dirty = false; - } - startix = (startix +1) % p->bufsize; +struct less_than_key +{ + inline bool operator() (const struct uffd_msg& lhs, const struct uffd_msg& rhs) + { + if (lhs.arg.pagefault.address == rhs.arg.pagefault.address) + return (lhs.arg.pagefault.flags >= rhs.arg.pagefault.address); + else + return (lhs.arg.pagefault.address < rhs.arg.pagefault.address); + } +}; + +void UserFaultHandler::uffd_handler(void) +{ + prctl(PR_SET_NAME, "UMAP UFFD Hdlr", 0, 0, 0); + for (;;) { + struct pollfd pollfd[1]; + pollfd[0].fd = userfault_fd; + pollfd[0].events = POLLIN; + + if (_u->uffd_time_to_stop_working) { + // + // Flush the in-memory page buffer + // + for (umap_page* ep = pagebuffer->get_page_desc_to_evict(); ep != nullptr; ep = pagebuffer->get_page_desc_to_evict()) { + evict_page(ep); + pagebuffer->dealloc_page_desc(ep); + } + return; + } + + // wait for a userfaultfd event to occur + int pollres = poll(pollfd, 1, 2000); + + switch (pollres) { + case -1: + perror("ERROR: poll/userfaultfd"); + continue; + case 0: + continue; + case 1: + break; + default: + cerr << __FUNCTION__ << " unexpected uffdio poll result\n"; + exit(1); + } + + if (pollfd[0].revents & POLLERR) { + cerr << __FUNCTION__ << " POLLERR\n"; + exit(1); + } + + if (!pollfd[0].revents & POLLIN) + continue; + + int readres = read(userfault_fd, &umessages[0], UMAP_UFFD_MAX_MESSAGES * sizeof(struct uffd_msg)); + + if (readres == -1) { + if (errno == EAGAIN) + continue; + perror("ERROR: read/userfaultfd"); + exit(1); + } + + assert(readres % sizeof(struct uffd_msg) == 0); + + int msgs = readres / sizeof(struct uffd_msg); + + if (msgs < 1) { + cerr << __FUNCTION__ << "invalid msg size " << readres << " " << msgs; + exit(1); + } + + sort(umessages.begin(), umessages.begin()+msgs, less_than_key()); + +#if 0 + stringstream ss; + ss << msgs << " Messages:\n"; + for (int i = 0; i < msgs; ++i) { + ss << " " << uffd_pf_reason(umessages[i]) << endl; + } + umapdbg("%s\n", ss.str().c_str()); +#endif + + uint64_t last_addr = 0; + for (int i = 0; i < msgs; ++i) { + if (umessages[i].event != UFFD_EVENT_PAGEFAULT) { + cerr << __FUNCTION__ << " Unexpected event " << hex << umessages[i].event << endl; + continue; + } + + if (umessages[i].arg.pagefault.address == last_addr) { + stat->dropped_dups++; + continue; // Skip pages we have already copied in + } + + last_addr = umessages[i].arg.pagefault.address; + stat->stat_faults++; + pagefault_event(umessages[i]); // At this point, we know we have had a page fault. Let's handle it. + } + } +} +void UserFaultHandler::pagefault_event(const struct uffd_msg& msg) +{ + void* page_begin = (void*)msg.arg.pagefault.address; + umap_page* pm = pagebuffer->find_inmem_page_desc(page_begin); + stringstream ss; + + if (pm != nullptr) { + if (msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) { + if (!pm->page_is_dirty()) { + pm->mark_page_dirty(); + disable_wp_on_pages((uint64_t)page_begin, 1, false); + stat->wp_messages++; + } + else if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) { struct uffdio_copy copy; copy.src = (uint64_t)tmppagebuf; copy.dst = (uint64_t)page_begin; - copy.len = pagesize; - if (msg.arg.pagefault.flags & - (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) { - copy.mode = 0; - if (ioctl(p->uffd, UFFDIO_COPY, ©) == -1) { - perror("ioctl(UFFDIO_COPY wake)"); - exit(1); - } - } - else { - copy.mode = UFFDIO_COPY_MODE_DONTWAKE; - if (ioctl(p->uffd, UFFDIO_COPY, ©) == -1) { - perror("ioctl(UFFDIO_COPY nowake)"); - exit(1); - } - - enable_wp_on_pages_and_wake(p->uffd, (uint64_t)page_begin, - pagesize, 1); + copy.len = page_size; + copy.mode = UFFDIO_COPY_MODE_WP; + + stat->stuck_wp++; + + umapdbg("EVICT WORKAROUND FOR %p\n", page_begin); + + pm->mark_page_clean(); + memcpy(tmppagebuf, page_begin, page_size); // Save our data + evict_page(pm); // Evict ourselves + pm->set_page(page_begin); // Bring ourselves back in + + if (ioctl(userfault_fd, UFFDIO_COPY, ©) == -1) { + perror("ERROR12: ioctl(UFFDIO_COPY nowake)"); + exit(1); } + + } } - return NULL; + return; + } + + // + // Page not in memory, read it in and (potentially) evict someone + // + int file_id=0; + off_t offset=(uint64_t)page_begin - (uint64_t)_u->region; + + file_id = offset/_u->bk_files[0].data_size; //find the file id and offset number + offset %= _u->bk_files[0].data_size; + + if (pread(_u->bk_files[file_id].fd, tmppagebuf, page_size, offset+_u->bk_files[file_id].data_offset) == -1) { + perror("ERROR: pread failed"); + exit(1); + } + + if (msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) + ss << "PF(" << msg.arg.pagefault.flags << " WRITE) (UFFDIO_COPY) @(" << page_begin << ")"; + else + ss << "PF(" << msg.arg.pagefault.flags << " READ) (UFFDIO_COPY) @(" << page_begin << ")"; + + umapdbg("%s\n", ss.str().c_str()); + for (pm = pagebuffer->alloc_page_desc(page_begin); pm == nullptr; pm = pagebuffer->alloc_page_desc(page_begin)) { + umap_page* ep = pagebuffer->get_page_desc_to_evict(); + assert(ep != nullptr); + + ss << " Evicting " << (ep->page_is_dirty() ? "Dirty" : "Clean") << "Page " << ep->get_page(); + evict_page(ep); + pagebuffer->dealloc_page_desc(ep); + } + pagebuffer->add_page_desc_to_inmem(pm); + + umapdbg("%s\n", ss.str().c_str()); + + struct uffdio_copy copy; + copy.src = (uint64_t)tmppagebuf; + copy.dst = (uint64_t)page_begin; + copy.len = page_size; + copy.mode = 0; + + if (msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) { + stat->write_faults++; + pm->mark_page_dirty(); + + if (ioctl(userfault_fd, UFFDIO_COPY, ©) == -1) { + perror("ERROR: ioctl(UFFDIO_COPY nowake)"); + exit(1); + } + } + else { + stat->read_faults++; + pm->mark_page_clean(); + + copy.mode = UFFDIO_COPY_MODE_WP; + if (ioctl(userfault_fd, UFFDIO_COPY, ©) == -1) { + perror("ERROR: ioctl(UFFDIO_COPY nowake)"); + exit(1); + } + + assert(memcmp(tmppagebuf, page_begin, page_size) == 0); + } } -void evict_page(params_t* p, pagebuffer_t* pb) +bool UserFaultHandler::page_is_in_umap(const void* page_begin) { - if (pb->dirty) { - // Prevent further writes. No need to do this if not dirty because - // WP is already on. - // - // Preventing further writes is problematic because the kernel - // module will wake up any threads that might be waiting for a fault - // to be handled in this page. - // - // It is possible to work around this by making sure that all faults - // and WP exceptions for this page have been handled prior to evicting - // the page. - // - enable_wp_on_pages_and_wake(p->uffd,(uint64_t)pb->page,p->pagesize,1); - - ssize_t rval; - - TRACE(pb->page, ft_NA, et_dirty); - - - rval = pwrite(p->fd, (void*)(pb->page), p->pagesize, - (off_t)((uint64_t)pb->page - (uint64_t)p->base_addr)); - - if (rval == -1) { - perror("pwrite failed"); - assert(0); - } - } - else { - TRACE(pb->page, ft_NA, et_clean); - } + for ( auto it : PageBlocks ) + if (page_begin >= it.base && page_begin < (void*)((uint64_t)it.base + it.length)) + return true; + return false; +} + +void UserFaultHandler::evict_page(umap_page* pb) +{ + uint64_t* page = (uint64_t*)pb->get_page(); - if (madvise((void*)(pb->page), p->pagesize, MADV_DONTNEED) == -1) { - perror("madvise"); - assert(0); - } + if (pb->page_is_dirty()) { + stat->dirty_evicts++; - pb->page = 0ull; + // Prevent further writes. No need to do this if not dirty because WP is already on. + + enable_wp_on_pages_and_wake((uint64_t)page, 1); + if (pwrite(_u->backingfile_fd, (void*)page, page_size, (off_t)((uint64_t)page - (uint64_t)_u->region)) == -1) { + perror("ERROR: pwrite failed"); + assert(0); + } + } + else { + stat->clean_evicts++; + } + + if (madvise((void*)page, page_size, MADV_DONTNEED) == -1) { + perror("ERROR: madvise"); + assert(0); + } + + disable_wp_on_pages((uint64_t)page, 1, true); + pb->set_page(nullptr); } // -// Enabling WP always wakes up any sleeping thread that may have been faulted -// in the specified range. +// Enabling WP always wakes up any sleeping thread that may have been faulted in the specified range. // -// For reasons I don't understand, the kernel module interface for -// UFFDIO_WRITEPROTECT does not allow for the caller to submit -// UFFDIO_WRITEPROTECT_MODE_DONTWAKE when enabling WP with -// UFFDIO_WRITEPROTECT_MODE_WP. UFFDIO_WRITEPROTECT_MODE_DONTWAKE is only +// For reasons which are unknown, the kernel module interface for UFFDIO_WRITEPROTECT does not allow for the caller to submit +// UFFDIO_WRITEPROTECT_MODE_DONTWAKE when enabling WP with UFFDIO_WRITEPROTECT_MODE_WP. UFFDIO_WRITEPROTECT_MODE_DONTWAKE is only // allowed when disabling WP. // -void enable_wp_on_pages_and_wake(int uffd, uint64_t start, int64_t size, int64_t pages) +void UserFaultHandler::enable_wp_on_pages_and_wake(uint64_t start, int64_t num_pages) { - struct uffdio_writeprotect wp; - wp.range.start = start; - wp.range.len = size * pages; + struct uffdio_writeprotect wp; + wp.range.start = start; + wp.range.len = num_pages * page_size; + wp.mode = UFFDIO_WRITEPROTECT_MODE_WP; - wp.mode = UFFDIO_WRITEPROTECT_MODE_WP; + //umapdbg("+WRITEPROTECT (%p -- %p)\n", (void*)start, (void*)(start+((num_pages*page_size)-1))); - if (ioctl(uffd, UFFDIO_WRITEPROTECT, &wp) == -1) { - perror("ioctl(UFFDIO_WRITEPROTECT Enable)"); - exit(1); - } + if (ioctl(userfault_fd, UFFDIO_WRITEPROTECT, &wp) == -1) { + perror("ERROR: ioctl(UFFDIO_WRITEPROTECT Enable)"); + exit(1); + } } // -// We intentionally do not wake up faulting thread when disabling WP. This -// is to handle the write-fault case when the page needs to be copied in. +// We intentionally do not wake up faulting thread when disabling WP. This is to handle the write-fault case when the page needs to be copied in. // -void disable_wp_on_pages(int uffd, uint64_t start, int64_t size, int64_t pages) +void UserFaultHandler::disable_wp_on_pages(uint64_t start, int64_t num_pages, bool do_not_awaken) { - struct uffdio_writeprotect wp; - wp.range.start = start; - wp.range.len = size * pages; - wp.mode = UFFDIO_WRITEPROTECT_MODE_DONTWAKE; - - if (ioctl(uffd, UFFDIO_WRITEPROTECT, &wp) == -1) { - perror("ioctl(UFFDIO_WRITEPROTECT Disable)"); - exit(1); - } + struct uffdio_writeprotect wp; + wp.range.start = start; + wp.range.len = page_size * num_pages; + //wp.mode = UFFDIO_WRITEPROTECT_MODE_DONTWAKE; + wp.mode = do_not_awaken ? UFFDIO_WRITEPROTECT_MODE_DONTWAKE : 0; + + //umapdbg("-WRITEPROTECT (%p -- %p)\n", (void*)start, (void*)(start+((num_pages*page_size)-1))); + + if (ioctl(userfault_fd, UFFDIO_WRITEPROTECT, &wp) == -1) { + perror("ERROR: ioctl(UFFDIO_WRITEPROTECT Disable)"); + exit(1); + } } -int uffd_finalize(void *arg, long num_pages) +// +// umap_page_buffer class implementation +// +umap_page_buffer::umap_page_buffer(uint64_t pbuffersize) : page_buffer_size{pbuffersize} { - params_t *p = (params_t *) arg; + for (uint64_t i = 0; i < page_buffer_size; ++i) + free_page_descriptors.push_front(new umap_page()); +} - // first write out all modified pages +umap_page_buffer::~umap_page_buffer() +{ + assert(inmem_page_map.size() == 0); + assert(inmem_page_descriptors.size() == 0); + assert(free_page_descriptors.size() == page_buffer_size); - int tmpix; - for (tmpix=0; tmpix < p->bufsize; tmpix++) - if (pagebuffer[tmpix].page) - evict_page(p, &pagebuffer[tmpix]); + for (unsigned long i = 0; i < page_buffer_size; ++i) + delete free_page_descriptors[i]; +} - struct uffdio_register uffdio_register; - uffdio_register.range.start = (uint64_t)p->base_addr; - uffdio_register.range.len = p->pagesize * num_pages; +umap_page* umap_page_buffer::alloc_page_desc(void* page) +{ + umap_page* p = nullptr; + if (!free_page_descriptors.empty()) { + p = free_page_descriptors.back(); + free_page_descriptors.pop_back(); + p->set_page(page); + } + return p; +} - if (ioctl(p->uffd, UFFDIO_UNREGISTER, &uffdio_register.range)) { - fprintf(stderr, "ioctl unregister failure\n"); - return 1; - } - return 0; +void umap_page_buffer::dealloc_page_desc(umap_page* page_desc) +{ + page_desc->mark_page_clean(); + page_desc->set_page(nullptr); + free_page_descriptors.push_front(page_desc); } -long get_pagesize(void) +void umap_page_buffer::add_page_desc_to_inmem(umap_page* page_desc) { - long ret = sysconf(_SC_PAGESIZE); - if (ret == -1) { - perror("sysconf/pagesize"); - exit(1); - } - assert(ret > 0); - return ret; + inmem_page_map[page_desc->get_page()] = page_desc; + inmem_page_descriptors.push_front(page_desc); } -void stop_umap_handler() +umap_page* umap_page_buffer::get_page_desc_to_evict() { - stop_uffd_handler = 1; + umap_page* p = nullptr; + if (!inmem_page_descriptors.empty()) { + p = inmem_page_descriptors.back(); + inmem_page_descriptors.pop_back(); + assert(p != nullptr); + assert(p->get_page() != nullptr); + inmem_page_map.erase(p->get_page()); + } + return p; } -#ifdef ENABLE_FAULT_TRACE_BUFFER -void pa_trace(uint64_t page, enum fault_types ftype, enum evict_types etype) +umap_page* umap_page_buffer::find_inmem_page_desc(void* page_addr) { - trace_buf[trace_idx].trace_seq = trace_seq++; - trace_buf[trace_idx].page = (void*)page; - trace_buf[trace_idx].ftype = ftype; - trace_buf[trace_idx].etype = etype; + auto it = inmem_page_map.find(page_addr); + return((it == inmem_page_map.end()) ? nullptr : it->second); +} + +// +// umap_page class implementation +// +void umap_page::set_page(void* _p) +{ + page = _p; +} - trace_idx = (trace_idx +1) % trace_bufsize; +// +// umap_stats implementation +// +void umap_stats::print_stats(void) +{ + cerr << stat_faults << " Faults\n" + << read_faults << " READ Faults" << endl + << write_faults << " WRITE Faults" << endl + << wp_messages << " WP Messages" << endl + << dirty_evicts << " Dirty Evictions" << endl + << clean_evicts << " Clean Evictions" << endl + << sigbus << " SIGBUS Errors" << endl + << stuck_wp << " Stuck WP Workarounds" << endl + << dropped_dups << " Dropped Duplicates" << endl; } -#endif // ENABLE_FAULT_TRACE_BUFFER diff --git a/src/umap.h b/src/umap.h deleted file mode 100644 index 3966d053..00000000 --- a/src/umap.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#ifndef UFFD_HANDLER_H -#define UFFD_HANDLER_H - -#include -#include -#include -#include - -const int UMAP_VERSION_MAJOR = 0; -const int UMAP_VERSION_MINOR = 0; -const int UMAP_VERSION_PATCH = 1; - -// Uncomment the following line to enable tracing (to circular buffer in mem) -#define ENABLE_FAULT_TRACE_BUFFER 1 - -#ifdef ENABLE_FAULT_TRACE_BUFFER -#define TRACE(_pb_, _ft_, _et_) pa_trace((uint64_t)_pb_, _ft_, _et_) -#else -#define TRACE(_pb_, _ft_, _et_) ; -#endif // ENABLE_FAULT_TRACE_BUFFER - -typedef struct params { - int uffd; - void* base_addr; - long pagesize; - int bufsize; - int faultnum; - int fd; -} params_t; - -typedef struct pagebuffer { - void* page; - bool dirty; -} pagebuffer_t; - -enum fault_types { - ft_NA=-1, - ft_read=0, - ft_write=1, - ft_wp=2 -}; - -enum evict_types { - et_NA=-1, - et_none=0, - et_clean=1, - et_dirty=2 -}; - -typedef struct { - int trace_seq; - void* page; - enum fault_types ftype; - enum evict_types etype; -} page_activity_trace_t; - -#ifdef __cplusplus -extern "C" { -#endif - void stop_umap_handler(void); - long get_pagesize(void); - int uffd_init(void*, long, long); - void *uffd_handler(void*); - int uffd_finalize(void*, long); -#ifdef __cplusplus -} -#endif - -#ifdef ENABLE_FAULT_TRACE_BUFFER -void pa_trace(uint64_t, enum fault_types, enum evict_types); -#endif // ENABLE_FAULT_TRACE_BUFFER - -void enable_wp_on_pages_and_wake(int, uint64_t, int64_t, int64_t); -void disable_wp_on_pages(int, uint64_t, int64_t, int64_t); -void evict_page(params_t*, pagebuffer_t *); - -#endif // UFFD_HANDLER_H diff --git a/src/umaplog.cpp b/src/umaplog.cpp new file mode 100644 index 00000000..5862e7cb --- /dev/null +++ b/src/umaplog.cpp @@ -0,0 +1,34 @@ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include +#include +#include +#include +#include "umaplog.h" // umap_log() + +using namespace std; + +static std::mutex mtx; +bool umap_logging = true; + +void umaplog_lock(void) +{ + mtx.lock(); +} + +void umaplog_unlock(void) +{ + mtx.unlock(); +} + +void __umaplog_init(void) +{ + char *log = getenv("UMAP_LOGGING"); + if (log && atoi(log)) + umap_logging = true; + else + umap_logging = false; +} diff --git a/src/umaplog.h b/src/umaplog.h new file mode 100644 index 00000000..8131dda3 --- /dev/null +++ b/src/umaplog.h @@ -0,0 +1,37 @@ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _UMAPLOG_H_ +#define _UMAPLOG_H_ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE +// +// Usage: This logging facility is available in Debug builds of the library. It is enabled by setting the UMAP_LOGGING to a value (if unset, it will be disabled. +#include +#include + +extern void __umaplog_init(void); +extern void umaplog_lock(void); +extern void umaplog_unlock(void); +extern bool umap_logging; + +#define umaperr(format, ...)\ + do {\ + struct timespec t;\ + (void)clock_gettime(CLOCK_MONOTONIC_RAW, &t);\ + umaplog_lock();\ + fprintf(stderr, "%ld.%09ld " format, t.tv_sec, t.tv_nsec, ## __VA_ARGS__);\ + umaplog_unlock();\ + } while (0) +#define umaplog_init __umaplog_init + +#define umapdbg(format, ...)\ + do {\ + if (umap_logging) {\ + struct timespec t;\ + (void)clock_gettime(CLOCK_MONOTONIC_RAW, &t);\ + umaplog_lock();\ + fprintf(stderr, "%ld.%09ld " format, t.tv_sec, t.tv_nsec, ## __VA_ARGS__);\ + umaplog_unlock();\ + }\ + } while (0) +#endif diff --git a/sysincludes/linux/userfaultfd.h b/sysincludes/linux/userfaultfd.h new file mode 100644 index 00000000..52635919 --- /dev/null +++ b/sysincludes/linux/userfaultfd.h @@ -0,0 +1,269 @@ +/* + * include/linux/userfaultfd.h + * + * Copyright (C) 2007 Davide Libenzi + * Copyright (C) 2015 Red Hat, Inc. + * + */ + +#ifndef _LINUX_USERFAULTFD_H +#define _LINUX_USERFAULTFD_H + +#include + +/* + * If the UFFDIO_API is upgraded someday, the UFFDIO_UNREGISTER and + * UFFDIO_WAKE ioctls should be defined as _IOW and not as _IOR. In + * userfaultfd.h we assumed the kernel was reading (instead _IOC_READ + * means the userland is reading). + */ +#define UFFD_API ((__u64)0xAA) +#define UFFD_API_FEATURES (UFFD_FEATURE_PAGEFAULT_FLAG_WP | \ + UFFD_FEATURE_EVENT_FORK | \ + UFFD_FEATURE_EVENT_REMAP | \ + UFFD_FEATURE_EVENT_REMOVE | \ + UFFD_FEATURE_EVENT_UNMAP | \ + UFFD_FEATURE_MISSING_HUGETLBFS | \ + UFFD_FEATURE_MISSING_SHMEM | \ + UFFD_FEATURE_SIGBUS | \ + UFFD_FEATURE_THREAD_ID) +#define UFFD_API_IOCTLS \ + ((__u64)1 << _UFFDIO_REGISTER | \ + (__u64)1 << _UFFDIO_UNREGISTER | \ + (__u64)1 << _UFFDIO_API) +#define UFFD_API_RANGE_IOCTLS \ + ((__u64)1 << _UFFDIO_WAKE | \ + (__u64)1 << _UFFDIO_COPY | \ + (__u64)1 << _UFFDIO_ZEROPAGE | \ + (__u64)1 << _UFFDIO_REMAP | \ + (__u64)1 << _UFFDIO_WRITEPROTECT) +#define UFFD_API_RANGE_IOCTLS_BASIC \ + ((__u64)1 << _UFFDIO_WAKE | \ + (__u64)1 << _UFFDIO_COPY) + +/* + * Valid ioctl command number range with this API is from 0x00 to + * 0x3F. UFFDIO_API is the fixed number, everything else can be + * changed by implementing a different UFFD_API. If sticking to the + * same UFFD_API more ioctl can be added and userland will be aware of + * which ioctl the running kernel implements through the ioctl command + * bitmask written by the UFFDIO_API. + */ +#define _UFFDIO_REGISTER (0x00) +#define _UFFDIO_UNREGISTER (0x01) +#define _UFFDIO_WAKE (0x02) +#define _UFFDIO_COPY (0x03) +#define _UFFDIO_ZEROPAGE (0x04) +#define _UFFDIO_REMAP (0x05) +#define _UFFDIO_WRITEPROTECT (0x06) +#define _UFFDIO_API (0x3F) + +/* userfaultfd ioctl ids */ +#define UFFDIO 0xAA +#define UFFDIO_API _IOWR(UFFDIO, _UFFDIO_API, \ + struct uffdio_api) +#define UFFDIO_REGISTER _IOWR(UFFDIO, _UFFDIO_REGISTER, \ + struct uffdio_register) +#define UFFDIO_UNREGISTER _IOR(UFFDIO, _UFFDIO_UNREGISTER, \ + struct uffdio_range) +#define UFFDIO_WAKE _IOR(UFFDIO, _UFFDIO_WAKE, \ + struct uffdio_range) +#define UFFDIO_COPY _IOWR(UFFDIO, _UFFDIO_COPY, \ + struct uffdio_copy) +#define UFFDIO_ZEROPAGE _IOWR(UFFDIO, _UFFDIO_ZEROPAGE, \ + struct uffdio_zeropage) +#define UFFDIO_REMAP _IOWR(UFFDIO, _UFFDIO_REMAP, \ + struct uffdio_remap) +#define UFFDIO_WRITEPROTECT _IOWR(UFFDIO, _UFFDIO_WRITEPROTECT, \ + struct uffdio_writeprotect) + +/* read() structure */ +struct uffd_msg { + __u8 event; + + __u8 reserved1; + __u16 reserved2; + __u32 reserved3; + + union { + struct { + __u64 flags; + __u64 address; + union { + __u32 ptid; + } feat; + } pagefault; + + struct { + __u32 ufd; + } fork; + + struct { + __u64 from; + __u64 to; + __u64 len; + } remap; + + struct { + __u64 start; + __u64 end; + } remove; + + struct { + /* unused reserved fields */ + __u64 reserved1; + __u64 reserved2; + __u64 reserved3; + } reserved; + } arg; +} __attribute__((packed)); + +/* + * Start at 0x12 and not at 0 to be more strict against bugs. + */ +#define UFFD_EVENT_PAGEFAULT 0x12 +#define UFFD_EVENT_FORK 0x13 +#define UFFD_EVENT_REMAP 0x14 +#define UFFD_EVENT_REMOVE 0x15 +#define UFFD_EVENT_UNMAP 0x16 + +/* flags for UFFD_EVENT_PAGEFAULT */ +#define UFFD_PAGEFAULT_FLAG_WRITE (1<<0) /* If this was a write fault */ +#define UFFD_PAGEFAULT_FLAG_WP (1<<1) /* If reason is VM_UFFD_WP */ + +struct uffdio_api { + /* userland asks for an API number and the features to enable */ + __u64 api; + /* + * Kernel answers below with the all available features for + * the API, this notifies userland of which events and/or + * which flags for each event are enabled in the current + * kernel. + * + * Note: UFFD_EVENT_PAGEFAULT and UFFD_PAGEFAULT_FLAG_WRITE + * are to be considered implicitly always enabled in all kernels as + * long as the uffdio_api.api requested matches UFFD_API. + * + * UFFD_FEATURE_MISSING_HUGETLBFS means an UFFDIO_REGISTER + * with UFFDIO_REGISTER_MODE_MISSING mode will succeed on + * hugetlbfs virtual memory ranges. Adding or not adding + * UFFD_FEATURE_MISSING_HUGETLBFS to uffdio_api.features has + * no real functional effect after UFFDIO_API returns, but + * it's only useful for an initial feature set probe at + * UFFDIO_API time. There are two ways to use it: + * + * 1) by adding UFFD_FEATURE_MISSING_HUGETLBFS to the + * uffdio_api.features before calling UFFDIO_API, an error + * will be returned by UFFDIO_API on a kernel without + * hugetlbfs missing support + * + * 2) the UFFD_FEATURE_MISSING_HUGETLBFS can not be added in + * uffdio_api.features and instead it will be set by the + * kernel in the uffdio_api.features if the kernel supports + * it, so userland can later check if the feature flag is + * present in uffdio_api.features after UFFDIO_API + * succeeded. + * + * UFFD_FEATURE_MISSING_SHMEM works the same as + * UFFD_FEATURE_MISSING_HUGETLBFS, but it applies to shmem + * (i.e. tmpfs and other shmem based APIs). + * + * UFFD_FEATURE_SIGBUS feature means no page-fault + * (UFFD_EVENT_PAGEFAULT) event will be delivered, instead + * a SIGBUS signal will be sent to the faulting process. + * + * UFFD_FEATURE_THREAD_ID pid of the page faulted task_struct will + * be returned, if feature is not requested 0 will be returned. + */ +#define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0) +#define UFFD_FEATURE_EVENT_FORK (1<<1) +#define UFFD_FEATURE_EVENT_REMAP (1<<2) +#define UFFD_FEATURE_EVENT_REMOVE (1<<3) +#define UFFD_FEATURE_MISSING_HUGETLBFS (1<<4) +#define UFFD_FEATURE_MISSING_SHMEM (1<<5) +#define UFFD_FEATURE_EVENT_UNMAP (1<<6) +#define UFFD_FEATURE_SIGBUS (1<<7) +#define UFFD_FEATURE_THREAD_ID (1<<8) + __u64 features; + + __u64 ioctls; +}; + +struct uffdio_range { + __u64 start; + __u64 len; +}; + +struct uffdio_register { + struct uffdio_range range; +#define UFFDIO_REGISTER_MODE_MISSING ((__u64)1<<0) +#define UFFDIO_REGISTER_MODE_WP ((__u64)1<<1) + __u64 mode; + + /* + * kernel answers which ioctl commands are available for the + * range, keep at the end as the last 8 bytes aren't read. + */ + __u64 ioctls; +}; + +struct uffdio_copy { + __u64 dst; + __u64 src; + __u64 len; +#define UFFDIO_COPY_MODE_DONTWAKE ((__u64)1<<0) + /* + * UFFDIO_COPY_MODE_WP will map the page wrprotected on the + * fly. UFFDIO_COPY_MODE_WP is available only if the + * wrprotection ioctl are implemented for the range according + * to the uffdio_register.ioctls. + */ +#define UFFDIO_COPY_MODE_WP ((__u64)1<<1) + __u64 mode; + + /* + * "copy" is written by the ioctl and must be at the end: the + * copy_from_user will not read the last 8 bytes. + */ + __s64 copy; +}; + +struct uffdio_zeropage { + struct uffdio_range range; +#define UFFDIO_ZEROPAGE_MODE_DONTWAKE ((__u64)1<<0) + __u64 mode; + + /* + * "zeropage" is written by the ioctl and must be at the end: + * the copy_from_user will not read the last 8 bytes. + */ + __s64 zeropage; +}; + +struct uffdio_remap { + __u64 dst; + __u64 src; + __u64 len; + /* + * Especially if used to atomically remove memory from the + * address space the wake on the dst range is not needed. + */ +#define UFFDIO_REMAP_MODE_DONTWAKE ((__u64)1<<0) +#define UFFDIO_REMAP_MODE_ALLOW_SRC_HOLES ((__u64)1<<1) + __u64 mode; + + /* + * "remap" is written by the ioctl and must be at the end: the + * copy_from_user will not read the last 8 bytes. + */ + __s64 remap; +}; + +struct uffdio_writeprotect { + struct uffdio_range range; + /* !WP means undo writeprotect. DONTWAKE is valid only with !WP */ +#define UFFDIO_WRITEPROTECT_MODE_WP ((__u64)1<<0) +#define UFFDIO_WRITEPROTECT_MODE_DONTWAKE ((__u64)1<<1) + __u64 mode; +}; +#endif /* _LINUX_USERFAULTFD_H */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1d480049..794f4896 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,13 @@ add_subdirectory(libumaptest) - -add_subdirectory(umaptest) +# add_subdirectory(umapcpu) add_subdirectory(umapsort) -add_subdirectory(readload) - -add_subdirectory(median) +# add_subdirectory(umaptest) +# add_subdirectory(umapmillions) +# add_subdirectory(median) +# add_subdirectory(readload) +# add_subdirectory(uffd_test) +# add_subdirectory(churn) +# add_subdirectory(rwseq) +if (BUILD_FITS) + add_subdirectory(FITS) +endif() diff --git a/tests/FITS/CMakeLists.txt b/tests/FITS/CMakeLists.txt new file mode 100644 index 00000000..cfa02269 --- /dev/null +++ b/tests/FITS/CMakeLists.txt @@ -0,0 +1,34 @@ +project(FITS) + +add_library(qfits SHARED IMPORTED) +set_target_properties(qfits PROPERTIES + IMPORTED_LOCATION "$ENV{HOME}/qfits/lib/libqfits.so.0.0.0" + INTERFACE_INCLUDE_DIRECTORIES "$ENV{HOME}/qfits/include" +) + +FIND_PACKAGE( OpenMP REQUIRED ) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + add_executable(exdata_fits exdata_fits.c) + add_executable(multiple multiple.cpp) + add_executable(simple simple_loader.cpp) + + target_link_libraries(multiple libumap_static) + target_link_libraries(multiple libumaptest_static) + target_link_libraries(multiple qfits) + + target_link_libraries(simple libumaptest_static) + target_link_libraries(simple qfits) + + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) + + install(TARGETS multiple simple exdata_fits + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + RUNTIME DESTINATION bin ) + +else() + message("Skpping median, OpenMP required") +endif() diff --git a/tests/FITS/README.md b/tests/FITS/README.md new file mode 100644 index 00000000..5083b835 --- /dev/null +++ b/tests/FITS/README.md @@ -0,0 +1,5 @@ +This is an example of accessing pixels of fits file using qfits library. +To install qfits library, check this link: +[qfits](https://www.eso.org/sci/software/eclipse/qfits/). +Please install library under $HOME/qfits.(If not, change path in CMakeList.txt and setup.sh to installed library path.) +Please use setup.sh to run compiled executable. \ No newline at end of file diff --git a/tests/FITS/exdata_fits.c b/tests/FITS/exdata_fits.c new file mode 100644 index 00000000..4c6e7887 --- /dev/null +++ b/tests/FITS/exdata_fits.c @@ -0,0 +1,89 @@ +#define _GNU_SOURCE +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#define BSIZE (256*1024*1024) + +void mv(char* ifile, char* ofile, int remove_old, char* buffer) +{ + struct stat st; + int ifd, ofd; + + printf("Processing %s\n", ofile); + if (buffer == NULL) { + fprintf(stderr, "Could not allocated %d bytes\n", BSIZE); + _exit(1); + } + + if (stat(ifile, &st)) { + fprintf(stderr, "Could not stat %s: %s\n", ifile, strerror(errno)); + _exit(1); + } + + if ((ifd = open(ifile, (O_RDONLY | O_LARGEFILE))) < 0) { + fprintf(stderr, "Could not open %s: %s\n", ifile, strerror(errno)); + _exit(1); + } + + if ((ofd = open(ofile, (O_RDWR | O_CREAT | O_LARGEFILE | O_TRUNC | O_DIRECT), (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP))) < 0) { + fprintf(stderr, "Could not create %s: %s\n", ofile, strerror(errno)); + _exit(1); + } + + if (lseek(ifd, 2880, SEEK_SET) == (off_t)-1) { + fprintf(stderr, "Could not set initial seek in %s: %s\n", ifile, strerror(errno)); + _exit(1); + } + + ssize_t tsize = 0; + for (ssize_t rv = BSIZE; rv == BSIZE; ) { + if ((rv = read(ifd, buffer, BSIZE)) < 0) { + fprintf(stderr, "Read failed in %s: %s\n", ifile, strerror(errno)); + _exit(1); + } + + if (rv < BSIZE) + break; + + if (write(ofd, buffer, rv) != rv) { + fprintf(stderr, "Read failed in %s: %s\n", ifile, strerror(errno)); + _exit(1); + } + tsize += rv; + } + + close(ifd); + close(ofd); + + if ( remove_old ) { + if (unlink(ifile) < 0) { + fprintf(stderr, "Read failed in %s: %s\n", ifile, strerror(errno)); + _exit(1); + } + } + + printf("Wrote %zu bytes to %s\n", tsize, ofile); +} + +int main(int ac, char** av) +{ + char ifilename[256]; + char ofilename[256]; + char* buffer = (char*)aligned_alloc(4096, BSIZE); + + for (int i = 1; i <= 100; i++) { + sprintf(ifilename, "asteroid_sim_epoch%d.fits", i); + sprintf(ofilename, "asteroid_sim_epoch%d.data", i); + mv(ifilename, ofilename, (i != 1), buffer); + } + + free(buffer); + return 0; +} diff --git a/tests/FITS/input.txt b/tests/FITS/input.txt new file mode 100644 index 00000000..c2d159b1 --- /dev/null +++ b/tests/FITS/input.txt @@ -0,0 +1,2 @@ +1 +0 32768 0 32768 diff --git a/tests/FITS/multi_private.cpp b/tests/FITS/multi_private.cpp new file mode 100644 index 00000000..3cf2b3fc --- /dev/null +++ b/tests/FITS/multi_private.cpp @@ -0,0 +1,306 @@ +/* $Id: flipx.c,v 1.10 2006/02/17 10:26:58 yjung Exp $ + * + * This file is part of the ESO QFITS Library + * Copyright (C) 2001-2004 European Southern Observatory + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * $Author: yjung $ + * $Date: 2006/02/17 10:26:58 $ + * $Revision: 1.10 $ + * $Name: qfits-6_2_0 $ + */ + +/*----------------------------------------------------------------------------- + Includes + -----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUMPAGES 10000 +#define NUMTHREADS 1 +#define BUFFERSIZE 100 + +#include "umap.h" +#include "umaptest.h" + +#ifdef _OPENMP +#include +#endif + +extern "C" +{ +#include "qfits.h" +} + +umt_optstruct_t options; + +struct patch +{ + int sx,sy,ex,ey; +}; // boundries for each patches + +static inline double gets(void) +{ + return omp_get_wtime(); +} +void swapbyte(float *a,float *b) +{ + char *a1=(char *)a; + char *b1=(char *)b; + b1[0]=a1[3]; + b1[3]=a1[0]; + b1[1]=a1[2]; + b1[2]=a1[1]; +} +bool fequal(double a, double b) +{ + if (fabs(a-b)<(1e-6)) return 1; + else return 0; +} +double torben(float **m, int n,int pos) +{ + int i,j, less, greater, equal; + double min, max, guess, maxltguess, mingtguess; + float num; + + swapbyte(m[0]+pos,&num); + min = max = num; + //fprintf(stdout,"m:%6.5lf\n",num); + + for (i=1 ; imax) max=num; + //fprintf(stdout,"m:%6.5lf\n",num); + } + //fprintf(stdout,"Max:%6.5lf\nMin:%6.5lf\n",max,min); + + while (1) { + guess = (min+max)/2; + less = 0; greater = 0; equal = 0; + maxltguess = min ; + mingtguess = max ; + for (j=0; jmaxltguess) maxltguess = m_swaped; + } else { + greater++; + //printf("%6.5lf, %6.5lf\n",m_swaped,mingtguess); + if (m_swapedgreater) max = maxltguess ; + else min = mingtguess; + } + //fprintf(stdout,"guess: %6.5lf less:%d greater:%d equal:%d all:%d\n",guess,less,greater,equal,(n+1)/2); + int half=(n+1)/2; + if (less>=half) min=maxltguess; + else min=mingtguess; + if (n&1) return min; + if (greater >= half) max = mingtguess; + else if (greater+equal >= half) max = guess; + else max = maxltguess; + return (min+max)/(double)2; +} +void displaycube(double *cube,struct patch *list,int n) +{ + int i,j,k; + uint64_t lx=list[0].ex; + //uint64_t ly=list[0].ey; + for (k=1;k<=n;k++) + { + for (i=list[k].sy; i>nlist; + list=(struct patch *)calloc(nlist+1,sizeof(struct patch)); + list[0].sx=0; + list[0].sy=0; + list[0].ex=lx; + list[0].ey=ly;//boundry of the image + i=0; + while (i>list[i].sx>>list[i].ex>>list[i].sy>>list[i].ey; + } + input.close(); + } + + double start = gets(); + median_calc(nlist,list,cube_median,cube); + fprintf(stdout, "Median Calculation %f s\n", (double)(gets() - start)); + //displaycube(cube_median,list,nlist); + umt_closeandunmap_fits2(&options, totalbytes, base_addr, bk_list); + free(cube_median); + free(list); + return 0 ; +} + +/*----------------------------------------------------------------------------- + Main + -----------------------------------------------------------------------------*/ +int main(int argc, char * argv[]) +{ + int err ; + umt_getoptions(&options, argc, argv); + err=0; + test_openfiles(options.fn); + //err += fits(); + if (err>0) + { + fprintf(stderr, "%s: %d error(s) occurred\n", argv[0], err); + return -1 ; + } + return 0 ; +} diff --git a/tests/FITS/multi_private.sh b/tests/FITS/multi_private.sh new file mode 100755 index 00000000..3eafc45e --- /dev/null +++ b/tests/FITS/multi_private.sh @@ -0,0 +1,5 @@ +File=/mnt/intel/xiao/asteroid_sim_epoch +File1=/mnt/intel/xiao/real/asteroid_sim_epoch +#env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/private -f $File -p 1050000 -n 50 -b 100000 -t 8 +env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/private -f $File1 -p 41000 -n 10 -b 100000 -t 8 +#r -f /mnt/intel/xiao/asteroid_sim_epoch -p 1050000 -n 50 -b 100000 -t 8 diff --git a/tests/FITS/multiple.cpp b/tests/FITS/multiple.cpp new file mode 100644 index 00000000..00b62e6c --- /dev/null +++ b/tests/FITS/multiple.cpp @@ -0,0 +1,297 @@ +/* $Id: flipx.c,v 1.10 2006/02/17 10:26:58 yjung Exp $ + * + * This file is part of the ESO QFITS Library + * Copyright (C) 2001-2004 European Southern Observatory + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * $Author: yjung $ + * $Date: 2006/02/17 10:26:58 $ + * $Revision: 1.10 $ + * $Name: qfits-6_2_0 $ + */ + +/*----------------------------------------------------------------------------- + Includes + -----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUMPAGES 10000 +#define NUMTHREADS 1 +#define BUFFERSIZE 100 + +#include "umap.h" +#include "umaptest.h" + +#ifdef _OPENMP +#include +#endif + +extern "C" +{ +#include "qfits.h" +} + +umt_optstruct_t options; + +struct patch +{ + uint64_t sx,sy,ex,ey; +}; // boundries for each patches + +static inline double gets(void) +{ + return omp_get_wtime(); +} + +void swapbyte(float *a,float *b) +{ + char *a1=(char *)a; + char *b1=(char *)b; + b1[0]=a1[3]; + b1[3]=a1[0]; + b1[1]=a1[2]; + b1[2]=a1[1]; +} +bool fequal(double a, double b) +{ + if (fabs(a-b)<(1e-6)) return 1; + else return 0; +} +double torben(float *m, int n,uint64_t step) +{ + int i, less, greater, equal; + double min, max, guess, maxltguess, mingtguess; + float num; + uint64_t j,maxj=n*step; + + swapbyte(m,&num); + min = max = num; + j=(uint64_t)step; + + for (i=1 ; imax) max=num; + j+=step; + } + + while (1) { + guess = (min+max)/2; + less = 0; greater = 0; equal = 0; + maxltguess = min ; + mingtguess = max ; + for (j=0; jmaxltguess) maxltguess = m_swaped; + } else if (m_swaped>guess) + { + greater++; + if (m_swapedgreater) max = maxltguess ; + else min = mingtguess; + } + int half=(n+1)/2; + if (less>=half) min=maxltguess; + else min=mingtguess; + if (n&1) return min; + if (greater >= half) max = mingtguess; + else if (greater+equal >= half) max = guess; + else max = maxltguess; + return (min+max)/(double)2; +} +void displaycube(double *cube,struct patch *list,int n) +{ + //int i,j,k; + uint64_t lx=list[0].ex; + //uint64_t ly=list[0].ey; + for (int k=1;k<=n;k++) + { + for (unsigned int i=list[k].sy; i>nlist; + list=(struct patch *)calloc(nlist+1,sizeof(struct patch)); + list[0].sx=0; + list[0].sy=0; + list[0].ex=lx; + list[0].ey=ly;//boundry of the image + i=0; + while (!input.eof()) + { + i++; + input>>list[i].sx>>list[i].ex>>list[i].sy>>list[i].ey; + } + input.close(); + } + else { + printf("Unable to find input.txt file\n"); + return -1; + } + + double start = gets(); + median_calc(nlist,list,cube_median,cube); + fprintf(stdout, "Median Calculation %f s\n", (double)(gets() - start)); + //displaycube(cube_median,list,nlist); + free(cube_median); + free(list); + umt_closeandunmap_mf(&options, totalbytes, base_addr, bk_list); + return 0 ; +} + +/*----------------------------------------------------------------------------- + Main + -----------------------------------------------------------------------------*/ +int main(int argc, char * argv[]) +{ + int err ; + umt_getoptions(&options, argc, argv); + err=0; + test_openfiles(options.filename); + //err += fits(); + if (err>0) + { + fprintf(stderr, "%s: %d error(s) occurred\n", argv[0], err); + return -1 ; + } + return 0 ; +} diff --git a/tests/FITS/multiple.sh b/tests/FITS/multiple.sh new file mode 100755 index 00000000..25b760db --- /dev/null +++ b/tests/FITS/multiple.sh @@ -0,0 +1,7 @@ +File=/mnt/intel/xiao/asteroid_sim_epoch +#env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/multiple -f $File -p 52429000 -n 50 -b 10000000 -t 8 +File1=/mnt/intel/xiao/real/asteroid_sim_epoch +env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/multiple -f $File1 -p 41000 -n 10 -b 100000 -t 8 +#r -f /mnt/intel/xiao/asteroid_sim_epoch -p 52429000 -n 50 -b 10000000 -t 16 +#env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/multiple -f $File -p 52429000 -n 50 -b 10 -t 1 +#r -f /mnt/intel/xiao/real/asteroid_sim_epoch -p 41000 -n 10 -b 100000 -t 8 diff --git a/tests/FITS/simple.sh b/tests/FITS/simple.sh new file mode 100755 index 00000000..4b98be9e --- /dev/null +++ b/tests/FITS/simple.sh @@ -0,0 +1,6 @@ +~/../perma/drop_buffer_cache +File=/mnt/intel/xiao/asteroid_sim_epoch +File1=/mnt/intel/xiao/real/asteroid_sim_epoch +#env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/simple -f $File -n 50 -t 8 +env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/simple -f $File1 -n 10 -t 8 +#r -f /mnt/intel/xiao/asteroid_sim_epoch -n 50 -t 8 diff --git a/tests/FITS/simple_loader.cpp b/tests/FITS/simple_loader.cpp new file mode 100644 index 00000000..1e16c533 --- /dev/null +++ b/tests/FITS/simple_loader.cpp @@ -0,0 +1,329 @@ +/* $Id: flipx.c,v 1.10 2006/02/17 10:26:58 yjung Exp $ + * + * This file is part of the ESO QFITS Library + * Copyright (C) 2001-2004 European Southern Observatory + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * $Author: yjung $ + * $Date: 2006/02/17 10:26:58 $ + * $Revision: 1.10 $ + * $Name: qfits-6_2_0 $ + */ + +/*----------------------------------------------------------------------------- + Includes + -----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _OPENMP +#include +#endif + +#include "umaptest.h" + +extern "C" +{ +#include "qfits.h" +} + +umt_optstruct_t options; +struct patch +{ + uint64_t sx,sy,ex,ey; +}; // boundries for each patches + + +static inline double gets(void) +{ + return omp_get_wtime(); +} + +bool fequal(double a, double b) +{ + if (fabs(a-b)<(1e-6)) return 1; + else return 0; +} +void swapbyte(float *a,float *b) +{ + char *a1=(char *)a; + char *b1=(char *)b; + b1[0]=a1[3]; + b1[3]=a1[0]; + b1[1]=a1[2]; + b1[2]=a1[1]; +} +double torben(float **m, int n,int pos) +{ + int i,j, less, greater, equal; + double min, max, guess, maxltguess, mingtguess; + float num; + + swapbyte(m[0]+pos,&num); + min = max = num; + //fprintf(stdout,"m:%6.5lf\n",num); + + for (i=1 ; imax) max=num; + //fprintf(stdout,"m:%6.5lf\n",num); + } + //fprintf(stdout,"Max:%6.5lf\nMin:%6.5lf\n",max,min); + + while (1) { + guess = (min+max)/2; + less = 0; greater = 0; equal = 0; + maxltguess = min ; + mingtguess = max ; + for (j=0; jmaxltguess) maxltguess = m_swaped; + } else { + greater++; + //printf("%6.5lf, %6.5lf\n",m_swaped,mingtguess); + if (m_swapedgreater) max = maxltguess ; + else min = mingtguess; + } + //fprintf(stdout,"guess: %6.5lf less:%d greater:%d equal:%d all:%d\n",guess,less,greater,equal,(n+1)/2); + int half=(n+1)/2; + if (less>=half) min=maxltguess; + else min=mingtguess; + if (n&1) return min; + if (greater >= half) max = mingtguess; + else if (greater+equal >= half) max = guess; + else max = maxltguess; + return (min+max)/(double)2; +} +void displaycube(double *cube,struct patch *list,int n) +{ + //unsigned int i,j,k; + uint64_t lx=list[0].ex; + //uint64_t ly=list[0].ey; + for (int k=1;k<=n;k++) + { + for (unsigned int i=list[k].sy; i>nlist; + list=(struct patch *)calloc(nlist+1,sizeof(struct patch)); + list[0].sx=0; + list[0].sy=0; + list[0].ex=lx; + list[0].ey=ly;//boundry of the image + i=0; + while (!input.eof()) + { + i++; + input>>list[i].sx>>list[i].ex>>list[i].sy>>list[i].ey; + } + input.close(); + } + + double *cube_median=(double *)malloc(sizeof(double)*lx*ly); + double start = gets(); + median_calc(nlist,list,cube_median,d); + fprintf(stdout, "Median Calculation %f s\n", (double)(gets() - start)); + //displaycube(cube_median,list,nlist); + free(cube_median); + free(list); + for (i=0;i0) + { + fprintf(stderr, "%s: %d error(s) occurred\n", argv[0], err); + return -1 ; + } + return 0 ; +} diff --git a/tests/churn/CMakeLists.txt b/tests/churn/CMakeLists.txt new file mode 100644 index 00000000..1ab1abad --- /dev/null +++ b/tests/churn/CMakeLists.txt @@ -0,0 +1,22 @@ +project(churn) + +FIND_PACKAGE( OpenMP REQUIRED ) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + add_executable(churn churn.cpp options.cpp) + + target_link_libraries(churn libumap_static) + target_link_libraries(churn libumaptest_static) + + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) + + install(TARGETS churn + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + RUNTIME DESTINATION bin ) +else() + message("Skpping churn, OpenMP required") +endif() + diff --git a/tests/churn/churn.cpp b/tests/churn/churn.cpp new file mode 100644 index 00000000..7d86a706 --- /dev/null +++ b/tests/churn/churn.cpp @@ -0,0 +1,266 @@ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + The idea is that we have a single "Load Page" and a set + of N "Churn Pages" as follows: + + +==================================================+ + | | + | One Read Load Page | + | | + +--------------------------------------------------+ + | | + | One Write{/Read} Load Page | + | | + +--------------------------------------------------+ + | | + | Churn Page #1 | + | | + +--------------------------------------------------+ + | | + | Churn Page #... | + | | + +--------------------------------------------------+ + | | + | Churn Page #N | + | | + +==================================================+ + + We then have a smaller page_buffer_size that these pages will be faulted into and madvised out of via umap(). + + The LoadPage will have a set of num_load_reader and num_load_writer threads focussed exclusively on making reads and writes to locations constrained to the Load Page. + + The the Churn Pages will have num_churn_reader threads performing random byte read accesses across all of the Churn Pages effectively causing the Load Page to be paged in and out of the smaller Page_Buffer. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // sched_getcpu() +#include + +#include "umap.h" +#include "options.h" +#include "umaptest.h" + +uint64_t g_count = 0; +using namespace std; +using namespace chrono; + +class pageiotest { +public: + pageiotest(int _ac, char** _av): time_to_stop{false}, pagesize{umt_getpagesize()} { + getoptions(options, _ac, _av); + + umt_options.iodirect = options.iodirect; + umt_options.usemmap = options.usemmap; + umt_options.noinit = 0; + umt_options.filename = options.fn; + + maphandle = umt_openandmap(&umt_options, (options.num_churn_pages+2*options.num_load_pages)*pagesize, &base_addr); + assert(maphandle != NULL); + + read_load_pages = base_addr; + write_load_pages = (void*)((uint64_t)base_addr + (options.num_load_pages*pagesize)); + churn_pages = (void*)((uint64_t)base_addr + ((options.num_load_pages*2)*pagesize)); + + if ( ! options.noinit ) + init(); + + cout << "Churn Test:\n\t" + << options.page_buffer_size << " Pages in page buffer\n\t" + << options.num_load_pages << " Read Load (focus) pages from " << hex << read_load_pages << " - " << (void*)((char*)read_load_pages+((options.num_load_pages*pagesize)-1)) << dec << "\n\t" + << options.num_load_pages << " Write Load (focus) pages from " << hex << write_load_pages << " - " << (void*)((char*)write_load_pages+((options.num_load_pages*pagesize)-1)) << dec << "\n\t" + << options.num_churn_pages << " Churn pages from " << hex << churn_pages << " - " << (void*)((char*)churn_pages+((options.num_churn_pages*pagesize)-1)) << dec << "\n\t" + << options.num_churn_threads << " Churn threads\n\t" + << options.num_load_reader_threads << " Load Reader threads\n\t" + << options.num_load_writer_threads << " Load Writer threads\n\t" + << options.fn << " Backing file\n\t" + << options.testduration << " seconds for test duration.\n\n"; + } + + ~pageiotest( void ) { + umt_closeandunmap(&umt_options, (options.num_churn_pages+2*options.num_load_pages)*pagesize, base_addr, maphandle); + } + + void start( void ) { + if (options.initonly) + return; + + for (uint64_t p = 0; p < options.num_load_pages; ++p) { + for (uint64_t t = 0; t < options.num_load_reader_threads; ++t) + load_readers.push_back(new thread{&pageiotest::load_read2, this, p, t}); + + for (uint64_t t = 0; t < options.num_load_reader_threads; ++t) + load_readers.push_back(new thread{&pageiotest::load_read, this, p, t}); + + for (uint64_t t = 0; t < options.num_load_writer_threads && t < 1; ++t) + load_writers.push_back(new thread{&pageiotest::load_write, this, p}); + } + + for (uint64_t t = 0; t < options.num_churn_threads; ++t) + churn_readers.push_back(new thread{&pageiotest::churn, this, t}); + } + + void run( void ) { + if (options.initonly) + return; + + this_thread::sleep_for(seconds(options.testduration)); + } + + void stop( void ) { + time_to_stop = true; + for (auto i : load_readers2) + i->join(); + for (auto i : load_readers) + i->join(); + for (auto i : load_writers) + i->join(); + for (auto i : churn_readers) + i->join(); + } + +private: + bool time_to_stop; + umt_optstruct_t umt_options; + options_t options; + + long pagesize; + void* base_addr; + + void* read_load_pages; + void* write_load_pages; + vector load_readers; + vector load_readers2; + vector load_writers; + + void* churn_pages; + vector churn_readers; + + void* maphandle; + + void init() { + cout << "Initializing\n"; + { + uint64_t* p = (uint64_t*)churn_pages; + for (uint64_t i = 0; i < options.num_churn_pages*(pagesize/sizeof(*p)); ++i) + p[i] = i; + } + + { + for (uint64_t pageno = 0; pageno < options.num_load_pages; ++pageno) { + uint64_t* p = (uint64_t*)((uint64_t)read_load_pages+(pagesize*pageno)); + for (uint64_t i = 0; i < options.num_load_pages*(pagesize/sizeof(*p)); ++i) + p[i] = i; + + p = (uint64_t*)((uint64_t)write_load_pages+(pagesize*pageno)); + for (uint64_t i = 0; i < options.num_load_pages*(pagesize/sizeof(*p)); ++i) + p[i] = i; + } + } + cout << "Initialization Complete\n"; + } + + mutex lock; + + uint64_t churn( int tnum ) { + uint64_t cnt = 0; + uint64_t idx; + uint64_t* p = (uint64_t*)churn_pages; + mt19937 gen(tnum); + uniform_int_distribution rnd_int(0, ((options.num_churn_pages*(pagesize/sizeof(*p)))-1)); + + while ( !time_to_stop ) { + idx = rnd_int(gen); + if (p[idx] != idx) { + lock.lock(); + cerr << hex << "churn() " << p[idx] << " != " << idx << " at address " << &p[idx] << endl; + lock.unlock(); + break; + } + } + return cnt; + } + + void load_read(uint64_t pageno, int tnum) { + uint64_t* p = (uint64_t*)((uint64_t)read_load_pages+(pagesize*pageno)); + tnum = tnum + tnum * pageno; + mt19937 gen(tnum); + uniform_int_distribution rnd_int(0, ((pagesize/sizeof(*p))-1)); + + while ( !time_to_stop ) { + uint64_t idx = rnd_int(gen); + + if (p[idx] != idx) { + lock.lock(); + cerr << "load_read *(uint64_t*)" << &p[idx] << "=" << p[idx] << " != " << idx << endl; + lock.unlock(); + break; + } + } + } + + // Have a reader going nuts on the write page for fun. No data validation since the writer is changing it from underneath us. + void load_read2(uint64_t pageno, int tnum) { + uint64_t* p = (uint64_t*)((uint64_t)write_load_pages+(pagesize*pageno)); + tnum = tnum + tnum * pageno; + mt19937 gen(tnum); + uniform_int_distribution rnd_int(0, ((pagesize/sizeof(*p))-1)); + + while ( !time_to_stop ) { + uint64_t idx = rnd_int(gen); + g_count += p[idx]; + } + } + + void load_write(uint64_t pageno) { + uint64_t cnt = 0; + uint64_t* p = (uint64_t*)((uint64_t)write_load_pages+(pagesize*pageno)); + const int num_entries = pagesize/sizeof(*p); + + omp_set_num_threads(options.num_load_writer_threads); + + while ( !time_to_stop ) { + uint64_t cnt_base = cnt; + +#pragma omp parallel for + for (int i = 0; i < num_entries; ++i) { + p[i] = cnt_base + i; + } + +#pragma omp parallel for + for (int i = 0; i < num_entries; ++i) { + if (p[i] != (cnt_base + i)) { +#pragma omp critical + { + lock.lock(); + cerr << "load_write *(uint64_t*)" << &p[i] << "=" << p[i] << " != " << (cnt_base+i) << ": (" << cnt_base << "+" << i << "=" << (cnt_base+i) << ") - " << p[i] << " = " << ((cnt_base+i)-p[i]) << endl; + if (i != 0) + cerr << "load_write *(uint64_t*)" << &p[0] << "=" << p[0] << " , " << (cnt_base+0) << ": (" << cnt_base << "+" << 0 << "=" << (cnt_base+0) << ") - " << p[0] << " = " << ((cnt_base+0)-p[0]) << endl; + lock.unlock(); + } + exit(1); + } + } + + cnt += pagesize/sizeof(*p); + } + } +}; + +int main(int argc, char **argv) +{ + pageiotest test{argc, argv}; + test.start(); + test.run(); + test.stop(); + cout << g_count << endl; + + return 0; +} diff --git a/tests/churn/options.cpp b/tests/churn/options.cpp new file mode 100644 index 00000000..13af1032 --- /dev/null +++ b/tests/churn/options.cpp @@ -0,0 +1,142 @@ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include // cout/cerr +#include // getopt() +#include // duh... +#include "options.h" +#include "umap.h" + +static char const* FILENAME = "/tmp/abc"; +static const uint64_t NUMCHURNPAGES = 99; +static const uint64_t NUMLOADPAGES = 1; +static const uint64_t NUMCHURNTHREADS = 48; +static const uint64_t NUMLOADREADERS = 48; +static const uint64_t NUMLOADWRITERS = 48; +static const uint64_t TESTDURATION = 60; + +using namespace std; + +static void usage(char* pname) +{ + cerr + << "Usage: " << pname << " [Options...]\n\n" + << " --help - This message\n" + << " --initonly - Initialize only\n" + << " --noinit - No Initialization\n" + << " --directio - Use O_DIRECT for file IO\n" + << " --usemmap - Use mmap instead of umap\n" + << " -p # of pages in page buffer - default: " << umap_cfg_get_bufsize() << " Pages\n" + << " -c # of churn pages - default: " << NUMCHURNPAGES << " Pages\n" + << " -l # of load pages - default: " << NUMLOADPAGES << " Pages\n" + << " -t # of churn threads - default: " << NUMCHURNTHREADS << endl + << " -r # of load reader threads - default: " << NUMLOADREADERS << endl + << " -w # of load writer threads - default: " << NUMLOADWRITERS << endl + << " -f [backing file name] - default: " << FILENAME << endl + << " -d # seconds to run test - default: " << TESTDURATION << " seconds\n"; + exit(1); +} + +void getoptions(options_t& testops, int& argc, char **argv) +{ + int c; + char *pname = argv[0]; + + testops.iodirect=0; + testops.usemmap=0; + testops.noinit=0; + testops.initonly=0; + testops.num_churn_pages=NUMCHURNPAGES; + testops.num_churn_threads=NUMCHURNTHREADS; + testops.num_load_pages=NUMLOADPAGES; + testops.num_load_reader_threads=NUMLOADREADERS; + testops.num_load_writer_threads=NUMLOADWRITERS; + testops.fn=FILENAME; + testops.testduration=TESTDURATION; + testops.page_buffer_size = umap_cfg_get_bufsize(); + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"directio", no_argument, &testops.iodirect, 1 }, + {"usemmap", no_argument, &testops.usemmap, 1 }, + {"initonly", no_argument, &testops.initonly, 1 }, + {"noinit", no_argument, &testops.noinit, 1 }, + {"help", no_argument, NULL, 0 }, + {0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "p:c:l:t:r:w:f:d:", long_options, &option_index); + if (c == -1) + break; + + switch(c) { + case 0: + if (long_options[option_index].flag != 0) + break; + + usage(pname); + break; + + case 'p': + if ((testops.page_buffer_size = strtoull(optarg, nullptr, 0)) > 0) + break; + goto R0; + case 'c': + if ((testops.num_churn_pages = strtoull(optarg, nullptr, 0)) > 0) + break; + goto R0; + case 'l': + if ((testops.num_load_pages = strtoull(optarg, nullptr, 0)) > 0) + break; + goto R0; + + case 'd': + if ((testops.testduration = strtoull(optarg, nullptr, 0)) > 0) + break; + goto R0; + case 'f': + testops.fn = optarg; + break; + case 'w': + if ((testops.num_load_writer_threads = strtoull(optarg, nullptr, 0)) >= 0) + break; + goto R0; + case 'r': + if ((testops.num_load_reader_threads = strtoull(optarg, nullptr, 0)) >= 0) + break; + goto R0; + case 't': + if ((testops.num_churn_threads = strtoull(optarg, nullptr, 0)) >= 0) + break; + goto R0; + + default: + R0: + usage(pname); + } + } + + if (optind < argc) { + cerr << "Unknown Arguments: "; + while (optind < argc) + cerr << "\"" << argv[optind++] << "\" "; + cerr << endl; + usage(pname); + } + + umap_cfg_set_bufsize(testops.page_buffer_size); +} + +long umt_getpagesize(void) +{ + long page_size = sysconf(_SC_PAGESIZE); + if (page_size == -1) { + perror("sysconf/page_size"); + exit(1); + } + return page_size; +} + diff --git a/tests/churn/options.h b/tests/churn/options.h new file mode 100644 index 00000000..a88bd31f --- /dev/null +++ b/tests/churn/options.h @@ -0,0 +1,28 @@ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _OPTIONS_H +#define _OPTIONS_H +#include + +typedef struct { + int iodirect; + int usemmap; + int initonly; + int noinit; + + uint64_t page_buffer_size; // # of pages that page buffer can hold + + uint64_t num_churn_pages; + uint64_t num_load_pages; + + uint64_t num_churn_threads; + + uint64_t num_load_reader_threads; + uint64_t num_load_writer_threads; + + char const* fn; // Backing file name + + uint64_t testduration; // Duration (in seconds) to run test +} options_t; + +void getoptions(options_t&, int&, char **argv); +#endif // _OPTIONS_H diff --git a/tests/libumaptest/CMakeLists.txt b/tests/libumaptest/CMakeLists.txt index 1b19c70d..0c80f3c0 100644 --- a/tests/libumaptest/CMakeLists.txt +++ b/tests/libumaptest/CMakeLists.txt @@ -1,12 +1,16 @@ project(umap_testlibraries) -add_library(libumaptest SHARED options.cpp mmap.cpp) -add_library(libumaptest_static STATIC options.cpp mmap.cpp) +add_library(libumaptest SHARED mmap.cpp options.cpp) +add_library(libumaptest_static STATIC mmap.cpp options.cpp) + +target_link_libraries(libumaptest libumap) +target_link_libraries(libumaptest_static libumap_static) + set_target_properties(libumaptest_static PROPERTIES OUTPUT_NAME libumaptest) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") -include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) +include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include) diff --git a/tests/libumaptest/mmap.cpp b/tests/libumaptest/mmap.cpp index 1e865eac..df7be2fa 100644 --- a/tests/libumaptest/mmap.cpp +++ b/tests/libumaptest/mmap.cpp @@ -13,83 +13,217 @@ Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include +#include +#include #include #include #include + #include #include #include #include #include +#include +#include "umap.h" #include "umaptest.h" using namespace std; -void umt_openandmap( - const umt_optstruct_t& testops, - uint64_t numbytes, - int &fd, - void*& region) +typedef struct umt_map_handle { + uint64_t total_range_size; + vector files; + vector mf_files; +} umt_map_handle; + +void* umt_openandmap(const umt_optstruct_t* testops, uint64_t numbytes, void** region) +{ + return umt_openandmap_mf(testops, numbytes, region, (off_t)0, numbytes); +} + +void umt_closeandunmap(const umt_optstruct_t* testops, uint64_t numbytes, void* region, void* p) { - int open_options = O_RDWR; + umt_closeandunmap_mf(testops, numbytes, region, p); +} + +// +// Usage: +// If testops->num_files is 1, the testops->filname is assumed to point +// to the full name of the file. +// +// Otherwise, if testops->num_files > 1, the testops->filename is +// assumed to be a prefix and the first file is assumed to contain +// the FITS header. The test programs use the header located in the +// first file to determine the data size. The implementation ASSUMES +// that all of the data files are the same size and that the size +// is page-aligned. +// +// For 3 FITS files with "foo" prefix, the implementation will look for +// the following files: +// +// foo1.fits - 1st file containing BOTH FITS Header and Data +// foo1.data - 1st file containing only data (use exdata_fits to generate) +// foo2.data - 2nd file containing only data +// foo3.data - 3rd file containing only data +// +void* umt_openandmap_mf(const umt_optstruct_t* testops, uint64_t numbytes, void** region, off_t offset, off_t data_size) +{ + int open_options = O_RDWR | O_LARGEFILE; // TODO: Handle READONLY case someday + umt_map_handle* handle = new umt_map_handle; - if (testops.iodirect) + offset = 0; // Hack for now until we determine how to distinguish files without headers. + + if ( testops->iodirect ) open_options |= O_DIRECT; - if ( !testops.noinit ) + if ( !testops->noinit ) open_options |= O_CREAT; -#ifdef O_LARGEFILE - open_options |= O_LARGEFILE; -#endif + handle->total_range_size = 0; - fd = open(testops.fn, open_options, S_IRUSR|S_IWUSR); - if(fd == -1) { - perror("open"); - exit(-1); - } + for ( int i = 0; i < testops->num_files; ++i ) { + string filename; + umap_backing_file bfile; + + { + ostringstream ss; + if (testops->num_files > 1) // Treat file name as a basename + ss << testops->filename << (i+1) << ".data"; + else + ss << testops->filename; + filename = ss.str(); + } + + if ( ( bfile.fd = open(filename.c_str(), open_options, S_IRUSR | S_IWUSR) ) == -1 ) { + string estr = "Failed to open/create " + filename + ": "; + perror(estr.c_str()); + return NULL; + } + + if ( ! testops->noinit ) { // If we are initializing, attempt to pre-allocate disk space for the file. + try { + int x; + if ( ( x = posix_fallocate(bfile.fd, 0, data_size) != 0 ) ) { + ostringstream ss; + ss << "Failed to pre-allocate " << data_size << " bytes in " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } catch(const std::exception& e) { + cerr << "posix_fallocate: " << e.what() << endl; + return NULL; + } catch(...) { + cerr << "posix_fallocate failed to instantiate _umap object\n"; + return NULL; + } + } - if (testops.noinit) { - // If we are not initializing file, make sure that it is big enough struct stat sbuf; + if (fstat(bfile.fd, &sbuf) == -1) { + string estr = "Failed to get status (fstat) for " + filename + ": "; + perror(estr.c_str()); + return NULL; + } - if (fstat(fd, &sbuf) == -1) { - perror("fstat"); - exit(-1); + if ( (off_t)sbuf.st_size != (data_size+offset) ) { + cerr << filename << " size " << sbuf.st_size << " does not match specified data size of " << (data_size+offset) << endl; + return NULL; } - if ((uint64_t)sbuf.st_size < numbytes) { - cerr << testops.fn - << " file is not large enough. " << sbuf.st_size - << " < size requested " << numbytes << endl; - exit(-1); + handle->total_range_size += (uint64_t)data_size; + bfile.data_size = data_size; + bfile.data_offset = offset; + handle->mf_files.push_back(bfile); + handle->files.push_back(filename); + } + + const int prot = PROT_READ|PROT_WRITE; + + if ( testops->usemmap ) { + void* next_mmap = mmap(NULL, handle->total_range_size, prot, MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + if (next_mmap == MAP_FAILED) { + ostringstream ss; + ss << "reservation (mmap) of " << handle->total_range_size << " bytes failed: "; + perror(ss.str().c_str()); + return NULL; + } + + *region = next_mmap; + + if ( munmap(next_mmap, handle->total_range_size) < 0 ) { + ostringstream ss; + ss << "reservation (mumap) of " << handle->total_range_size << " from " << next_mmap << " failed: "; + perror(ss.str().c_str()); + return NULL; + } + + //cout << "Starting contiguous mappings at: " << next_mmap << endl; + + for ( int i = 0; i < testops->num_files; ++i ) { + void* mmap_region; + mmap_region = mmap(next_mmap, handle->mf_files[i].data_size, prot, MAP_SHARED | MAP_FIXED | MAP_NORESERVE, handle->mf_files[i].fd, offset); + if (mmap_region == MAP_FAILED) { + ostringstream ss; + ss << "mmap of " << handle->mf_files[i].data_size << " bytes failed for " << handle->files[i] << ": "; + perror(ss.str().c_str()); + return NULL; + } + //cout << handle->files[i] << "\t" << next_mmap << "\t" << mmap_region << endl; + + assert(mmap_region == next_mmap); + next_mmap = static_cast(mmap_region) + handle->mf_files[i].data_size; } } + else { + int flags = UMAP_PRIVATE; - if(posix_fallocate(fd,0, numbytes) != 0) { - perror("posix_fallocate"); - exit(-1); + *region = umap_mf(NULL, handle->total_range_size, prot, flags, testops->num_files, &handle->mf_files[0]); + if ( *region == UMAP_FAILED ) { + ostringstream ss; + ss << "umap_mf of " << handle->total_range_size << " bytes failed for " << handle->files[0] << ": "; + perror(ss.str().c_str()); + return NULL; + } + //cout << handle->files[0] << "\t" << handle->total_range_size << " bytes allocated at " << *region << endl; } - int prot = PROT_READ|PROT_WRITE; - int flags; - int my_fd; + //umt_closeandunmap_mf(testops, handle->total_range_size, *region, handle); + + //exit(0); + return (void *)handle; +} + +void umt_closeandunmap_mf(const umt_optstruct_t* testops, uint64_t numbytes, void* region, void* p) +{ + umt_map_handle* handle = static_cast(p); - if ( !testops.usemmap ) { - flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; - my_fd = -1; + if ( testops->usemmap ) { + int cnt = 0; + for ( auto i : handle->mf_files ) { + //cout << "munmap(region=" << region << ", size=" << i.data_size << ") for file " << handle->files[ cnt ] << endl; + if ( munmap(region, i.data_size) < 0 ) { + ostringstream ss; + ss << "munmap of " << i.data_size << " bytes failed for " << handle->files[0] << "on region " << region << ": "; + perror(ss.str().c_str()); + exit(-1); + } + cnt++; + region = static_cast(region) + i.data_size; + } } else { - my_fd = fd; - flags = MAP_SHARED; + //cout << "uunmap(region=" << region << ", size=" << numbytes << ") for file " << handle->files[0] << endl; + if (uunmap(region, numbytes) < 0) { + ostringstream ss; + ss << "munmap of " << numbytes << " bytes failed for " << handle->files[0] << "on region " << region << ": "; + perror(ss.str().c_str()); + exit(-1); + } } - // allocate a memory region to be managed by userfaultfd - region = mmap(NULL, numbytes, prot, flags, my_fd, 0); + for ( auto i : handle->mf_files ) + close(i.fd); - if (region == MAP_FAILED) { - perror("mmap"); - exit(-1); - } + delete handle; } - diff --git a/tests/libumaptest/options.cpp b/tests/libumaptest/options.cpp index 34a6621a..288eb963 100644 --- a/tests/libumaptest/options.cpp +++ b/tests/libumaptest/options.cpp @@ -1,21 +1,13 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + #include // cout/cerr #include // getopt() #include // duh... #include "umaptest.h" +#include "umap.h" char const* FILENAME = "/tmp/abc"; const uint64_t NUMPAGES = 10000000; @@ -36,32 +28,39 @@ static void usage(char* pname) << " --usemmap - Use mmap instead of umap\n" << " -p # of pages - default: " << NUMPAGES << endl << " -t # of threads - default: " << NUMTHREADS << endl - << " -b page buffer size - default: " << BUFFERSIZE << endl + << " -b page buffer size - default: " << umap_cfg_get_bufsize() << " Pages\n" + << " -n number of files - default: " << -1 << endl << " -f [file name] - backing file name. Must exist and be correct size for noinit\n"; exit(1); } -void umt_getoptions(umt_optstruct_t& testops, int argc, char *argv[]) +void umt_getoptions(umt_optstruct_t* testops, int argc, char *argv[]) { int c; char *pname = argv[0]; - testops = (umt_optstruct_t) { .initonly = 0, .noinit = 0, .iodirect = 0, - .usemmap = 0, .numpages = NUMPAGES, - .numthreads = NUMTHREADS, - .bufsize = BUFFERSIZE, .fn = FILENAME}; + testops->initonly = 0; + testops->noinit = 0; + testops->iodirect = 0; + testops->usemmap = 0; + testops->numpages = NUMPAGES; + testops->numthreads = NUMTHREADS; + testops->bufsize = umap_cfg_get_bufsize(); + testops->filename = FILENAME; + testops->num_files = 1; + while (1) { int option_index = 0; static struct option long_options[] = { - {"initonly", no_argument, &testops.initonly, 1 }, - {"noinit", no_argument, &testops.noinit, 1 }, - {"directio", no_argument, &testops.iodirect, 1 }, - {"usemmap", no_argument, &testops.usemmap, 1 }, + {"initonly", no_argument, &testops->initonly, 1 }, + {"noinit", no_argument, &testops->noinit, 1 }, + {"directio", no_argument, &testops->iodirect, 1 }, + {"usemmap", no_argument, &testops->usemmap, 1 }, {"help", no_argument, NULL, 0 }, {0, 0, 0, 0 } }; - c = getopt_long(argc, argv, "p:t:f:b:", long_options, &option_index); + c = getopt_long(argc, argv, "p:t:f:b:n:", long_options, &option_index); if (c == -1) break; @@ -74,19 +73,23 @@ void umt_getoptions(umt_optstruct_t& testops, int argc, char *argv[]) break; case 'p': - if ((testops.numpages = strtoull(optarg, nullptr, 0)) > 0) + if ((testops->numpages = strtoull(optarg, nullptr, 0)) > 0) break; goto R0; case 't': - if ((testops.numthreads = strtoull(optarg, nullptr, 0)) > 0) + if ((testops->numthreads = strtoull(optarg, nullptr, 0)) > 0) break; else goto R0; case 'b': - if ((testops.bufsize = strtoull(optarg, nullptr, 0)) > 0) + if ((testops->bufsize = strtoull(optarg, nullptr, 0)) > 0) + break; + else goto R0; + case 'n': + if ((testops->num_files = strtoull(optarg, nullptr, 0)) > 0) break; else goto R0; case 'f': - testops.fn = optarg; + testops->filename = optarg; break; default: R0: @@ -101,4 +104,17 @@ void umt_getoptions(umt_optstruct_t& testops, int argc, char *argv[]) cerr << endl; usage(pname); } + + umap_cfg_set_bufsize(testops->bufsize); } + +long umt_getpagesize(void) +{ + long page_size = sysconf(_SC_PAGESIZE); + if (page_size == -1) { + perror("sysconf/page_size"); + exit(1); + } + return page_size; +} + diff --git a/tests/libumaptest/umaptest.h b/tests/libumaptest/umaptest.h index 6363a092..5e88a663 100644 --- a/tests/libumaptest/umaptest.h +++ b/tests/libumaptest/umaptest.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _UMAPTEST_H #define _UMAPTEST_H -#include +#include typedef struct { int initonly; @@ -25,11 +25,20 @@ typedef struct { uint64_t numpages; uint64_t numthreads; uint64_t bufsize; - char const* fn; + int num_files; + char const* filename; // file prefix if num_files > 1 } umt_optstruct_t; +#ifdef __cplusplus extern "C" { - void umt_getoptions(umt_optstruct_t&, int, char *argv[]); - void umt_openandmap(const umt_optstruct_t&, uint64_t, int&, void*&); +#endif + void umt_getoptions(umt_optstruct_t*, int, char *argv[]); + void* umt_openandmap(const umt_optstruct_t*, uint64_t, void**); + void umt_closeandunmap(const umt_optstruct_t*, uint64_t, void*, void*); + long umt_getpagesize(void); + void* umt_openandmap_mf(const umt_optstruct_t*, uint64_t, void**,off_t,off_t); + void umt_closeandunmap_mf(const umt_optstruct_t*, uint64_t, void*,void*); +#ifdef __cplusplus } +#endif #endif // _UMAPTEST_H diff --git a/tests/median/CMakeLists.txt b/tests/median/CMakeLists.txt index 70e5a65b..1a956153 100644 --- a/tests/median/CMakeLists.txt +++ b/tests/median/CMakeLists.txt @@ -16,7 +16,7 @@ if(OPENMP_FOUND) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) - install(TARGETS median + install(TARGETS median median_cube LIBRARY DESTINATION lib ARCHIVE DESTINATION lib/static RUNTIME DESTINATION bin ) diff --git a/tests/median/median.cpp b/tests/median/median.cpp index 31d1f870..53e80772 100644 --- a/tests/median/median.cpp +++ b/tests/median/median.cpp @@ -57,7 +57,7 @@ void initdata(uint64_t *region, int64_t rlen) { std::mt19937 gen(rd()); std::uniform_int_distribution rnd_int; #pragma omp parallel for - for(int i=0; i< rlen; ++i) { + for(int64_t i=0; i< rlen; ++i) { region[i] = (uint64_t) (rlen - i);// rnd_int(gen); //region[i] = rnd_int(gen)>>1;//divide all values by 2 because of overflow in torben //printf("%llu\n", (long long)region[i]); @@ -72,7 +72,7 @@ uint64_t torben(uint64_t *m, int n) for (i=1 ; imax) max=m[i]; - if (m[i]>n) fprintf(stdout,"m:%llu\n",m[i]); + //if (m[i]>n) fprintf(stdout,"m:%llu\n",m[i]); } //fprintf(stdout,"Max:%llu\nMin:%llu\n",max,min); @@ -104,47 +104,28 @@ uint64_t torben(uint64_t *m, int n) int main(int argc, char **argv) { umt_optstruct_t options; - int uffd; long pagesize; int64_t totalbytes; - pthread_t uffd_thread; int64_t arraysize; uint64_t median; - int fd; - void *base_addr; - // parameter block to uffd - params_t *p = (params_t *) malloc(sizeof(params_t)); + void* base_addr; + void* maphandle; - pagesize = get_pagesize(); + pagesize = umt_getpagesize(); - umt_getoptions(options, argc, argv); + umt_getoptions(&options, argc, argv); totalbytes = options.numpages*pagesize; - umt_openandmap(options, totalbytes, p->fd,p->base_addr); - - if ( ! options.usemmap ) - { - fprintf(stdout, "Using UserfaultHandler Buffer\n"); - p->pagesize = pagesize; - p->bufsize = options.bufsize; - p->faultnum = 0; - p->uffd = uffd_init(p->base_addr, pagesize, options.numpages); - - pthread_create(&uffd_thread, NULL, uffd_handler, p); - sleep(1); - } - else - { - fprintf(stdout, "Using vanilla mmap()\n"); - } + maphandle = umt_openandmap(&options, totalbytes, &base_addr); + assert(maphandle != NULL); - fprintf(stdout, "%d pages, %d threads\n", options.numpages, options.numthreads); + fprintf(stdout, "%lu pages, %lu threads\n", options.numpages, options.numthreads); omp_set_num_threads(options.numthreads); - uint64_t *arr = (uint64_t *) p->base_addr; + uint64_t *arr = (uint64_t *) base_addr; arraysize = totalbytes/sizeof(int64_t); - fprintf(stdout,"Array size: %lld\n",arraysize); + fprintf(stdout,"Array size: %ld\n",arraysize); uint64_t start = getns(); // init data @@ -153,15 +134,9 @@ int main(int argc, char **argv) start = getns(); median=torben(arr,arraysize); - fprintf(stdout, "Median is %llu, Find median took %f us\n",median,(double)(getns() - start)/1000000.0); - - if ( ! options.usemmap ) - { - stop_umap_handler(); - pthread_join(uffd_thread, NULL); - uffd_finalize(p, options.numpages); - } + fprintf(stdout, "Median is %lu, Find median took %f us\n",median,(double)(getns() - start)/1000000.0); + umt_closeandunmap(&options, totalbytes, base_addr, maphandle); return 0; } diff --git a/tests/median/median_cube.cpp b/tests/median/median_cube.cpp index bfb071bd..978fe615 100644 --- a/tests/median/median_cube.cpp +++ b/tests/median/median_cube.cpp @@ -38,9 +38,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "umap.h" #include "umaptest.h" -//volatile int stop_uffd_handler; - - #define NUMPAGES 10000000 #define NUMTHREADS 2 #define BUFFERSIZE 16 @@ -128,7 +125,7 @@ void displaycube(uint64_t *cube,int a,int b,int c) for (i=0;ifd,p->base_addr); - - if ( ! options.usemmap ) - { - fprintf(stdout, "Using UserfaultHandler Buffer\n"); - p->pagesize = pagesize; - p->bufsize = options.bufsize; - p->faultnum = 0; - p->uffd = uffd_init(p->base_addr, pagesize, options.numpages); - - pthread_create(&uffd_thread, NULL, uffd_handler, p); - sleep(1); - } - else - { - fprintf(stdout, "Using vanilla mmap()\n"); - } - fprintf(stdout, "%d pages, %d threads\n", options.numpages, options.numthreads); + maphandle = umt_openandmap(&options, totalbytes, &base_addr); + assert(maphandle != NULL); + + fprintf(stdout, "%lu pages, %lu threads\n", options.numpages, options.numthreads); omp_set_num_threads(options.numthreads); - uint64_t *arr = (uint64_t *) p->base_addr; + uint64_t *arr = (uint64_t *) base_addr; arraysize = totalbytes/sizeof(int64_t); - fprintf(stdout,"Array size: %lld\n",arraysize); + fprintf(stdout, "Array size: %ld\n", arraysize); uint64_t start = getns(); size_a=10; @@ -183,29 +164,23 @@ int main(int argc, char **argv) // init data initdata(arr, size_a*size_b*size_c); cube=arr; - displaycube(cube,size_a,size_b,size_c); fprintf(stdout, "Init took %f us\n", (double)(getns() - start)/1000000.0); start = getns(); getall_median(); //median=torben(arr,arraysize); - fprintf(stdout, "Median is %llu, Find median took %f us\n",median,(double)(getns() - start)/1000000.0); + fprintf(stdout, "Find median took %f us\n",(double)(getns() - start)/1000000.0); int i,j; for (i=0;i rnd_int(0, rlen-1); - while (1) { + for (uint64_t i = 0; i < test_iterations; ++i) { uint64_t index = rnd_int(gen); if (region[index] != index) { fprintf(stderr, "%lu != %lu\n", index, region[index]); @@ -75,36 +76,23 @@ int main(int argc, char **argv) umt_optstruct_t options; long pagesize; int64_t totalbytes; - pthread_t uffd_thread; uint64_t arraysize; - params_t *p = (params_t *) malloc(sizeof(params_t)); + void* base_addr; + void* maphandle; - pagesize = get_pagesize(); + pagesize = umt_getpagesize(); - umt_getoptions(options, argc, argv); + umt_getoptions(&options, argc, argv); totalbytes = options.numpages*pagesize; - umt_openandmap(options, totalbytes, p->fd, p->base_addr); + maphandle = umt_openandmap(&options, totalbytes, &base_addr); + assert(maphandle != NULL); - if ( ! options.usemmap ) { - fprintf(stdout, "Using UserfaultHandler Buffer\n"); - p->pagesize = pagesize; - p->bufsize = options.bufsize; - p->faultnum = 0; - p->uffd = uffd_init(p->base_addr, pagesize, options.numpages); - - pthread_create(&uffd_thread, NULL, uffd_handler, p); - sleep(1); - } - else { - fprintf(stdout, "Using vanilla mmap()\n"); - } - fprintf(stdout, "%lu pages, %lu threads\n", options.numpages, options.numthreads); omp_set_num_threads(options.numthreads); - uint64_t *arr = (uint64_t *) p->base_addr; + uint64_t *arr = (uint64_t *) base_addr; arraysize = totalbytes/sizeof(int64_t); uint64_t start = getns(); @@ -119,12 +107,7 @@ int main(int argc, char **argv) runtest(arr, arraysize); fprintf(stdout, "Sort took %f us\n", (double)(getns() - start)/1000000.0); } - - if ( ! options.usemmap ) { - stop_umap_handler(); - pthread_join(uffd_thread, NULL); - uffd_finalize(p, options.numpages); - } + umt_closeandunmap(&options, totalbytes, base_addr, maphandle); return 0; } diff --git a/tests/readload/wastemem.sh b/tests/readload/wastemem.sh new file mode 100644 index 00000000..99402b19 --- /dev/null +++ b/tests/readload/wastemem.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if [ ! -d /mnt/tmpfs ]; then + sudo mkdir -p /mnt/tmpfs + sudo chmod go+rwx /mnt/tmpfs + sudo mount -t tmpfs -o size=$((510*1024*1024*1024)) tmpfs /mnt/tmpfs +fi + +WASTE=480 + +echo "Flushing Memory Cache" +sudo sync +echo 3 | sudo tee /proc/sys/vm/drop_caches + +echo "Disabling swap" +sudo sync +sudo swapoff -a + +if [ ! -f /mnt/tmpfs/3_${WASTE}GB ]; then + echo dd if=/dev/zero of=/mnt/tmpfs/3_${WASTE}GB bs=4096 count=$((${WASTE}*256*1024)) + dd if=/dev/zero of=/mnt/tmpfs/3_${WASTE}GB bs=4096 count=$((${WASTE}*256*1024)) +fi +exit diff --git a/tests/rwseq/CMakeLists.txt b/tests/rwseq/CMakeLists.txt new file mode 100644 index 00000000..abf6d0b8 --- /dev/null +++ b/tests/rwseq/CMakeLists.txt @@ -0,0 +1,22 @@ +project(rwseq) + +FIND_PACKAGE( OpenMP REQUIRED ) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + add_executable(rwseq rwseq.cpp options.cpp) + + target_link_libraries(rwseq libumap_static) + target_link_libraries(rwseq libumaptest_static) + + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) + + install(TARGETS rwseq + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + RUNTIME DESTINATION bin ) +else() + message("Skpping rwseq, OpenMP required") +endif() + diff --git a/tests/rwseq/options.cpp b/tests/rwseq/options.cpp new file mode 100644 index 00000000..256143c2 --- /dev/null +++ b/tests/rwseq/options.cpp @@ -0,0 +1,81 @@ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include // cout/cerr +#include // getopt() +#include // duh... +#include "options.h" +#include "umap.h" + +static char const* FILENAME = "/tmp/abc"; + +using namespace std; + +static void usage(char* pname) +{ + cerr + << "Usage: " << pname << " [Options...]\n\n" + << " --noread - Only perform write, not read\n" + << " --help - This message\n" + << " -f [backing file name] - default: " << FILENAME << endl; + exit(1); +} + +void getoptions(options_t& testops, int& argc, char **argv) +{ + int c; + char *pname = argv[0]; + + testops.fn=FILENAME; + testops.noread = 0; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"noread", no_argument, &testops.noread, 1 }, + {"help", no_argument, NULL, 0 }, + {0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "f:", long_options, &option_index); + if (c == -1) + break; + + switch(c) { + case 0: + if (long_options[option_index].flag != 0) + break; + + usage(pname); + break; + + case 'f': + testops.fn = optarg; + break; + + default: + usage(pname); + } + } + + if (optind < argc) { + cerr << "Unknown Arguments: "; + while (optind < argc) + cerr << "\"" << argv[optind++] << "\" "; + cerr << endl; + usage(pname); + } +} + +long umt_getpagesize(void) +{ + long page_size = sysconf(_SC_PAGESIZE); + if (page_size == -1) { + perror("sysconf/page_size"); + exit(1); + } + return page_size; +} + diff --git a/tests/rwseq/options.h b/tests/rwseq/options.h new file mode 100644 index 00000000..ae3e7631 --- /dev/null +++ b/tests/rwseq/options.h @@ -0,0 +1,12 @@ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _OPTIONS_H +#define _OPTIONS_H +#include + +typedef struct { + char const* fn; // Backing file name + int noread; +} options_t; + +void getoptions(options_t&, int&, char **argv); +#endif // _OPTIONS_H diff --git a/tests/rwseq/rwseq.cpp b/tests/rwseq/rwseq.cpp new file mode 100644 index 00000000..0e71a2b9 --- /dev/null +++ b/tests/rwseq/rwseq.cpp @@ -0,0 +1,96 @@ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include +//#include +//#include +//#include +#include +//#include +//#include +//#include +//#include // sched_getcpu() +#include +#include +#include + +#include "umap.h" +#include "options.h" +#include "umaptest.h" + +uint64_t g_count = 0; +using namespace std; + +class pageiotest { +public: + pageiotest(int _ac, char** _av): pagesize{umt_getpagesize()} { + getoptions(options, _ac, _av); + + umt_options.iodirect = 1; + umt_options.usemmap = 0; + umt_options.noinit = 0; + umt_options.filename = options.fn; + + maphandle = umt_openandmap(&umt_options, pagesize, &base_addr); + assert(maphandle != NULL); + } + + ~pageiotest( void ) { + umt_closeandunmap(&umt_options, pagesize, base_addr, maphandle); + } + + void start( void ) { + reader = new thread{&pageiotest::read, this}; + writer = new thread{&pageiotest::write, this}; + } + + void stop( void ) { + reader->join(); + writer->join(); + } + +private: + thread *reader; + thread *writer; + thread *monitor; + + umt_optstruct_t umt_options; + options_t options; + long pagesize; + void* base_addr; + void* maphandle; + + void read( void ) { + if (options.noread) { + cout << "Skipping read, only writes will occur\n"; + return; + } + + uint64_t* p = (uint64_t*)base_addr; + + cout << "Reading from: " << p << endl; + g_count = *p; // Won't return from this until AFTER umap handler, umap hanlder will sleep for 5 seconds + cout << "Read of " << p << " returned " << g_count << endl; + } + + void write( void ) { + uint64_t* p = (uint64_t*)base_addr; + + sleep(1); + cout << "Writing 12345678 to: " << p << endl; + *p = 12345678; + sleep(2); + cout << "Writing 87654321 to: " << p << endl; + *p = 87654321; + sleep(10); + cout << "Writing 1010101010 to: " << p << endl; + *p = 1010101010; + } +}; + +int main(int argc, char **argv) +{ + pageiotest test{argc, argv}; + test.start(); + test.stop(); + + return 0; +} diff --git a/tests/tools/adjust_free_mem b/tests/tools/adjust_free_mem new file mode 100755 index 00000000..37d55959 --- /dev/null +++ b/tests/tools/adjust_free_mem @@ -0,0 +1,82 @@ +#!/bin/bash +function usage { + echo "Usage:" + echo "$0 Size - Size is the number of GB to adjust free memory to." + exit 1 +} + +function amounttowaste { + m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` + echo $m + fm=$(((${m}/1024)/1024)) + waste=$((${fm}-${memtoleave})) + echo $fm GB Available, Wasting $waste GB +} + +function setuptmpfs { + if [ ! -d /mnt/tmpfs ]; then + sudo mkdir -p /mnt/tmpfs + fi + + # Unmount / Reset of already mounted + fs=`stat -f -c '%T' /mnt/tmpfs` + + if [ "$fs" = "tmpfs" ]; then + echo "Resetting tmpfs" + sudo umount /mnt/tmpfs + fi + + fs=`stat -f -c '%T' /mnt/tmpfs` + if [ "$fs" != "tmpfs" ]; then + if [ ! -d /mnt/tmpfs ]; then + sudo mkdir -p /mnt/tmpfs + fi + sudo chmod go+rwx /mnt/tmpfs + sudo mount -t tmpfs -o size=600g tmpfs /mnt/tmpfs + fs=`stat -f -c '%T' /mnt/tmpfs` + echo "/mnt/tmpfs mounted as: $fs" + else + echo "Unable to reset /mnt/tmpfs, exiting" + exit 1 + fi +} + +function drop_page_cache { + echo "Dropping page cache" + sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches' +} + +function disable_swap { + echo "Disabling swap" + sudo swapoff -a +} + +function turn_off_readahead { + fs=`mount | grep intel | cut -d " " -f 1` + sudo blockdev --setra 0 $fs + ra=`sudo blockdev --getra $fs` + echo "Read ahead set to $ra for $fs" +} + +function waste_memory { + echo "Wasting $waste GB of memory" + + #echo numactl -i 1 dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) + #numactl -i 1 dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) + echo dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) + dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) +} + +if [ $# -ne 1 ]; then + echo "Bad argument count: $#" + usage +fi + +memtoleave=$1 + +turn_off_readahead +disable_swap +setuptmpfs +drop_page_cache +amounttowaste +waste_memory diff --git a/tests/tools/flush_cache b/tests/tools/flush_cache new file mode 100755 index 00000000..12e34c44 --- /dev/null +++ b/tests/tools/flush_cache @@ -0,0 +1,29 @@ +#!/bin/bash +function free_mem { + m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` + fm=$(((${m}/1024)/1024)) + echo $fm GB Free +} + +function drop_page_cache { + echo "Dropping page cache" + sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches' +} + +function disable_swap { + echo "Disabling swap" + sudo swapoff -a +} + +function turn_off_readahead { + fs=`mount | grep intel | cut -d " " -f 1` + sudo blockdev --setra 0 $fs + ra=`sudo blockdev --getra $fs` + echo "Read ahead set to $ra for $fs" +} + +free_mem +disable_swap +turn_off_readahead +drop_page_cache +free_mem diff --git a/tests/tools/freemem b/tests/tools/freemem new file mode 100755 index 00000000..4ac531e3 --- /dev/null +++ b/tests/tools/freemem @@ -0,0 +1,4 @@ +#!/bin/bash +m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` +fm=$(((${m}/1024)/1024)) +echo $fm GB Free diff --git a/tests/tools/waste_memory b/tests/tools/waste_memory new file mode 100755 index 00000000..2f99f766 --- /dev/null +++ b/tests/tools/waste_memory @@ -0,0 +1,82 @@ +#!/bin/bash +function usage { + echo "Usage:" + echo "$0 Size - Size is the number of GB to waste." + exit 1 +} + +function free_mem { + m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` + echo $m + fm=$(((${m}/1024)/1024)) + wm=$((${fm}-20)) + echo $fm GB Available, Wasting $wm GB +} + +function setuptmpfs { + if [ ! -d /mnt/tmpfs ]; then + sudo mkdir -p /mnt/tmpfs + fi + + # Unmount / Reset of already mounted + fs=`stat -f -c '%T' /mnt/tmpfs` + + if [ "$fs" = "tmpfs" ]; then + echo "Resetting tmpfs" + sudo umount /mnt/tmpfs + fi + + fs=`stat -f -c '%T' /mnt/tmpfs` + if [ "$fs" != "tmpfs" ]; then + if [ ! -d /mnt/tmpfs ]; then + sudo mkdir -p /mnt/tmpfs + fi + sudo chmod go+rwx /mnt/tmpfs + sudo mount -t tmpfs -o size=600g tmpfs /mnt/tmpfs + fs=`stat -f -c '%T' /mnt/tmpfs` + echo "/mnt/tmpfs mounted as: $fs" + else + echo "Unable to reset /mnt/tmpfs, exiting" + exit 1 + fi +} + +function drop_page_cache { + echo "Dropping page cache" + sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches' +} + +function disable_swap { + echo "Disabling swap" + sudo swapoff -a +} + +function turn_off_readahead { + fs=`mount | grep intel | cut -d " " -f 1` + sudo blockdev --setra 0 $fs + ra=`sudo blockdev --getra $fs` + echo "Read ahead set to $ra for $fs" +} + +function waste_memory { + echo "Wasting $waste GB of memory" + + #echo numactl -i 1 dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) + #numactl -i 1 dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) + echo dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) + dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) +} + +if [ $# -ne 1 ]; then + echo "Bad argument count: $#" + usage +fi + +waste=$1 + +setuptmpfs +drop_page_cache +disable_swap +turn_off_readahead +drop_page_cache +waste_memory diff --git a/tests/uffd_test/CMakeLists.txt b/tests/uffd_test/CMakeLists.txt new file mode 100644 index 00000000..a3d7e928 --- /dev/null +++ b/tests/uffd_test/CMakeLists.txt @@ -0,0 +1,23 @@ +project(uffd_test) + +FIND_PACKAGE( OpenMP REQUIRED ) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + add_executable(uffd_test uffd_test2.cpp) + + target_link_libraries(uffd_test libumap_static) + target_link_libraries(uffd_test libumaptest_static) + + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) + + install(TARGETS uffd_test + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + RUNTIME DESTINATION bin ) +else() + message("Skpping uffd_test, OpenMP required") +endif() + + diff --git a/uffd_test/uffd_test2.cpp b/tests/uffd_test/uffd_test2.cpp similarity index 67% rename from uffd_test/uffd_test2.cpp rename to tests/uffd_test/uffd_test2.cpp index 61361d80..f67d9d14 100644 --- a/uffd_test/uffd_test2.cpp +++ b/tests/uffd_test/uffd_test2.cpp @@ -33,7 +33,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include - #define NUMPAGES 10000 #define NUMTHREADS 1 #define BUFFERSIZE 100 @@ -42,11 +41,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #endif -extern "C"{ -#include "../uffd_handler/uffd_handler.h" - -volatile int stop_uffd_handler; -} +#include "umap.h" +#include "umaptest.h" static inline uint64_t getns(void) { @@ -56,43 +52,26 @@ static inline uint64_t getns(void) return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; } -typedef struct { - int numpages; - int numthreads; - int bufsize; - char *fn; -} optstruct_t; - -optstruct_t options; - int main(int argc, char **argv) { - long num_pages; - int uffd; + umt_optstruct_t options; long pagesize; int64_t totalbytes; - pthread_t uffd_thread; - int64_t arraysize; - void *base_mmap_array; + void *base_addr; int value=0; - pagesize = get_pagesize(); + void* maphandle; - options.numpages = NUMPAGES; - options.numthreads = NUMTHREADS; - options.bufsize= BUFFERSIZE; - options.fn = NULL; - num_pages= options.numpages; + pagesize = umt_getpagesize(); - base_mmap_array = mmap(NULL, options.numpages*pagesize, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); - if (base_mmap_array == MAP_FAILED) - { - perror("mmap"); - exit(1); - } + umt_getoptions(&options, argc, argv); + + totalbytes = options.numpages*pagesize; + + maphandle = umt_openandmap(&options, totalbytes, &base_addr); + assert(maphandle != NULL); - uint64_t* array = (uint64_t*) base_mmap_array; // feed it the mmaped region - uint64_t array_length = num_pages * 512; // in number of 8-byte integers. + uint64_t* array = (uint64_t*) base_addr; // feed it the mmaped region + uint64_t array_length = totalbytes/sizeof(int64_t); // in number of 8-byte integers. uint64_t experiment_count = 100000; // Size of experiment, number of accesses uint64_t batch_size = 1000; // Set a batch size MUST BE MULTIPLE OF experiment_count std::vector vec_random_indices; @@ -112,28 +91,8 @@ int main(int argc, char **argv) assert(latencies); memset(latencies,0,sizeof(uint64_t)*num_batches); - //getoptions(&options, argc, argv); - - //totalbytes = options.numpages*pagesize; - //openandmap(options.fn, totalbytes, p->fd, p->base_addr); - - // start the thread that will handle userfaultfd events - - stop_uffd_handler = 0; - - params_t *p = (params_t *)malloc(sizeof(params_t)); - p->base_addr = (void *)array; - p->pagesize = pagesize; - p->bufsize = options.bufsize; - p->faultnum = 0; - p->uffd = uffd_init(p->base_addr, pagesize, num_pages); - //fprintf(stdout, "%d pages, %d threads\n", options.numpages, options.numthreads); - pthread_create(&uffd_thread, NULL, uffd_handler, p); - - sleep(1); - omp_set_num_threads(options.numthreads); // Fetch indices in batches @@ -160,12 +119,8 @@ int main(int argc, char **argv) // CALC LATENCY & IOPS } - - printf("\n"); - stop_uffd_handler = 1; - pthread_join(uffd_thread, NULL); - //fprintf(stdout, "mode %llu\n", (unsigned long long)uffdio_register.mode); - uffd_finalize(p, options.numpages); + printf("%d\n", value); + umt_closeandunmap(&options, totalbytes, base_addr, maphandle); for (long i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // optind +#include +#include +#include + +#ifdef _OPENMP +#include +#endif + +#include "umap.h" +#include "umaptest.h" + +#define handle_error_en(en, msg) \ + do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) + +void cpu_setcpu(int cpu) +{ + int s; + cpu_set_t cpuset; + pthread_t thread; + + thread = pthread_self(); + + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + + s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); + if (s != 0) + handle_error_en(s, "pthread_setaffinity_np"); + + /* Check the actual affinity mask assigned to the thread */ + + s = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset); + if (s != 0) + handle_error_en(s, "pthread_getaffinity_np"); +} +static inline uint64_t getns(void) +{ + struct timespec ts; + int ret = clock_gettime(CLOCK_MONOTONIC, &ts); + assert(ret == 0); + return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; +} + +void initdata(uint64_t *region, int64_t rlen) { + fprintf(stdout, "initdata: %p, %ld\n", region, rlen); +#pragma omp parallel for + for(int64_t i=0; i< rlen; ++i) { + region[i] = (uint64_t) (rlen - i); + } +} + +int main(int argc, char **argv) +{ + umt_optstruct_t options; + long pagesize; + int64_t totalbytes; + uint64_t arraysize; + void* base_addr; + void* maphandle; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution rnd_int(0, 39); + + pagesize = umt_getpagesize(); + + umt_getoptions(&options, argc, argv); + + omp_set_num_threads(options.numthreads); + + totalbytes = options.numpages*pagesize; + maphandle = umt_openandmap(&options, totalbytes, &base_addr); + assert(maphandle != NULL); + + fprintf(stdout, "%lu pages, %lu threads\n", options.numpages, options.numthreads); + + uint64_t *arr = (uint64_t *) base_addr; + arraysize = totalbytes/sizeof(int64_t); + + uint64_t start = getns(); + if ( !options.noinit ) { + // init data + initdata(arr, arraysize); + fprintf(stdout, "Init took %f us\n", (double)(getns() - start)/1000000.0); + } + + const int testpages = 400; + + if ( !options.initonly ) + { + std::vector cpus{0, 10, 20, 30}; + + start = getns(); +#pragma omp parallel for + for (uint64_t page = 0; page < options.numpages - testpages; page += testpages) { + uint64_t sum = 0; + + //cpu_setcpu(10); + for (int x = 0; x < testpages; x++) { + uint64_t* p = &arr[(page+x)*(pagesize/sizeof(uint64_t*))]; + sum += *p; + } + + cpu_setcpu(rnd_int(gen)); + + //cpu_setcpu(30); + for (int x = 0; x < testpages; ++x) { + uint64_t* p = &arr[(page+x)*(pagesize/sizeof(uint64_t*))]; + *p = sum; + } + } + + fprintf(stdout, "test took %f us\n", (double)(getns() - start)/1000000.0); + } + + umt_closeandunmap(&options, totalbytes, base_addr, maphandle); + + return 0; +} diff --git a/tests/umapmillions/CMakeLists.txt b/tests/umapmillions/CMakeLists.txt new file mode 100644 index 00000000..1845d3c9 --- /dev/null +++ b/tests/umapmillions/CMakeLists.txt @@ -0,0 +1,23 @@ +project(umapmillions) + +FIND_PACKAGE( OpenMP REQUIRED ) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + add_executable(umapmillions umapmillions.cpp) + + target_link_libraries(umapmillions libumap_static) + target_link_libraries(umapmillions libumaptest_static) + + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) + + install(TARGETS umapmillions + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + RUNTIME DESTINATION bin ) +else() + message("Skpping umapmillions, OpenMP required") +endif() + + diff --git a/tests/umapmillions/umapmillions.cpp b/tests/umapmillions/umapmillions.cpp new file mode 100644 index 00000000..c290b5b0 --- /dev/null +++ b/tests/umapmillions/umapmillions.cpp @@ -0,0 +1,98 @@ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +// uffd sort benchmark + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // optind +#include +#include +#include + +#include + +#include "umap.h" +#include "umaptest.h" + +static const uint64_t IndexesSize = 20000000; +static uint64_t* Indexes; + +// We initilize an array with a random set of indexes into our GIANT 600GB array +void initdata( uint64_t totalbytes ) +{ + Indexes = new uint64_t [IndexesSize]; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution rnd_int(0, totalbytes-1); +#pragma omp parallel for + for(uint64_t i = 0; i < IndexesSize; ++i) + Indexes[i] = rnd_int(gen); +} + +static inline uint64_t getns(void) +{ + struct timespec ts; + int ret = clock_gettime(CLOCK_MONOTONIC, &ts); + assert(ret == 0); + return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; +} + +int main(int argc, char **argv) +{ + umt_optstruct_t options; + long pagesize; + uint64_t totalbytes; + void* base_addr; + void* maphandle; + + pagesize = umt_getpagesize(); + umt_getoptions(&options, argc, argv); + omp_set_num_threads(options.numthreads); + + totalbytes = options.numpages*pagesize; + maphandle = umt_openandmap(&options, totalbytes, &base_addr); + assert(maphandle != NULL); + + fprintf(stdout, "%lu GB %lu pages, %lu threads\n", totalbytes/1024/1024/1024, options.numpages, options.numthreads); + + char *arr = (char *) base_addr; + + uint64_t start = getns(); + initdata(totalbytes); + fprintf(stdout, "Init took %f us\n", (double)(getns() - start)/1000000.0); + + start = getns(); +#pragma omp parallel for + for(uint64_t i = 0; i < IndexesSize; ++i) + arr[Indexes[i]] += 1; + + uint64_t end = getns(); + fprintf(stdout, "%lu updates took %f seconds, %f updates per second\n", + IndexesSize, + (double)(end - start)/100000000.0, + (double)IndexesSize / (double)((double)(end - start)/100000000.0) + ); + + umt_closeandunmap(&options, totalbytes, base_addr, maphandle); + + return 0; +} diff --git a/tests/umapsort/.gitignore b/tests/umapsort/.gitignore deleted file mode 100644 index d3d01b9c..00000000 --- a/tests/umapsort/.gitignore +++ /dev/null @@ -1 +0,0 @@ -umapsort diff --git a/tests/umapsort/CMakeLists.txt b/tests/umapsort/CMakeLists.txt index 4dbd749e..f7161c72 100644 --- a/tests/umapsort/CMakeLists.txt +++ b/tests/umapsort/CMakeLists.txt @@ -2,6 +2,16 @@ project(umapsort) FIND_PACKAGE( OpenMP REQUIRED ) if(OPENMP_FOUND) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/perftest_mmap.sh" + "${CMAKE_CURRENT_BINARY_DIR}/perftest_mmap.sh" + COPYONLY + ) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/perftest_umap.sh" + "${CMAKE_CURRENT_BINARY_DIR}/perftest_umap.sh" + COPYONLY + ) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") diff --git a/tests/umapsort/mmapsort.cpp b/tests/umapsort/mmapsort.cpp deleted file mode 100644 index ef0ef434..00000000 --- a/tests/umapsort/mmapsort.cpp +++ /dev/null @@ -1,309 +0,0 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* - * Copyright (c) 2013, Lawrence Livermore National Security, LLC. - * Produced at the Lawrence Livermore National Laboratory. - * Written by Roger Pearce . - * LLNL-CODE-624712. - * All rights reserved. - * - * This file is part of LRIOT, Version 1.0. - * For details, see https://computation.llnl.gov/casc/dcca-pub/dcca/Downloads.html - * - * Please also read this link – Additional BSD Notice. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * • Redistributions of source code must retain the above copyright notice, this - * list of conditions and the disclaimer below. - * - * • Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the disclaimer (as noted below) in the - * documentation and/or other materials provided with the distribution. - * - * • Neither the name of the LLNS/LLNL nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, - * THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Additional BSD Notice - * - * 1. This notice is required to be provided under our contract with the - * U.S. Department of Energy (DOE). This work was produced at Lawrence Livermore - * National Laboratory under Contract No. DE-AC52-07NA27344 with the DOE. - * - * 2. Neither the United States Government nor Lawrence Livermore National - * Security, LLC nor any of their employees, makes any warranty, express or - * implied, or assumes any liability or responsibility for the accuracy, - * completeness, or usefulness of any information, apparatus, product, or - * process disclosed, or represents that its use would not infringe - * privately-owned rights. - * - * 3. Also, reference herein to any specific commercial products, process, or - * services by trade name, trademark, manufacturer or otherwise does not - * necessarily constitute or imply its endorsement, recommendation, or favoring - * by the United States Government or Lawrence Livermore National Security, - * LLC. The views and opinions of authors expressed herein do not necessarily - * state or reflect those of the United States Government or Lawrence Livermore - * National Security, LLC, and shall not be used for advertising or product - * endorsement purposes. - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _OPENMP -#include -#endif - -#ifdef UFFD -extern "C" { -#include "../uffd_handler/uffd_handler.h" - -volatile int stop_uffd_handler; -} -#endif - -double get_wtime(); - -void create_files (const char* base_fname, int fnum, uint64_t file_size, bool do_fallocate); -void init_data (const char* base_fname, int fnum, uint64_t file_size); -void sort_data (const char* base_fname, int fnum, uint64_t file_size); -void validate_data(const char* base_fname, int fnum, uint64_t file_size); - - -int main(int argc, char** argv) { - - // for uffd - int uffd; - pthread_t uffd_thread; - - if(argc != 4) { - std::cerr << "Usage: " << argv[0] << " " < rnd_int; - for(int i=0; i /proc/sys/vm/drop_caches +} + +function disable_swap { + echo "Disabling swap" + swapoff -av +} + +function set_readahead { + fs=`mount | grep intel | cut -d " " -f 1` + blockdev --setra $readahead $fs + ra=`blockdev --getra $fs` + echo "Read ahead set to $ra for $fs" +} + +function amounttowaste { + m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` + echo $m + fm=$(((${m}/1024)/1024)) + waste=$((${fm}-${memtoleave})) + echo $fm GB Available, Wasting $waste GB +} + +function setuptmpfs { + if [ ! -d /mnt/tmpfs ]; then + mkdir -p /mnt/tmpfs + fi + + # Unmount / Reset of already mounted + fs=`stat -f -c '%T' /mnt/tmpfs` + + if [ "$fs" = "tmpfs" ]; then + echo "Resetting tmpfs" + umount /mnt/tmpfs + fi + + fs=`stat -f -c '%T' /mnt/tmpfs` + if [ "$fs" != "tmpfs" ]; then + if [ ! -d /mnt/tmpfs ]; then + mkdir -p /mnt/tmpfs + fi + chmod go+rwx /mnt/tmpfs + mount -t tmpfs -o size=600g tmpfs /mnt/tmpfs + fs=`stat -f -c '%T' /mnt/tmpfs` + echo "/mnt/tmpfs mounted as: $fs" + else + echo "Unable to reset /mnt/tmpfs, exiting" + exit 1 + fi +} + +function waste_memory { + echo "Wasting $waste GB of memory" + echo dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) + dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) +} + +memtoleave=$((64+6)) +readahead=256 + +set_readahead +disable_swap +setuptmpfs +drop_page_cache +amounttowaste +waste_memory + +for t in 128 64 32 16 +do + rm -f /mnt/intel/sort_perf_data + drop_page_cache + free_mem + cmd="./umapsort --usemmap --directio -f /mnt/intel/sort_perf_data -p $(((96*1024*1024*1024)/4096)) -n 1 -b $(((64*1024*1024*1024)/4096)) -t $t" + date + echo $cmd + time sh -c "$cmd" +done diff --git a/tests/umapsort/perftest_umap.sh b/tests/umapsort/perftest_umap.sh new file mode 100755 index 00000000..fbcf91ac --- /dev/null +++ b/tests/umapsort/perftest_umap.sh @@ -0,0 +1,86 @@ +#!/bin/bash +function free_mem { + m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` + fm=$(((${m}/1024)/1024)) + echo $fm GB Free +} + +function drop_page_cache { + echo "Dropping page cache" + echo 3 > /proc/sys/vm/drop_caches +} + +function disable_swap { + echo "Disabling swap" + swapoff -av +} + +function set_readahead { + fs=`mount | grep intel | cut -d " " -f 1` + blockdev --setra $readahead $fs + ra=`blockdev --getra $fs` + echo "Read ahead set to $ra for $fs" +} + +function amounttowaste { + m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` + echo $m + fm=$(((${m}/1024)/1024)) + waste=$((${fm}-${memtoleave})) + echo $fm GB Available, Wasting $waste GB +} + +function setuptmpfs { + if [ ! -d /mnt/tmpfs ]; then + mkdir -p /mnt/tmpfs + fi + + # Unmount / Reset of already mounted + fs=`stat -f -c '%T' /mnt/tmpfs` + + if [ "$fs" = "tmpfs" ]; then + echo "Resetting tmpfs" + umount /mnt/tmpfs + fi + + fs=`stat -f -c '%T' /mnt/tmpfs` + if [ "$fs" != "tmpfs" ]; then + if [ ! -d /mnt/tmpfs ]; then + mkdir -p /mnt/tmpfs + fi + chmod go+rwx /mnt/tmpfs + mount -t tmpfs -o size=600g tmpfs /mnt/tmpfs + fs=`stat -f -c '%T' /mnt/tmpfs` + echo "/mnt/tmpfs mounted as: $fs" + else + echo "Unable to reset /mnt/tmpfs, exiting" + exit 1 + fi +} + +function waste_memory { + echo "Wasting $waste GB of memory" + echo dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) + dd if=/dev/zero of=/mnt/tmpfs/${waste}GB bs=4096 count=$((${waste}*256*1024)) +} + +memtoleave=$((64+6)) +readahead=0 + +set_readahead +disable_swap +setuptmpfs +drop_page_cache +amounttowaste +waste_memory + +for t in 128 64 32 16 +do + rm -f /mnt/intel/sort_perf_data + drop_page_cache + free_mem + cmd="./umapsort --directio -f /mnt/intel/sort_perf_data -p $(((96*1024*1024*1024)/4096)) -n 1 -b $(((64*1024*1024*1024)/4096)) -t $t" + date + echo $cmd + time sh -c "$cmd" +done diff --git a/tests/umapsort/umapsort.cpp b/tests/umapsort/umapsort.cpp index b577f264..13df5dda 100644 --- a/tests/umapsort/umapsort.cpp +++ b/tests/umapsort/umapsort.cpp @@ -1,19 +1,11 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ +/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // uffd sort benchmark +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include #include #include #include @@ -32,10 +24,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include // optind #include - -#ifdef _OPENMP -#include -#endif +#include +#include #ifdef _OPENMP #include @@ -58,7 +48,7 @@ void initdata(uint64_t *region, int64_t rlen) { std::mt19937 gen(rd()); std::uniform_int_distribution rnd_int; #pragma omp parallel for - for(int i=0; i< rlen; ++i) { + for(int64_t i=0; i< rlen; ++i) { region[i] = (uint64_t) (rlen - i);// rnd_int(gen); //region[i] = rnd_int(gen); } @@ -99,41 +89,23 @@ int main(int argc, char **argv) umt_optstruct_t options; long pagesize; int64_t totalbytes; - pthread_t uffd_thread; uint64_t arraysize; - // parameter block to uffd - params_t *p = (params_t *) malloc(sizeof(params_t)); + void* base_addr; + void* umaphandle; - pagesize = get_pagesize(); + pagesize = umt_getpagesize(); - umt_getoptions(options, argc, argv); + umt_getoptions(&options, argc, argv); + + omp_set_num_threads(options.numthreads); totalbytes = options.numpages*pagesize; - umt_openandmap(options, totalbytes, p->fd, p->base_addr); + umaphandle = umt_openandmap(&options, totalbytes, &base_addr); + assert(umaphandle != NULL); - if ( ! options.usemmap ) { - fprintf(stdout, "Using UserfaultHandler Buffer\n"); - - // start the thread that will handle userfaultfd events - p->pagesize = pagesize; - - p->bufsize = options.bufsize; - - p->faultnum = 0; - p->uffd = uffd_init(p->base_addr, pagesize, options.numpages); - - pthread_create(&uffd_thread, NULL, uffd_handler, p); - sleep(1); - } - else { - fprintf(stdout, "Using vanilla mmap()\n"); - } - fprintf(stdout, "%lu pages, %lu threads\n", options.numpages, options.numthreads); - omp_set_num_threads(options.numthreads); - - uint64_t *arr = (uint64_t *) p->base_addr; + uint64_t *arr = (uint64_t *) base_addr; arraysize = totalbytes/sizeof(int64_t); uint64_t start = getns(); @@ -143,9 +115,10 @@ int main(int argc, char **argv) fprintf(stdout, "Init took %f us\n", (double)(getns() - start)/1000000.0); } - if ( !options.initonly ) { + if ( !options.initonly ) + { start = getns(); - std::sort(arr, &arr[arraysize]); + __gnu_parallel::sort(arr, &arr[arraysize],__gnu_parallel::quicksort_tag()); fprintf(stdout, "Sort took %f us\n", (double)(getns() - start)/1000000.0); start = getns(); @@ -153,11 +126,7 @@ int main(int argc, char **argv) fprintf(stdout, "Validate took %f us\n", (double)(getns() - start)/1000000.0); } - if ( ! options.usemmap ) { - stop_umap_handler(); - pthread_join(uffd_thread, NULL); - uffd_finalize(p, options.numpages); - } + umt_closeandunmap(&options, totalbytes, base_addr, umaphandle); return 0; } diff --git a/tests/umaptest/CMakeLists.txt b/tests/umaptest/CMakeLists.txt index d7d22c45..b4066f3a 100644 --- a/tests/umaptest/CMakeLists.txt +++ b/tests/umaptest/CMakeLists.txt @@ -8,6 +8,7 @@ if(OPENMP_FOUND) add_executable(umaptest test.c) target_link_libraries(umaptest libumap_static) + target_link_libraries(umaptest libumaptest_static) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) diff --git a/tests/umaptest/test.c b/tests/umaptest/test.c index 49988d81..b37ebe75 100644 --- a/tests/umaptest/test.c +++ b/tests/umaptest/test.c @@ -36,8 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #endif #include "umap.h" - -volatile int stop_uffd_handler; +#include "umaptest.h" static inline uint64_t getns(void) { @@ -47,106 +46,26 @@ static inline uint64_t getns(void) return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; } -typedef struct { - int numpages; - int numthreads; - int bufsize; - char *fn; -} optstruct_t; - -optstruct_t options; - -void getoptions(optstruct_t *options, int argc, char *argv[]) { - - int c; - - while ((c = getopt(argc, argv, "p:t:f:b:")) != -1) { - - switch(c) { - case 'p': - options->numpages = atoi(optarg); - if (options->numpages > 0) - break; - else goto R0; - case 't': - options->numthreads = atoi(optarg); - if (options->numthreads > 0) - break; - else goto R0; - case 'b': - options->bufsize = atoi(optarg); - if (options->bufsize > 0) - break; - else goto R0; - case 'f': - options->fn = optarg; - break; - R0: - default: - fprintf(stdout, "Usage: %s ", argv[0]); - fprintf(stdout, " -p [number of pages], default: %d ", NUMPAGES); - fprintf(stdout, " -t [number of threads], default: %d ", NUMTHREADS); - fprintf(stdout, " -b [page buffer size], default: %d ", BUFFERSIZE); - - fprintf(stdout, " -f [file name], name of existing file to read pages from, default no -f\n"); - exit(1); - } - } -} - int main(int argc, char **argv) { long pagesize; long num_pages; void *region; - pthread_t uffd_thread; - - pagesize = get_pagesize(); + umt_optstruct_t options; + void* maphandle; - options.numpages = NUMPAGES; - options.numthreads = NUMTHREADS; - options.bufsize = BUFFERSIZE; - options.fn = NULL; + pagesize = umt_getpagesize(); - getoptions(&options, argc, argv); + umt_getoptions(&options, argc, argv); num_pages = options.numpages; omp_set_num_threads(options.numthreads); - // allocate a memory region to be managed by userfaultfd - region = mmap(NULL, pagesize * num_pages, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); - //MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (region == MAP_FAILED) { - perror("mmap"); - exit(1); - } - - // start the thread that will handle userfaultfd events - - stop_uffd_handler = 0; - - params_t *p = malloc(sizeof(params_t)); - p->uffd = uffd_init(region, pagesize, num_pages); - p->pagesize = pagesize; - p->bufsize = options.bufsize; - p->faultnum = 0; - p->base_addr = region; - fprintf(stdout, "%ld pages, %d threads\n", num_pages, options.numthreads); - if (!options.fn) - options.fn = "/tmp/abc.0"; - fprintf(stdout, "USEFILE enabled %s\n", options.fn); - // TODO (mjm) - Why doesn't O_DIRECT work?!? - //p->fd = open(options.fn, O_RDWR|O_DIRECT, S_IRUSR|S_IWUSR);// | O_DIRECT); - p->fd = open(options.fn, O_RDWR, S_IRUSR|S_IWUSR);// | O_DIRECT); - if (p->fd == -1) { - perror("file open"); - exit(1); - } - - pthread_create(&uffd_thread, NULL, uffd_handler, p); - //printf("total number of fault:%d\n",faultnum); - sleep(1); + maphandle = umt_openandmap(&options, options.numpages*pagesize, ®ion); + assert(maphandle != NULL); + + fprintf(stdout, "%ld pages, %lu threads\n", num_pages, options.numthreads); + fprintf(stdout, "USEFILE enabled %s\n", options.filename); // storage for the latencies for each page int num_batches = 10; @@ -166,60 +85,25 @@ int main(int argc, char **argv) uint64_t start = getns(); for (long j=0;jfaultnum,value); - - uffd_finalize(p, num_pages); + + umt_closeandunmap(&options, options.numpages*pagesize, region, maphandle); for (long i = 0; i < num_batches; i++) { fprintf(stdout, "%llu\n", (unsigned long long)latencies[i]); } free(latencies); - munmap(region, pagesize * num_pages); return 0; } diff --git a/thirdparty_licenses.md b/thirdparty_licenses.md new file mode 100644 index 00000000..0503939c --- /dev/null +++ b/thirdparty_licenses.md @@ -0,0 +1,6 @@ +# Third Party Licenses +==================== + +## C and C++ Tests +- *qfits*: ftp://ftp.eso.org/pub/qfits/ (GPL License) + diff --git a/uffd_test/Makefile b/uffd_test/Makefile deleted file mode 100755 index 8261cdba..00000000 --- a/uffd_test/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -CXX=g++ -CC=gcc - -TARGET=uffd_test2 - -DEFS=-D_GNU_SOURCE - -ifdef D - SEP := , - DEFS += $(patsubst %,-D%,$(subst $(SEP), ,$(D))) -endif - -CXXFLAGS= $(DEFS) -std=c++11 -g -lpthread -fopenmp # -O3 - -LDFLAGS=-lpthread -fopenmp - -OS := $(shell uname) -ifneq ($(OS), Darwin) -LDFLAGS+= -lrt -endif - -UFFDLOC = ../uffd_handler -UFFDLD= -lrt -lssl -lcrypto -CFLAGS=-std=gnu99 $(DEFS) -g -lpthread # -O3 -HEADERS=$(UFFDLOC)/uffd_handler.h -TARGETOBJ=$(TARGET).o uffd_handler.o - -all: $(TARGET) - -$(TARGET): $(TARGETOBJ) - $(LINK.cpp) $(TARGETOBJ) $(DEFS) $(LDFLAGS) $(UFFDLD) -o $@ -uffd_handler.o: $(HEADERS) - $(CC) $(UFFDLOC)/uffd_handler.c $(CFLAGS) $(DEFS) -c -o $@ -$(TARGET).o: $(TARGET).cpp - $(CXX) $< $(CXXFLAGS) -c -o $@ -clean: - rm -f $(TARGET) $(TARGET).o uffd_handler.o