Program Listing for File pfbenchmark.cpp

Return to documentation for file (pfbenchmark/pfbenchmark.cpp)

/* 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 program is a benchmark for UMAP page fault handling.  The specied backing file
 * for UMAP is accessed a page at a time indirectly by accessing pages that have been
 * mapped to the file.  Read accesses to memory will determine the average nanosecond
 * cost for servicing a READ PAGE FAULT and write accesses to memory will determine
 * the average cost of WRITE PAGE FAULTs.
 *
 * A number of threads may be specified on the command line to enable concurrent I/O
 * access within the file.  Further, the file may be accessed sequentially (default)
 * or randomly (if "--shuffle" command line option is specified).
 */

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif // _GNU_SOURCE

#include <iostream>
#include <chrono>
#include <omp.h>
#include <string.h>
#include <vector>
#include <random>
#include <algorithm>
#include <iterator>

#include "umap/umap.h"
#include "../utility/umap_file.hpp"
#include "../utility/commandline.hpp"

using namespace std;
using namespace chrono;
static bool usemmap = false;
static uint64_t pagesize;
static uint64_t page_step;
static uint64_t* glb_array;
static utility::umt_optstruct_t options;
static uint64_t pages_to_access;
vector<uint64_t> shuffled_indexes;

void do_write_pages(uint64_t page_step, uint64_t pages)
{
#pragma omp parallel for
  for (uint64_t i = 0; i < pages; ++i) {
    uint64_t myidx = shuffled_indexes[i];
    glb_array[myidx * page_step] = (myidx * page_step);
  }
}

uint64_t do_read_pages(uint64_t page_step, uint64_t pages)
{
  uint64_t x;

  // Weird logic to make sure that compiler doesn't optimize out our read of glb_array[i]
#pragma omp parallel for
  for (uint64_t i = 0; i < pages; ++i) {
    uint64_t myidx = shuffled_indexes[i];
    x = glb_array[myidx * page_step];

    if (x != (myidx * page_step)) {
      cout << __FUNCTION__ << "glb_array[" << myidx * page_step << "]: (" << glb_array[myidx*page_step] << ") != " << myidx * page_step << "\n";
      exit(1);
    }
  }

  return x;
}

uint64_t do_read_modify_write_pages(uint64_t page_step, uint64_t pages)
{
  uint64_t x;

  // Weird logic to make sure that compiler doesn't optimize out our read of glb_array[i]
#pragma omp parallel for
  for (uint64_t i = 0; i < pages; ++i) {
    uint64_t myidx = shuffled_indexes[i];
    x = glb_array[myidx * page_step];

    if (x != (myidx * page_step)) {
      cout << __FUNCTION__ << "glb_array[" << myidx * page_step << "]: (" << x << ") != " << myidx * page_step << "\n";
      exit(1);
    }
    else {
      glb_array[myidx * page_step] = (myidx * page_step);
      x++;
    }
  }
  return x;
}

void print_stats( void )
{
  if (!usemmap) {
    struct umap_cfg_stats s;
    umap_cfg_get_stats(glb_array, &s);

    //cout << s.dirty_evicts << " Dirty Evictions\n";
    //cout << s.clean_evicts << " Clean Evictions\n";
    //cout << s.evict_victims << " Victims\n";
    //cout << s.wp_messages << " WP Faults\n";
    //cout << s.read_faults << " Read Faults\n";
    //cout << s.write_faults << " Write Faults\n";
    if (s.sigbus)
      cout << s.sigbus << " SIGBUS Signals\n";
    if (s.stuck_wp)
      cout << s.stuck_wp << " Stuck WP Workarounds\n";
    //cout << s.dropped_dups << " Dropped Duplicates\n";
  }
}

int read_test(int argc, char **argv)
{
  auto start_time = chrono::high_resolution_clock::now();
  do_read_pages(page_step, pages_to_access);
  auto end_time = chrono::high_resolution_clock::now();

  cout << ((options.usemmap == 1) ? "mmap" : "umap") << ","
      << (( options.shuffle == 1) ? "shuffle" : "seq") << ","
      << "read,"
      << options.numthreads << ","
      << options.uffdthreads << ","
      << chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count() / pages_to_access << "\n";

  return 0;
}

int write_test(int argc, char **argv)
{
  auto start_time = chrono::high_resolution_clock::now();
  do_write_pages(page_step, pages_to_access);
  auto end_time = chrono::high_resolution_clock::now();

  cout << ((options.usemmap == 1) ? "mmap" : "umap") << ","
      << (( options.shuffle == 1) ? "shuffle" : "seq") << ","
      << "write,"
      << options.numthreads << ","
      << options.uffdthreads << ","
      << chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count() / pages_to_access << "\n";

  return 0;
}

int read_modify_write_test(int argc, char **argv)
{
  auto start_time = chrono::high_resolution_clock::now();
  auto end_time = chrono::high_resolution_clock::now();

  start_time = chrono::high_resolution_clock::now();
  do_read_modify_write_pages(page_step, pages_to_access);
  end_time = chrono::high_resolution_clock::now();

  cout << ((options.usemmap == 1) ? "mmap" : "umap") << ","
      << (( options.shuffle == 1) ? "shuffle" : "seq") << ","
      << "rmw,"
      << options.numthreads << ","
      << options.uffdthreads << ","
      << chrono::duration_cast<chrono::nanoseconds>(end_time - start_time).count() / pages_to_access << "\n";

  return 0;
}

int main(int argc, char **argv)
{
  int rval = -1;
  std::random_device rd;
  std::mt19937 g(rd());

  umt_getoptions(&options, argc, argv);

  for (uint64_t i = 0; i < options.numpages; ++i)
    shuffled_indexes.push_back(i);

  pages_to_access = options.pages_to_access ? options.pages_to_access : options.numpages;

  if ( options.shuffle )
    std::shuffle(shuffled_indexes.begin(), shuffled_indexes.end(), g);

  options.initonly = 0;
  usemmap = (options.usemmap == 1);
  omp_set_num_threads(options.numthreads);
  pagesize = (uint64_t)utility::umt_getpagesize();
  page_step = pagesize/sizeof(uint64_t);

  glb_array = (uint64_t*) utility::map_in_file(options.filename, options.initonly,
      options.noinit, options.usemmap, pagesize * options.numpages);

  if (strcmp(argv[0], "pfbenchmark-read") == 0)
    rval = read_test(argc, argv);
  else if (strcmp(argv[0], "pfbenchmark-write") == 0)
    rval = write_test(argc, argv);
  else if (strcmp(argv[0], "pfbenchmark-readmodifywrite") == 0)
    rval = read_modify_write_test(argc, argv);
  else
    cerr << "Unknown test mode " << argv[0] << "\n";

  print_stats();
  utility::unmap_file(options.usemmap, pagesize * options.numpages, glb_array);
  return rval;
}