Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using HugePages on TCMalloc #190

Open
george-matheww opened this issue Jun 21, 2023 · 7 comments
Open

Using HugePages on TCMalloc #190

george-matheww opened this issue Jun 21, 2023 · 7 comments

Comments

@george-matheww
Copy link

george-matheww commented Jun 21, 2023

Wanted to know if there was a fixed procedure on how to enable the use of HugePages on TCMalloc; like how it can be done on gperftools tcmalloc.
I am using TCMalloc on Ubuntu 22.04.

@ckennelly
Copy link
Collaborator

TCMalloc is routinely used with Transparent Huge Pages and it works out-of-the-box.

For backing memory with hugetlbfs, you can install a custom system allocator (tcmalloc::MallocExtension::SetRegionFactory).

@george-matheww
Copy link
Author

So I am working on an application where I want to exclusively force hugepages to be used instead of transparent HugePages.
As mentioned by your reply, I want to back memory with hugetlbfs, however in the tcmalloc::MallocExtension::SetRegionFactory
there is no explicit call/Region set to hugetlbfs or to dev/hugepages/.
Please help me understand if I am missing something or how I must approach setting up hugepages in this way using the SetRegionFactory allocator.

@george-matheww
Copy link
Author

george-matheww commented Jun 28, 2023

Just checking in, so I tried running one of the test files: tcmalloc/huge_page_aware_allocator_test.cc however when I check the size of the pages being used on cat /proc/meminfo ; it shows that there are no huge pages being used with hugetlbfs; which probably suggests that it's only using transparent hugepages.

If you could please let me know how and where I can back memory with hugetlbs using the tcmalloc::MallocExtension::SetRegionFactory system allocator.

@george-matheww
Copy link
Author

george-matheww commented Jul 3, 2023

So I have made a rough code that creates a region that has been mapped to hugetlb using mmap and have attempted to set this region using SetRegionFactory; I have attached the code below:

#include <iostream>
#include <cstddef>
#include "tcmalloc/malloc_extension.h"
#include <stddef.h>
#include <sys/mman.h>
#include "tcmalloc/common.h"
#include <unistd.h>
inline constexpr size_t kMinMmapAlloc = 1 << 30; //1GiB region

#define PROTECTION (PROT_READ | PROT_WRITE)
#define LENGTH (100UL * 1024 * 1024)

#define ADDR (void *) (0x0UL)
#define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE)

using namespace std; 

//Creating a class ExtraRegion to create a new region and allocate hugePages
class ExtraRegion : public tcmalloc::AddressRegion {
 public:
  explicit ExtraRegion(AddressRegion* under) : under_(under) {}

  std::pair<void*, size_t> Alloc(size_t size, size_t alignment) override {
    size_t big = size + alignment + alignment;
    // Can't pad if allocation is within 2 * alignment of region size.
    if (big > kMinMmapAlloc) {
      return under_->Alloc(size, alignment);
    }
    void* ptr;
    size_t actual_size;
    std::tie(ptr, actual_size) = under_->Alloc(big, alignment);
    if (!ptr) return {nullptr, 0};
    actual_size = actual_size - alignment * 2;
    return {static_cast<char*>(ptr) + alignment, actual_size};
  }

 private:
  AddressRegion* under_;
};

//Creating a class ExtraRegionFactory to create a new region and allocate hugePages
class ExtraRegionFactory : public tcmalloc::AddressRegionFactory {
 public:
  explicit ExtraRegionFactory(AddressRegionFactory* under) : under_(under) {}

  tcmalloc::AddressRegion* Create(void* start, size_t size, UsageHint hint) override {
    tcmalloc::AddressRegion* underlying_region = under_->Create(start, size, hint);
    CHECK_CONDITION(underlying_region);
    void* region_space = MallocInternal(sizeof(ExtraRegion));
    CHECK_CONDITION(region_space);
    return new (region_space) ExtraRegion(underlying_region);
  }

  size_t GetStats(absl::Span<char> buffer) override {
    return under_->GetStats(buffer);
  }

 private:
  AddressRegionFactory* under_;
};


int main(int argc, char *argv[]) {
    //using mmap on buf to create a hugetlb region
    void * buf;
    buf = mmap(NULL, LENGTH, PROTECTION, FLAGS, 0, 0);
    if (buf == MAP_FAILED) {
        perror("mmap");
        return 1;
    }
    char cmd[128];
    const int pid = getpid();
    snprintf(cmd, sizeof(cmd),"cat /proc/%d/maps", pid);

    //searching for the respective maps file of the code
    system(cmd);
    cout << "MMap done, buf is " << pid << " " << std::hex << buf << " done " << std::endl;
    
    //Setting the new region factory
    ExtraRegionFactory* extra_;
    tcmalloc::AddressRegionFactory* before_;
    tcmalloc::AddressRegionFactory::UsageHint hint = tcmalloc::AddressRegionFactory::UsageHint::kNormal;


    before_ = tcmalloc::MallocExtension::GetRegionFactory();
    extra_ = new ExtraRegionFactory(before_);
    
    double *ptr1 = (double*) malloc(sizeof(double));
    cout << "Malloc done ptr1: " << std::hex << ptr1 << " done " << std::endl;

    //Creating the hugePages region
    extra_->Create(buf, LENGTH, hint);

    tcmalloc::MallocExtension::SetRegionFactory(extra_);

    //Allocating memory from the hugePages region
    double *ptr1 = (double*) malloc(sizeof(double));//kSize
    cout << "Malloc done: " << std::hex << ptr1 << " done " << std::endl;

return 0;
}

Here I have printed the maps file as well as the variable buf which has been used as a variable for mmap: I have also printed ptr1 which has been declared after calling SetRegionFactory.

The output is as follows:

.
.
7fbbf2800000-7fbbf8c00000 rw-p 00000000 00:0f 43468                      /anon_hugepage (deleted)
.
.

MMap done, buf is 5603 0x7fbbf2800000 done 

Malloc done: ptr1: 0x25c33fa00040 done

As we can see above: 7fbbf2800000-7fbbf8c00000 refers to the hugepage region however in the final line of the output we can see that ptr1 does not take memory from this region. Would like to know if there is some error that has been made in the understanding of the code and how it can be changed to fix this.

@ckennelly
Copy link
Collaborator

My recollection is that MmapAligned (in system-alloc.cc) governs the placement and choice of virtual address space. Given a particular piece of virtual address space chosen by MmapAligned, the region/factory logic determines how to appropriately back it. I'm not sure it can take an externally chosen virtual address, since RegionManager::Allocate is going from desired target address to region.

Once installed, though, we might not immediately need more virtual address space. For example, malloc(sizeof(double)) is very likely hitting the per-CPU cache and reusing memory that has already been allocated, rather than obtaining more.

@alokkataria
Copy link

@ckennelly how does the region factory logic tell MmapAligned how to back this memory ? We don't see a flag or any argument when creating the region factory to tell it to use hugetlbfs.

To give you a bit more background, I am more familiar with the memfs implementation in gperfs/tcmalloc, over there it allows us to specify TCMALLOC_MEMFS_MALLOC_PATH which helps us initialize the memfs allocator rather than the default system_allocator (which allocates from small page pool). See this for reference - https://github.com/gperftools/gperftools/blob/master/src/memfs_malloc.cc#L232

For that matter, for gperftools/tcmalloc we even updated this code to ensure that we disable fallback option, so that all allocations from tcmalloc only comes from the hugepages backed region. See - gperftools/gperftools@b7607ee

We have been meaning to experiment with the per-cpu cache effort that is available with this fork of tcmalloc but for us to make any progress with this I would first like to ensure that we can get all objects exclusively allocated from the hugepage pool.

@torshepherd
Copy link

Resurrecting to say I have a similar use case

We disable transparent hugepages in kernel params because they showed nondeterminism/ high variance in our latency, but I want to try using /dev/hugepages to see if we get better performance.

Like @alokkataria mentioned this is easy in gperftools/tcmalloc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants