From bc052f14432acebf5f42326366813297d70afc94 Mon Sep 17 00:00:00 2001 From: Sergei Vinogradov Date: Mon, 23 Aug 2021 18:53:40 +0300 Subject: [PATCH] Implemented Memory Provider for Heterogeneous memory. The idea is to add abstraction layer for heterogeneous memory. The interface is built on top of C++ 17 std::pmr::memory_resource interface. --- CMakeLists.txt | 8 +- .../HeteroMem/MemResourceProvider.cpp | 156 +++++++++++++++ .../HeteroMem/MemResourceProvider.h | 45 +++++ .../HeteroMem/MemResources/memory_resources.h | 178 ++++++++++++++++++ DataMgr/CMakeLists.txt | 1 + Tests/CMakeLists.txt | 2 +- scripts/install-memkind.sh | 44 +++++ scripts/mapd-deps-ubuntu.sh | 10 +- 8 files changed, 436 insertions(+), 8 deletions(-) create mode 100644 DataMgr/Allocators/HeteroMem/MemResourceProvider.cpp create mode 100644 DataMgr/Allocators/HeteroMem/MemResourceProvider.h create mode 100644 DataMgr/Allocators/HeteroMem/MemResources/memory_resources.h create mode 100755 scripts/install-memkind.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index a53b3332e70..3b669c8c58c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -332,6 +332,10 @@ else() endif() endif() +set(MEMKIND_REQUIRED_VERSION 1.11.0) +find_package(PkgConfig QUIET) +pkg_check_modules(memkind REQUIRED memkind>=${MEMKIND_REQUIRED_VERSION}) + set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) if(MSVC) @@ -830,11 +834,11 @@ add_custom_target(rerun_cmake ALL ) add_dependencies(omnisci_server rerun_cmake) -target_link_libraries(omnisci_server mapd_thrift thrift_handler ${MAPD_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS} ${CUDA_LIBRARIES} ${PROFILER_LIBS} ${ZLIB_LIBRARIES} ${LOCALE_LINK_FLAG}) +target_link_libraries(omnisci_server mapd_thrift thrift_handler ${MAPD_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS} ${CUDA_LIBRARIES} ${PROFILER_LIBS} ${ZLIB_LIBRARIES} ${LOCALE_LINK_FLAG} ${memkind_LIBRARIES}) target_link_libraries(initdb mapd_thrift thrift_handler ${MAPD_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS} ${CUDA_LIBRARIES} ${PROFILER_LIBS} ${ZLIB_LIBRARIES} ${BLOSC_LIBRARIES} - ${LOCALE_LINK_FLAG}) + ${LOCALE_LINK_FLAG} ${memkind_LIBRARIES}) macro(set_dpkg_arch arch_in arch_out) if("${arch_in}" STREQUAL "x86_64") diff --git a/DataMgr/Allocators/HeteroMem/MemResourceProvider.cpp b/DataMgr/Allocators/HeteroMem/MemResourceProvider.cpp new file mode 100644 index 00000000000..73c8bd68946 --- /dev/null +++ b/DataMgr/Allocators/HeteroMem/MemResourceProvider.cpp @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* Copyright (C) 2020 Intel Corporation. */ + +#include "DataMgr/Allocators/HeteroMem/MemResourceProvider.h" + +#include "Logger/Logger.h" + +// MemKind Allocator +#include "DataMgr/Allocators/HeteroMem/MemResources/memory_resources.h" + +#include + +#include +#include +#include +#include "memkind.h" + +#ifdef __APPLE__ +#include +#include +#endif + +namespace Buffer_Namespace { +using pmem_memory_resource_type = libmemkind::pmem::memory_resource; +using static_kind_memory_resource_type = libmemkind::static_kind::memory_resource; + +MemoryResourceProvider::MemoryResourceProvider() + : dram_mem_resource_(new static_kind_memory_resource_type(libmemkind::kinds::REGULAR)) + , mem_resources_(3, dram_mem_resource_.get()) { + if (memkind_check_available(MEMKIND_DAX_KMEM_ALL) == MEMKIND_SUCCESS) { + pmem_mem_resource_ = std::make_unique( + libmemkind::kinds::DAX_KMEM_ALL); + mem_resources_[CAPACITY] = pmem_mem_resource_.get(); + CHECK(mem_resources_[CAPACITY]); + LOG(INFO) << "KMEM DAX nodes are detected - will use it as a capacity pool"; + pmem_memory_size = SIZE_MAX; + } + initAvailableDRAMSize(); +} + +MemoryResourceProvider::MemoryResourceProvider(const std::string& pmem_path, size_t size) + : dram_mem_resource_(new static_kind_memory_resource_type(libmemkind::kinds::REGULAR)) + , mem_resources_(3, dram_mem_resource_.get()) { + initAvailableDRAMSize(); + initPmm(pmem_path, size); +} + +std::pmr::memory_resource* MemoryResourceProvider::get(const MemRequirements& req) const { + CHECK(req < mem_resources_.size()); + CHECK(mem_resources_[req]); + return mem_resources_[req]; +} + +size_t MemoryResourceProvider::getAvailableMemorySize( + std::pmr::memory_resource* mem_resource) const { + if (dram_mem_resource_.get() == mem_resource) { + return dram_memory_size; + } + + if (pmem_mem_resource_.get() == mem_resource) { + return pmem_memory_size; + } + + throw std::runtime_error("Memory type unidentified"); +} + +MemType MemoryResourceProvider::getMemoryType( + std::pmr::memory_resource* memory_resource) const { + if (dram_mem_resource_.get() == memory_resource) { + return DRAM; + } + if (pmem_mem_resource_.get() == memory_resource) { + return PMEM; + } + + throw std::runtime_error("Memory type unidentified"); +} + +std::vector MemoryResourceProvider::getOrderByBandwidth() + const { + CHECK(dram_mem_resource_); + std::vector res; + // TODO: Once we have HBM, we need to add it to the resulting vector first. + + res.emplace_back(dram_mem_resource_.get()); + if (pmem_mem_resource_) + res.emplace_back(pmem_mem_resource_.get()); + + return res; +} + +void MemoryResourceProvider::initPmm(const std::string& pmem_path, size_t size) { + if (isDAXPath(pmem_path)) { + LOG(INFO) << pmem_path << " is on DAX-enabled file system."; + } else { + LOG(WARNING) << pmem_path << " is not on DAX-enabled file system."; + } + + if (size == 0) { + initAvailablePMEMSize(pmem_path); + } else { + pmem_memory_size = size; + } + + pmem_mem_resource_ = + std::make_unique(pmem_path, pmem_memory_size); + mem_resources_[CAPACITY] = pmem_mem_resource_.get(); + CHECK(mem_resources_[CAPACITY]); +} + +bool MemoryResourceProvider::isDAXPath(const std::string& path) const { + int status = memkind_check_dax_path(path.c_str()); + if (status) { + return false; + } + + return true; +} + +void MemoryResourceProvider::initAvailablePMEMSize(std::string path) { + std::error_code ec; + const std::filesystem::space_info si = std::filesystem::space(path, ec); + if (!ec) { + pmem_memory_size = static_cast(si.capacity); + } else { + LOG(FATAL) << "Invalid pmem path: impossible to determine size of the pmem memory"; + return; + } +} + +void MemoryResourceProvider::initAvailableDRAMSize() { +// TODO: this implementation is incorrect for hetero memory +// Memkind will provide proper implementation soon. +// Will update this function once we have correct implementation from Memkind. +#ifdef __APPLE__ + int mib[2]; + size_t physical_memory; + size_t length; + // Get the Physical memory size + mib[0] = CTL_HW; + mib[1] = HW_MEMSIZE; + length = sizeof(size_t); + sysctl(mib, 2, &physical_memory, &length, NULL, 0); + dram_memory_size = physical_memory; +#elif defined(_MSC_VER) + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + dram_memory_size = status.ullTotalPhys; +#else // Linux + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + dram_memory_size = pages * page_size; +#endif +} +} // namespace Buffer_Namespace \ No newline at end of file diff --git a/DataMgr/Allocators/HeteroMem/MemResourceProvider.h b/DataMgr/Allocators/HeteroMem/MemResourceProvider.h new file mode 100644 index 00000000000..11ef6ea5777 --- /dev/null +++ b/DataMgr/Allocators/HeteroMem/MemResourceProvider.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* Copyright (C) 2020 Intel Corporation. */ + +#pragma once + +#include +#include +#include + +namespace Buffer_Namespace { + +enum MemRequirements { CAPACITY, HIGH_BDWTH, LOW_LATENCY }; + +enum MemType { DRAM, PMEM, HBM }; + +class MemoryResourceProvider { + public: + MemoryResourceProvider(); + + explicit MemoryResourceProvider(const std::string& pmem_path, size_t size); + + std::pmr::memory_resource* get(const MemRequirements& req) const; + MemType getMemoryType(std::pmr::memory_resource* other) const; + size_t getAvailableMemorySize(std::pmr::memory_resource* mem_resource) const; + std::vector getOrderByBandwidth() const; + + private: + void initPmm(const std::string& pmem_path, size_t size); + + bool isDAXPath(const std::string& path) const; + + void initAvailablePMEMSize(std::string path); + void initAvailableDRAMSize(); + + using mem_resources_storage_type = std::vector; + + std::unique_ptr dram_mem_resource_; + std::unique_ptr pmem_mem_resource_; + size_t dram_memory_size; + size_t pmem_memory_size; + + mem_resources_storage_type mem_resources_; +}; + +} // namespace Buffer_Namespace diff --git a/DataMgr/Allocators/HeteroMem/MemResources/memory_resources.h b/DataMgr/Allocators/HeteroMem/MemResources/memory_resources.h new file mode 100644 index 00000000000..1a380738ccb --- /dev/null +++ b/DataMgr/Allocators/HeteroMem/MemResources/memory_resources.h @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* Copyright (C) 2020 Intel Corporation. */ + +#pragma once + +#include +#include +#include +#include + +#include "memkind.h" +#include "memkind_allocator.h" +#include "pmem_allocator.h" + +namespace libmemkind { +namespace static_kind { +class memory_resource : public std::pmr::memory_resource { + public: + explicit memory_resource(libmemkind::kinds kind) { + switch (kind) { + case libmemkind::kinds::DEFAULT: + _kind = MEMKIND_DEFAULT; + break; + case libmemkind::kinds::HUGETLB: + _kind = MEMKIND_HUGETLB; + break; + case libmemkind::kinds::INTERLEAVE: + _kind = MEMKIND_INTERLEAVE; + break; + case libmemkind::kinds::HBW: + _kind = MEMKIND_HBW; + break; + case libmemkind::kinds::HBW_ALL: + _kind = MEMKIND_HBW_ALL; + break; + case libmemkind::kinds::HBW_HUGETLB: + _kind = MEMKIND_HBW_HUGETLB; + break; + case libmemkind::kinds::HBW_ALL_HUGETLB: + _kind = MEMKIND_HBW_ALL_HUGETLB; + break; + case libmemkind::kinds::HBW_PREFERRED: + _kind = MEMKIND_HBW_PREFERRED; + break; + case libmemkind::kinds::HBW_PREFERRED_HUGETLB: + _kind = MEMKIND_HBW_PREFERRED_HUGETLB; + break; + case libmemkind::kinds::HBW_INTERLEAVE: + _kind = MEMKIND_HBW_INTERLEAVE; + break; + case libmemkind::kinds::REGULAR: + _kind = MEMKIND_REGULAR; + break; + case libmemkind::kinds::DAX_KMEM: + _kind = MEMKIND_DAX_KMEM; + break; + case libmemkind::kinds::DAX_KMEM_ALL: + _kind = MEMKIND_DAX_KMEM_ALL; + break; + case libmemkind::kinds::DAX_KMEM_PREFERRED: + _kind = MEMKIND_DAX_KMEM_PREFERRED; + break; + case libmemkind::kinds::DAX_KMEM_INTERLEAVE: + _kind = MEMKIND_DAX_KMEM_INTERLEAVE; + break; + case libmemkind::kinds::HIGHEST_CAPACITY: + _kind = MEMKIND_HIGHEST_CAPACITY; + break; + case libmemkind::kinds::HIGHEST_CAPACITY_PREFERRED: + _kind = MEMKIND_HIGHEST_CAPACITY_PREFERRED; + break; + case libmemkind::kinds::HIGHEST_CAPACITY_LOCAL: + _kind = MEMKIND_HIGHEST_CAPACITY_LOCAL; + break; + case libmemkind::kinds::HIGHEST_CAPACITY_LOCAL_PREFERRED: + _kind = MEMKIND_HIGHEST_CAPACITY_LOCAL_PREFERRED; + break; + case libmemkind::kinds::LOWEST_LATENCY_LOCAL: + _kind = MEMKIND_LOWEST_LATENCY_LOCAL; + break; + case libmemkind::kinds::LOWEST_LATENCY_LOCAL_PREFERRED: + _kind = MEMKIND_LOWEST_LATENCY_LOCAL_PREFERRED; + break; + case libmemkind::kinds::HIGHEST_BANDWIDTH_LOCAL: + _kind = MEMKIND_HIGHEST_BANDWIDTH_LOCAL; + break; + case libmemkind::kinds::HIGHEST_BANDWIDTH_LOCAL_PREFERRED: + _kind = MEMKIND_HIGHEST_BANDWIDTH_LOCAL_PREFERRED; + break; + default: + throw std::runtime_error("Unknown libmemkind::kinds"); + break; + } + } + + private: + void* do_allocate(std::size_t bytes, std::size_t alignment) override { + void* res = 0; +#if 0 + //TODO: For some reasons this version throws bad_alloc. Need to investigate + if(memkind_posix_memalign(_kind, &res, alignment, bytes) != MEMKIND_SUCCESS) { + throw std::bad_alloc(); + } +#else + res = memkind_malloc(_kind, bytes); + if (!res) { + throw std::bad_alloc(); + } +#endif + + return res; + } + + void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override { + memkind_free(_kind, p); + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { + const memory_resource* other_ptr = dynamic_cast(&other); + return (other_ptr != nullptr && _kind == other_ptr->_kind); + } + + memkind_t _kind; +}; // class memory_resource +} // namespace static_kind +namespace pmem { +class memory_resource : public std::pmr::memory_resource { + using kind_wrapper_type = internal::kind_wrapper_t; + std::shared_ptr kind_wrapper_ptr; + + public: + explicit memory_resource(const char* dir, size_t max_size) + : kind_wrapper_ptr(std::make_shared(dir, max_size)) {} + + explicit memory_resource(const std::string& dir, size_t max_size) + : memory_resource(dir.c_str(), max_size) {} + + explicit memory_resource(const char* dir, + size_t max_size, + libmemkind::allocation_policy alloc_policy) + : kind_wrapper_ptr( + std::make_shared(dir, max_size, alloc_policy)) {} + + explicit memory_resource(const std::string& dir, + size_t max_size, + libmemkind::allocation_policy alloc_policy) + : memory_resource(dir.c_str(), max_size, alloc_policy) {} + + private: + void* do_allocate(std::size_t bytes, std::size_t alignment) override { + void* res = 0; +#if 0 + //TODO: For some reasons this version throws bad_alloc. Need to investigate + if(memkind_posix_memalign(kind_wrapper_ptr->get(), &res, alignment, bytes) != MEMKIND_SUCCESS) { + throw std::bad_alloc(); + } +#else + res = memkind_malloc(kind_wrapper_ptr->get(), bytes); + if (!res) { + throw std::bad_alloc(); + } +#endif + + return res; + } + + void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override { + memkind_free(kind_wrapper_ptr->get(), p); + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { + const memory_resource* other_ptr = dynamic_cast(&other); + return (other_ptr != nullptr && + kind_wrapper_ptr->get() == other_ptr->kind_wrapper_ptr->get()); + } +}; // class memory_resource +} // namespace pmem +} // namespace libmemkind diff --git a/DataMgr/CMakeLists.txt b/DataMgr/CMakeLists.txt index 9962527cd88..e55c28d352f 100644 --- a/DataMgr/CMakeLists.txt +++ b/DataMgr/CMakeLists.txt @@ -6,6 +6,7 @@ set(datamgr_source_files AbstractBuffer.cpp Allocators/CudaAllocator.cpp Allocators/ThrustAllocator.cpp + Allocators/HeteroMem/MemResourceProvider.cpp Chunk/Chunk.cpp DataMgr.cpp Encoder.cpp diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 7867335330a..739f9e334c6 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -116,7 +116,7 @@ endif() add_executable(TableUpdateDeleteBenchmark TableUpdateDeleteBenchmark.cpp) add_executable(GeospatialBenchmark GeospatialBenchmark.cpp) -set(EXECUTE_TEST_LIBS gtest mapd_thrift QueryRunner ${MAPD_LIBRARIES} ${CMAKE_DL_LIBS} ${CUDA_LIBRARIES} ${Boost_LIBRARIES} ${ZLIB_LIBRARIES} ${PROFILER_LIBS}) +set(EXECUTE_TEST_LIBS gtest mapd_thrift QueryRunner ${MAPD_LIBRARIES} ${CMAKE_DL_LIBS} ${CUDA_LIBRARIES} ${Boost_LIBRARIES} ${ZLIB_LIBRARIES} ${PROFILER_LIBS} ${memkind_LIBRARIES}) set(THRIFT_HANDLER_TEST_LIBRARIES thrift_handler ${EXECUTE_TEST_LIBS}) # Replace Licensing library with TestLicensing library diff --git a/scripts/install-memkind.sh b/scripts/install-memkind.sh new file mode 100755 index 00000000000..ee0e65d292d --- /dev/null +++ b/scripts/install-memkind.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2019, Intel Corporation + +# +# install-memkind.sh - installs memkind from sources; depends on +# the system it uses proper installation paramaters +# + +set -e + +OS=$1 + +# v1.11.0, contains new libmemkind namespace +MEMKIND_VERSION=v1.11.0 + +GIT_REPO="https://github.com/memkind/memkind" + +WORKDIR=$(pwd) + +git clone ${GIT_REPO} +cd $WORKDIR/memkind +git checkout $MEMKIND_VERSION + +export NDCTL_LIBRARY_VERSION=v70.1 + +./utils/docker/docker_install_ndctl.sh + +# set OS-specific configure options +OS_SPECIFIC="" +case $(echo $OS | cut -d'-' -f1) in + centos|opensuse) + OS_SPECIFIC="--libdir=/usr/lib64" + ;; +esac + +./autogen.sh +./configure --prefix=/usr $OS_SPECIFIC +make -j$(nproc) +make -j$(nproc) install + +# cleanup +cd $WORKDIR +rm -r memkind diff --git a/scripts/mapd-deps-ubuntu.sh b/scripts/mapd-deps-ubuntu.sh index 4305eda3632..53676ab5076 100755 --- a/scripts/mapd-deps-ubuntu.sh +++ b/scripts/mapd-deps-ubuntu.sh @@ -58,8 +58,8 @@ DEBIAN_FRONTEND=noninteractive sudo apt install -y \ git \ wget \ curl \ - gcc-8 \ - g++-8 \ + gcc-9 \ + g++-9 \ libboost-all-dev \ libgoogle-glog-dev \ libssl-dev \ @@ -104,10 +104,10 @@ DEBIAN_FRONTEND=noninteractive sudo apt install -y \ libxerces-c-dev \ libxmlsec1-dev -# Set up gcc-8 as default gcc +# Set up gcc-9 as default gcc sudo update-alternatives \ - --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 \ - --slave /usr/bin/g++ g++ /usr/bin/g++-8 + --install /usr/bin/gcc gcc /usr/bin/gcc-9 800 \ + --slave /usr/bin/g++ g++ /usr/bin/g++-9 # Needed to find sqlite3, xmltooling, and xml_security_c export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig:$PREFIX/lib64/pkgconfig:$PKG_CONFIG_PATH