2017-10-27 19:18:39 +00:00
# include <Common/Allocator.h>
2017-04-08 01:32:05 +00:00
# if !defined(__APPLE__) && !defined(__FreeBSD__)
# include <malloc.h>
# endif
# include <cstdlib>
# include <sys/mman.h>
2017-11-16 19:17:09 +00:00
# include <common/mremap.h>
2017-04-08 01:32:05 +00:00
# include <Common/MemoryTracker.h>
# include <Common/Exception.h>
2017-10-27 19:18:39 +00:00
# include <Common/formatReadable.h>
2017-08-10 23:26:38 +00:00
# include <IO/WriteHelpers.h>
2017-10-27 19:18:39 +00:00
2017-06-20 07:51:33 +00:00
/// Required for older Darwin builds, that lack definition of MAP_ANONYMOUS
2017-06-20 04:49:00 +00:00
# ifndef MAP_ANONYMOUS
# define MAP_ANONYMOUS MAP_ANON
# endif
2017-04-08 01:32:05 +00:00
namespace DB
{
namespace ErrorCodes
{
extern const int BAD_ARGUMENTS ;
extern const int CANNOT_ALLOCATE_MEMORY ;
extern const int CANNOT_MUNMAP ;
extern const int CANNOT_MREMAP ;
}
}
2017-05-09 19:07:35 +00:00
/** Many modern allocators (for example, tcmalloc) do not do a mremap for realloc,
2017-05-07 20:25:26 +00:00
* even in case of large enough chunks of memory .
* Although this allows you to increase performance and reduce memory consumption during realloc .
2017-05-09 19:07:35 +00:00
* To fix this , we do mremap manually if the chunk of memory is large enough .
2017-05-07 20:25:26 +00:00
* The threshold ( 64 MB ) is chosen quite large , since changing the address space is
2017-05-09 19:07:35 +00:00
* very slow , especially in the case of a large number of threads .
2017-05-07 20:25:26 +00:00
* We expect that the set of operations mmap / something to do / mremap can only be performed about 1000 times per second .
2017-04-08 01:32:05 +00:00
*
2017-05-07 20:25:26 +00:00
* PS . This is also required , because tcmalloc can not allocate a chunk of memory greater than 16 GB .
2017-04-08 01:32:05 +00:00
*/
2017-08-10 23:25:51 +00:00
static constexpr size_t MMAP_THRESHOLD = 64 * ( 1ULL < < 20 ) ;
2017-04-08 01:32:05 +00:00
static constexpr size_t MMAP_MIN_ALIGNMENT = 4096 ;
static constexpr size_t MALLOC_MIN_ALIGNMENT = 8 ;
template < bool clear_memory_ >
void * Allocator < clear_memory_ > : : alloc ( size_t size , size_t alignment )
{
2017-04-10 21:40:38 +00:00
CurrentMemoryTracker : : alloc ( size ) ;
2017-04-08 01:32:05 +00:00
void * buf ;
if ( size > = MMAP_THRESHOLD )
{
if ( alignment > MMAP_MIN_ALIGNMENT )
2017-11-17 20:48:00 +00:00
throw DB : : Exception ( " Too large alignment " + formatReadableSizeWithBinarySuffix ( alignment ) + " : more than page size when allocating "
2017-10-27 19:18:39 +00:00
+ formatReadableSizeWithBinarySuffix ( size ) + " . " , DB : : ErrorCodes : : BAD_ARGUMENTS ) ;
2017-04-08 01:32:05 +00:00
2017-08-10 23:25:51 +00:00
buf = mmap ( nullptr , size , PROT_READ | PROT_WRITE , MAP_PRIVATE | MAP_ANONYMOUS , - 1 , 0 ) ;
2017-04-08 01:32:05 +00:00
if ( MAP_FAILED = = buf )
2017-10-27 19:18:39 +00:00
DB : : throwFromErrno ( " Allocator: Cannot mmap " + formatReadableSizeWithBinarySuffix ( size ) + " . " , DB : : ErrorCodes : : CANNOT_ALLOCATE_MEMORY ) ;
2017-04-08 01:32:05 +00:00
/// No need for zero-fill, because mmap guarantees it.
}
else
{
if ( alignment < = MALLOC_MIN_ALIGNMENT )
{
if ( clear_memory )
buf = : : calloc ( size , 1 ) ;
else
buf = : : malloc ( size ) ;
if ( nullptr = = buf )
2017-10-27 19:18:39 +00:00
DB : : throwFromErrno ( " Allocator: Cannot malloc " + formatReadableSizeWithBinarySuffix ( size ) + " . " , DB : : ErrorCodes : : CANNOT_ALLOCATE_MEMORY ) ;
2017-04-08 01:32:05 +00:00
}
else
{
buf = nullptr ;
int res = posix_memalign ( & buf , alignment , size ) ;
if ( 0 ! = res )
2017-10-27 19:18:39 +00:00
DB : : throwFromErrno ( " Cannot allocate memory (posix_memalign) " + formatReadableSizeWithBinarySuffix ( size ) + " . " , DB : : ErrorCodes : : CANNOT_ALLOCATE_MEMORY , res ) ;
2017-04-08 01:32:05 +00:00
if ( clear_memory )
memset ( buf , 0 , size ) ;
}
}
return buf ;
}
template < bool clear_memory_ >
void Allocator < clear_memory_ > : : free ( void * buf , size_t size )
{
if ( size > = MMAP_THRESHOLD )
{
if ( 0 ! = munmap ( buf , size ) )
2017-10-27 19:18:39 +00:00
DB : : throwFromErrno ( " Allocator: Cannot munmap " + formatReadableSizeWithBinarySuffix ( size ) + " . " , DB : : ErrorCodes : : CANNOT_MUNMAP ) ;
2017-04-08 01:32:05 +00:00
}
else
{
: : free ( buf ) ;
}
2017-04-10 21:40:38 +00:00
CurrentMemoryTracker : : free ( size ) ;
2017-04-08 01:32:05 +00:00
}
template < bool clear_memory_ >
void * Allocator < clear_memory_ > : : realloc ( void * buf , size_t old_size , size_t new_size , size_t alignment )
{
2017-11-17 20:27:12 +00:00
if ( old_size = = new_size )
{
/// nothing to do.
}
else if ( old_size < MMAP_THRESHOLD & & new_size < MMAP_THRESHOLD & & alignment < = MALLOC_MIN_ALIGNMENT )
2017-04-08 01:32:05 +00:00
{
2017-04-10 21:40:38 +00:00
CurrentMemoryTracker : : realloc ( old_size , new_size ) ;
2017-04-08 01:32:05 +00:00
2018-06-04 19:43:38 +00:00
void * new_buf = : : realloc ( buf , new_size ) ;
if ( nullptr = = new_buf )
2017-10-27 19:18:39 +00:00
DB : : throwFromErrno ( " Allocator: Cannot realloc from " + formatReadableSizeWithBinarySuffix ( old_size ) + " to " + formatReadableSizeWithBinarySuffix ( new_size ) + " . " , DB : : ErrorCodes : : CANNOT_ALLOCATE_MEMORY ) ;
2017-04-08 01:32:05 +00:00
2018-06-04 19:43:38 +00:00
buf = new_buf ;
2017-11-17 20:27:12 +00:00
if ( clear_memory & & new_size > old_size )
2017-04-08 01:32:05 +00:00
memset ( reinterpret_cast < char * > ( buf ) + old_size , 0 , new_size - old_size ) ;
}
else if ( old_size > = MMAP_THRESHOLD & & new_size > = MMAP_THRESHOLD )
{
2017-04-10 21:40:38 +00:00
CurrentMemoryTracker : : realloc ( old_size , new_size ) ;
2017-04-08 01:32:05 +00:00
2017-11-16 19:17:09 +00:00
// On apple and freebsd self-implemented mremap used (common/mremap.h)
buf = clickhouse_mremap ( buf , old_size , new_size , MREMAP_MAYMOVE , PROT_READ | PROT_WRITE , MAP_PRIVATE | MAP_ANONYMOUS , - 1 , 0 ) ;
2017-04-08 01:32:05 +00:00
if ( MAP_FAILED = = buf )
2017-10-27 19:18:39 +00:00
DB : : throwFromErrno ( " Allocator: Cannot mremap memory chunk from " + formatReadableSizeWithBinarySuffix ( old_size ) + " to " + formatReadableSizeWithBinarySuffix ( new_size ) + " . " , DB : : ErrorCodes : : CANNOT_MREMAP ) ;
2017-04-08 01:32:05 +00:00
/// No need for zero-fill, because mmap guarantees it.
}
2017-11-17 20:27:12 +00:00
else if ( old_size > = MMAP_THRESHOLD & & new_size < MMAP_THRESHOLD )
{
void * new_buf = alloc ( new_size , alignment ) ;
memcpy ( new_buf , buf , new_size ) ;
if ( 0 ! = munmap ( buf , old_size ) )
{
2017-11-17 20:48:00 +00:00
: : free ( new_buf ) ;
2017-11-17 20:27:12 +00:00
DB : : throwFromErrno ( " Allocator: Cannot munmap " + formatReadableSizeWithBinarySuffix ( old_size ) + " . " , DB : : ErrorCodes : : CANNOT_MUNMAP ) ;
}
buf = new_buf ;
}
2017-04-08 01:32:05 +00:00
else
{
void * new_buf = alloc ( new_size , alignment ) ;
memcpy ( new_buf , buf , old_size ) ;
free ( buf , old_size ) ;
buf = new_buf ;
}
return buf ;
}
2017-04-18 03:03:39 +00:00
/// Explicit template instantiations.
2017-04-08 01:32:05 +00:00
template class Allocator < true > ;
template class Allocator < false > ;