Allocator Accessibility

The Umpire library provides a variety of umpire::resource::MemoryResource s which can be used to create umpire::Allocator s depending on what’s available on your system. The resources are explained more on the Resources page.

Additionally, the platforms that Umpire supports is defined by the CAMP library. This means that there is also a selection of platforms for which an allocator can be associated with as well. For example, an Allocator created with the pinned memory resource can be used with the host, cuda, hip, or sycl platforms.

Because of these options, it can be difficult to trace not only which memory resource an allocator has been created with but also which allocators can be accessed by which platforms. Umpire has the memory resource trait, resource_type, to provide the ability to query which memory resource is associated with a particular allocator (See example here).

Additionally, Umpire has a function, is_accessible(Platform p, Allocator a), that determines if a particular allocator is accessible by a particular platform (See example here). The allocator_accessibility.cpp test checks what platforms are available and confirms that all memory resources which should be accessible to that platform can actually be accessed and used.

For example, if a umpire::Allocator, alloc, is created with the host memory resource and we want to know if it should be accessible from the omp_target CAMP platform, then we can use the is_accessible(Platform::omp_target, alloc) function and find that it should be accessible. The allocator_access.cpp file demonstrates this functionality for the host platform specifically.

Allocator Inaccessibility Configuration

On a different note, for those allocators that are deemed inaccessible, it may be useful to double check or confirm that the allocator can in fact NOT access memory on that given platform. In this case, the cmake flag, UMPIRE_ENABLE_INACCESSIBILITY_TESTS, will need to be turned on.

Build and Run Configuration

To build and run these files, either use uberenv or the appropriate cmake flags for the desired platform and then run ctest -T test -R allocator_accessibility_tests --output-on-failure for the test code and ./bin/alloc_access for the example code.

Note

The Developer’s Guide shows how to configure Umpire with uberenv to build with different CAMP platforms.

Below, the allocator_access.cpp code is shown to demonstrate how this functionality can be used during development.

//////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-24, Lawrence Livermore National Security, LLC and Umpire
// project contributors. See the COPYRIGHT file for details.
//
// SPDX-License-Identifier: (MIT)
//////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <string>

#include "umpire/Allocator.hpp"
#include "umpire/ResourceManager.hpp"
#include "umpire/Umpire.hpp"

bool is_accessible_from_host(umpire::Allocator a)
{
  if (umpire::is_accessible(umpire::Platform::host, a)) {
    std::cout << "The allocator, " << a.getName() << ", is accessible." << std::endl;
    return true;
  } else {
    std::cout << "The allocator, " << a.getName() << ", is _not_ accessible." << std::endl << std::endl;
    return false;
  }
}

/////////////////////////////////////////////////////////////////////////////////////////
// Depending on how Umpire has been set up, several different allocators could be accessible
// from the host CAMP platform. This example will create a list of all currently available
// allocators and then determine whether each can be accessed from the host platform.
//(To test other platforms, see allocator accessibility test.)
////////////////////////////////////////////////////////////////////////////////////////
int main()
{
  auto& rm = umpire::ResourceManager::getInstance();

  std::vector<std::string> allNames = rm.getResourceNames();
  std::vector<umpire::Allocator> alloc;

  ///////////////////////////////////////////////////
  // Create an allocator for each available type
  //////////////////////////////////////////////////
  std::cout << "Available allocators: ";
  for (auto a : allNames) {
    if (a.find("::") == std::string::npos) {
      alloc.push_back(rm.getAllocator(a));
      std::cout << a << " ";
    }
  }
  std::cout << std::endl;

  ///////////////////////////////////////////////////
  // Test accessibility
  ///////////////////////////////////////////////////
  std::cout << "Testing the available allocators for accessibility from the CAMP host platform:" << std::endl;
  const int size = 100;
  for (auto a : alloc) {
    if (is_accessible_from_host(a)) {
      int* data = static_cast<int*>(a.allocate(size * sizeof(int)));
      for (int i = 0; i < size; i++) {
        data[i] = i * i;
      }
      UMPIRE_ASSERT(data[size - 1] == (size - 1) * (size - 1) && "Inequality found in array that should be accessible");
    }
  }

  return 0;
}