krareT / pub-task

Terark public developing
MIT License
3 stars 6 forks source link

In place shrink memory: jemalloc: xallocx #38

Closed rockeet closed 5 years ago

rockeet commented 5 years ago

When MemTableRep::MarkReadOnly(), we should do something like vector::shrink_to_fit() to free unused extra memory. since we use large memory block, the unused memory is the upper part of the memory block.

Such memory block can be shrinked by realloc, but realloc is not guaranteed to shrink memory in place. This is not an issue in single thread, but it is a big issue in multi-thread:

  1. Thread-1 calling realloc, and realloc reallocated a smaller memory block and copying old memory into the smaller memory block, then free the old memory block.
  2. Thread-2 is reading the old memory block....

A good news is that jemalloc has a specific function xallocx:

// jemalloc specific:
size_t xallocx(void *ptr, size_t size, size_t extra, int flags);

jemalloc document says:

The xallocx() function resizes the allocation at ptr in place to be at least size bytes, and returns the real size of the allocation. If extra is non-zero, an attempt is made to resize the allocation to be at least (size + extra) bytes, though inability to allocate the extra byte(s) will not by itself result in failure to resize. Behavior is undefined if size is 0, or if (size + extra > SIZE_T_MAX).

jemalloc document also says:

The realloc(), rallocx(), and xallocx() functions may resize allocations without moving them under limited circumstances. Unlike the allocx() API, the standard API does not officially round up the usable size of an allocation to the nearest size class, so technically it is necessary to call realloc() to grow e.g. a 9-byte allocation to 16 bytes, or shrink a 16-byte allocation to 9 bytes. Growth and shrinkage trivially succeeds in place as long as the pre-size and post-size both round up to the same size class. No other API guarantees are made regarding in-place resizing, but the current implementation also tries to resize large allocations in place, as long as the pre-size and post-size are both large. For shrinkage to succeed, the extent allocator must support splitting (see arena.<i>.extent_hooks). Growth only succeeds if the trailing memory is currently available, and the extent allocator supports merging.

The conclusion is better: for any real world realloc, shrink is in place when the memory block is large, the large defined in jemalloc is really small for our usage, we can define our large value larger to get stronger guarantee, such as 2M.

rockeet commented 5 years ago

not needed any more if use virtual mem