Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
2dot5
ClickHouse
提交
f0a5ec47
C
ClickHouse
项目概览
2dot5
/
ClickHouse
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
ClickHouse
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
f0a5ec47
编写于
8月 16, 2015
作者:
A
Alexey Milovidov
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
dbms: unification; using huge pages (experimental) [#METR-2944].
上级
38fa9c89
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
163 addition
and
101 deletion
+163
-101
dbms/include/DB/Common/Allocator.h
dbms/include/DB/Common/Allocator.h
+124
-0
dbms/include/DB/Common/Arena.h
dbms/include/DB/Common/Arena.h
+4
-10
dbms/include/DB/Common/HashTable/HashTableAllocator.h
dbms/include/DB/Common/HashTable/HashTableAllocator.h
+14
-1
dbms/include/DB/Common/PODArray.h
dbms/include/DB/Common/PODArray.h
+5
-54
dbms/include/DB/IO/BufferWithOwnMemory.h
dbms/include/DB/IO/BufferWithOwnMemory.h
+16
-36
未找到文件。
dbms/include/DB/Common/Allocator.h
0 → 100644
浏览文件 @
f0a5ec47
#pragma once
#include <malloc.h>
#include <string.h>
#include <sys/mman.h>
#include <DB/Common/MemoryTracker.h>
#include <DB/Core/Exception.h>
#include <DB/Core/ErrorCodes.h>
/** Отвечает за выделение/освобождение памяти. Используется, например, в PODArray, Arena.
* Интерфейс отличается от std::allocator
* - наличием метода realloc, который для больших кусков памяти использует mremap;
* - передачей размера в метод free;
* - наличием аргумента alignment;
*/
class
Allocator
{
private:
/** См. комментарий в HashTableAllocator.h
*/
static
constexpr
size_t
MMAP_THRESHOLD
=
64
*
(
1
<<
20
);
static
constexpr
size_t
HUGE_PAGE_SIZE
=
2
*
(
1
<<
20
);
static
constexpr
size_t
MMAP_MIN_ALIGNMENT
=
4096
;
static
constexpr
size_t
MALLOC_MIN_ALIGNMENT
=
8
;
public:
/// Выделить кусок памяти.
void
*
alloc
(
size_t
size
,
size_t
alignment
=
0
)
{
if
(
current_memory_tracker
)
current_memory_tracker
->
alloc
(
size
);
void
*
buf
;
if
(
size
>=
MMAP_THRESHOLD
)
{
if
(
alignment
>
MMAP_MIN_ALIGNMENT
)
throw
DB
::
Exception
(
"Too large alignment: more than page size."
,
DB
::
ErrorCodes
::
BAD_ARGUMENTS
);
buf
=
mmap
(
NULL
,
size
,
PROT_READ
|
PROT_WRITE
,
MAP_PRIVATE
|
MAP_ANONYMOUS
,
-
1
,
0
);
if
(
MAP_FAILED
==
buf
)
DB
::
throwFromErrno
(
"Allocator: Cannot mmap."
,
DB
::
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
);
/// См. комментарий в HashTableAllocator.h
if
(
size
>=
HUGE_PAGE_SIZE
&&
0
!=
madvise
(
buf
,
size
,
MADV_HUGEPAGE
))
DB
::
throwFromErrno
(
"HashTableAllocator: Cannot madvise with MADV_HUGEPAGE."
,
DB
::
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
);
}
else
{
if
(
alignment
<=
MALLOC_MIN_ALIGNMENT
)
{
buf
=
::
malloc
(
size
);
if
(
nullptr
==
buf
)
DB
::
throwFromErrno
(
"Allocator: Cannot malloc."
,
DB
::
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
);
}
else
{
buf
=
nullptr
;
int
res
=
posix_memalign
(
&
buf
,
alignment
,
size
);
if
(
0
!=
res
)
DB
::
throwFromErrno
(
"Cannot allocate memory (posix_memalign)"
,
DB
::
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
,
res
);
}
}
return
buf
;
}
/// Освободить память.
void
free
(
void
*
buf
,
size_t
size
)
{
if
(
size
>=
MMAP_THRESHOLD
)
{
if
(
0
!=
munmap
(
buf
,
size
))
DB
::
throwFromErrno
(
"Allocator: Cannot munmap."
,
DB
::
ErrorCodes
::
CANNOT_MUNMAP
);
}
else
{
::
free
(
buf
);
}
if
(
current_memory_tracker
)
current_memory_tracker
->
free
(
size
);
}
/** Увеличить размер куска памяти.
* Содержимое старого куска памяти переезжает в начало нового.
* Положение куска памяти может измениться.
*/
void
*
realloc
(
void
*
buf
,
size_t
old_size
,
size_t
new_size
,
size_t
alignment
=
0
)
{
if
(
old_size
<
MMAP_THRESHOLD
&&
new_size
<
MMAP_THRESHOLD
&&
alignment
<=
MALLOC_MIN_ALIGNMENT
)
{
if
(
current_memory_tracker
)
current_memory_tracker
->
realloc
(
old_size
,
new_size
);
buf
=
::
realloc
(
buf
,
new_size
);
if
(
nullptr
==
buf
)
DB
::
throwFromErrno
(
"Allocator: Cannot realloc."
,
DB
::
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
);
}
else
if
(
old_size
>=
MMAP_THRESHOLD
&&
new_size
>=
MMAP_THRESHOLD
)
{
if
(
current_memory_tracker
)
current_memory_tracker
->
realloc
(
old_size
,
new_size
);
buf
=
mremap
(
buf
,
old_size
,
new_size
,
MREMAP_MAYMOVE
);
if
(
MAP_FAILED
==
buf
)
DB
::
throwFromErrno
(
"Allocator: Cannot mremap."
,
DB
::
ErrorCodes
::
CANNOT_MREMAP
);
}
else
{
void
*
new_buf
=
alloc
(
new_size
,
alignment
);
memcpy
(
new_buf
,
buf
,
old_size
);
free
(
buf
,
old_size
);
buf
=
new_buf
;
}
return
buf
;
}
};
dbms/include/DB/Common/Arena.h
浏览文件 @
f0a5ec47
...
...
@@ -6,7 +6,7 @@
#include <Poco/SharedPtr.h>
#include <Yandex/likely.h>
#include <DB/Common/ProfileEvents.h>
#include <DB/Common/
MemoryTracke
r.h>
#include <DB/Common/
Allocato
r.h>
namespace
DB
...
...
@@ -25,7 +25,7 @@ class Arena
{
private:
/// Непрерывный кусок памяти и указатель на свободное место в нём. Односвязный список.
struct
Chunk
:
private
std
::
allocator
<
char
>
/// empty base optimization
struct
Chunk
:
private
Allocator
/// empty base optimization
{
char
*
begin
;
char
*
pos
;
...
...
@@ -38,10 +38,7 @@ private:
ProfileEvents
::
increment
(
ProfileEvents
::
ArenaAllocChunks
);
ProfileEvents
::
increment
(
ProfileEvents
::
ArenaAllocBytes
,
size_
);
if
(
current_memory_tracker
)
current_memory_tracker
->
alloc
(
size_
);
begin
=
allocate
(
size_
);
begin
=
reinterpret_cast
<
char
*>
(
Allocator
::
alloc
(
size_
));
pos
=
begin
;
end
=
begin
+
size_
;
prev
=
prev_
;
...
...
@@ -49,10 +46,7 @@ private:
~
Chunk
()
{
deallocate
(
begin
,
size
());
if
(
current_memory_tracker
)
current_memory_tracker
->
free
(
size
());
Allocator
::
free
(
begin
,
size
());
if
(
prev
)
delete
prev
;
...
...
dbms/include/DB/Common/HashTable/HashTableAllocator.h
浏览文件 @
f0a5ec47
...
...
@@ -19,6 +19,7 @@
/** Общая часть разных хэш-таблиц, отвечающая за выделение/освобождение памяти.
* Отличается от Allocator тем, что зануляет память.
* Используется в качестве параметра шаблона (есть несколько реализаций с таким же интерфейсом).
*/
class
HashTableAllocator
...
...
@@ -33,9 +34,9 @@ private:
* Рассчитываем, что набор операций mmap/что-то сделать/mremap может выполняться всего лишь около 1000 раз в секунду.
*
* PS. Также это требуется, потому что tcmalloc не может выделить кусок памяти больше 16 GB.
* NOTE Можно попробовать MAP_HUGETLB, но придётся самостоятельно управлять количеством доступных страниц.
*/
static
constexpr
size_t
MMAP_THRESHOLD
=
64
*
(
1
<<
20
);
static
constexpr
size_t
HUGE_PAGE_SIZE
=
2
*
(
1
<<
20
);
public:
/// Выделить кусок памяти и заполнить его нулями.
...
...
@@ -52,6 +53,14 @@ public:
if
(
MAP_FAILED
==
buf
)
DB
::
throwFromErrno
(
"HashTableAllocator: Cannot mmap."
,
DB
::
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
);
/** Использование huge pages позволяет увеличить производительность более чем в три раза
* в запросе SELECT number % 1000000 AS k, count() FROM system.numbers GROUP BY k,
* (хэш-таблица на 1 000 000 элементов)
* и примерно на 15% в случае хэш-таблицы на 100 000 000 элементов.
*/
if
(
size
>=
HUGE_PAGE_SIZE
&&
0
!=
madvise
(
buf
,
size
,
MADV_HUGEPAGE
))
DB
::
throwFromErrno
(
"HashTableAllocator: Cannot madvise with MADV_HUGEPAGE."
,
DB
::
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
);
/// Заполнение нулями не нужно - mmap сам это делает.
}
else
...
...
@@ -108,6 +117,10 @@ public:
if
(
MAP_FAILED
==
buf
)
DB
::
throwFromErrno
(
"HashTableAllocator: Cannot mremap."
,
DB
::
ErrorCodes
::
CANNOT_MREMAP
);
/** Здесь не получается сделать madvise с MADV_HUGEPAGE.
* Похоже, что при mremap, huge pages сами расширяются на новую область.
*/
/// Заполнение нулями не нужно.
}
else
...
...
dbms/include/DB/Common/PODArray.h
浏览文件 @
f0a5ec47
#pragma once
#include <string.h>
#include <malloc.h>
#include <cstddef>
#include <algorithm>
#include <memory>
...
...
@@ -12,7 +11,7 @@
#include <Yandex/likely.h>
#include <Yandex/strong_typedef.h>
#include <DB/Common/
MemoryTracke
r.h>
#include <DB/Common/
Allocato
r.h>
#include <DB/Core/Exception.h>
#include <DB/Core/ErrorCodes.h>
...
...
@@ -32,28 +31,18 @@ namespace DB
* Конструктор по-умолчанию создаёт пустой объект, который не выделяет память.
* Затем выделяется память минимум под POD_ARRAY_INITIAL_SIZE элементов.
*
* При первом выделении памяти использует std::allocator.
* В реализации из libstdc++ он кэширует куски памяти несколько больше, чем обычный malloc.
*
* При изменении размера, использует realloc, который может (но не обязан) использовать mremap для больших кусков памяти.
* По факту, mremap используется при использовании аллокатора из glibc, но не используется, например, в tcmalloc.
*
* Если вставлять элементы push_back-ом, не делая reserve, то PODArray примерно в 2.5 раза быстрее std::vector.
*/
#define POD_ARRAY_INITIAL_SIZE 4096UL
template
<
typename
T
>
class
PODArray
:
private
boost
::
noncopyable
,
private
std
::
allocator
<
char
>
/// empty base optimization
class
PODArray
:
private
boost
::
noncopyable
,
private
Allocator
/// empty base optimization
{
private:
typedef
std
::
allocator
<
char
>
Allocator
;
char
*
c_start
;
char
*
c_end
;
char
*
c_end_of_storage
;
bool
use_libc_realloc
=
false
;
T
*
t_start
()
{
return
reinterpret_cast
<
T
*>
(
c_start
);
}
T
*
t_end
()
{
return
reinterpret_cast
<
T
*>
(
c_end
);
}
T
*
t_end_of_storage
()
{
return
reinterpret_cast
<
T
*>
(
c_end_of_storage
);
}
...
...
@@ -90,10 +79,7 @@ private:
size_t
bytes_to_alloc
=
to_size
(
n
);
if
(
current_memory_tracker
)
current_memory_tracker
->
alloc
(
bytes_to_alloc
);
c_start
=
c_end
=
Allocator
::
allocate
(
bytes_to_alloc
);
c_start
=
c_end
=
reinterpret_cast
<
char
*>
(
Allocator
::
alloc
(
bytes_to_alloc
));
c_end_of_storage
=
c_start
+
bytes_to_alloc
;
}
...
...
@@ -102,13 +88,7 @@ private:
if
(
c_start
==
nullptr
)
return
;
if
(
use_libc_realloc
)
::
free
(
c_start
);
else
Allocator
::
deallocate
(
c_start
,
storage_size
());
if
(
current_memory_tracker
)
current_memory_tracker
->
free
(
storage_size
());
Allocator
::
free
(
c_start
,
storage_size
());
}
void
realloc
(
size_t
n
)
...
...
@@ -122,38 +102,10 @@ private:
ptrdiff_t
end_diff
=
c_end
-
c_start
;
size_t
bytes_to_alloc
=
to_size
(
n
);
char
*
old_c_start
=
c_start
;
char
*
old_c_end_of_storage
=
c_end_of_storage
;
if
(
current_memory_tracker
)
current_memory_tracker
->
realloc
(
storage_size
(),
bytes_to_alloc
);
if
(
use_libc_realloc
)
{
auto
new_c_start
=
reinterpret_cast
<
char
*>
(
::
realloc
(
c_start
,
bytes_to_alloc
));
if
(
nullptr
==
new_c_start
)
throwFromErrno
(
"PODArray: cannot realloc"
,
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
);
c_start
=
new_c_start
;
}
else
{
auto
new_c_start
=
reinterpret_cast
<
char
*>
(
malloc
(
bytes_to_alloc
));
if
(
nullptr
==
new_c_start
)
throwFromErrno
(
"PODArray: cannot realloc"
,
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
);
c_start
=
new_c_start
;
memcpy
(
c_start
,
old_c_start
,
std
::
min
(
bytes_to_alloc
,
static_cast
<
size_t
>
(
end_diff
)));
Allocator
::
deallocate
(
old_c_start
,
old_c_end_of_storage
-
old_c_start
);
}
c_start
=
reinterpret_cast
<
char
*>
(
Allocator
::
realloc
(
c_start
,
storage_size
(),
bytes_to_alloc
));
c_end
=
c_start
+
end_diff
;
c_end_of_storage
=
c_start
+
bytes_to_alloc
;
use_libc_realloc
=
true
;
}
public:
...
...
@@ -187,7 +139,6 @@ public:
std
::
swap
(
c_start
,
other
.
c_start
);
std
::
swap
(
c_end
,
other
.
c_end
);
std
::
swap
(
c_end_of_storage
,
other
.
c_end_of_storage
);
std
::
swap
(
use_libc_realloc
,
other
.
use_libc_realloc
);
return
*
this
;
}
...
...
dbms/include/DB/IO/BufferWithOwnMemory.h
浏览文件 @
f0a5ec47
...
...
@@ -3,7 +3,7 @@
#include <boost/noncopyable.hpp>
#include <DB/Common/ProfileEvents.h>
#include <DB/Common/
MemoryTracke
r.h>
#include <DB/Common/
Allocato
r.h>
#include <DB/Core/Exception.h>
#include <DB/Core/ErrorCodes.h>
...
...
@@ -18,7 +18,7 @@ namespace DB
* Отличается тем, что не делает лишний memset. (И почти ничего не делает.)
* Также можно попросить выделять выровненный кусок памяти.
*/
struct
Memory
:
boost
::
noncopyable
struct
Memory
:
boost
::
noncopyable
,
Allocator
{
size_t
m_capacity
=
0
;
size_t
m_size
=
0
;
...
...
@@ -66,16 +66,22 @@ struct Memory : boost::noncopyable
}
else
{
dealloc
(
);
new_size
=
align
(
new_size
);
m_data
=
reinterpret_cast
<
char
*>
(
Allocator
::
realloc
(
m_data
,
m_capacity
,
new_size
,
alignment
));
m_capacity
=
new_size
;
m_size
=
m_capacity
;
alloc
();
}
}
private:
size_t
align
(
size_t
value
)
const
{
if
(
!
alignment
)
return
value
;
return
(
value
+
alignment
-
1
)
/
alignment
*
alignment
;
}
void
alloc
()
{
if
(
!
m_capacity
)
...
...
@@ -87,33 +93,10 @@ private:
ProfileEvents
::
increment
(
ProfileEvents
::
IOBufferAllocs
);
ProfileEvents
::
increment
(
ProfileEvents
::
IOBufferAllocBytes
,
m_capacity
);
if
(
current_memory_tracker
)
current_memory_tracker
->
alloc
(
m_capacity
);
char
*
new_m_data
=
nullptr
;
if
(
!
alignment
)
{
new_m_data
=
reinterpret_cast
<
char
*>
(
malloc
(
m_capacity
));
if
(
!
new_m_data
)
throw
Exception
(
"Cannot allocate memory (malloc)"
,
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
);
m_data
=
new_m_data
;
return
;
}
size_t
aligned_capacity
=
(
m_capacity
+
alignment
-
1
)
/
alignment
*
alignment
;
m_capacity
=
aligned_capacity
;
size_t
new_capacity
=
align
(
m_capacity
);
m_data
=
reinterpret_cast
<
char
*>
(
Allocator
::
alloc
(
new_capacity
,
alignment
));
m_capacity
=
new_capacity
;
m_size
=
m_capacity
;
int
res
=
posix_memalign
(
reinterpret_cast
<
void
**>
(
&
new_m_data
),
alignment
,
m_capacity
);
if
(
0
!=
res
)
DB
::
throwFromErrno
(
"Cannot allocate memory (posix_memalign)"
,
ErrorCodes
::
CANNOT_ALLOCATE_MEMORY
,
res
);
m_data
=
new_m_data
;
}
void
dealloc
()
...
...
@@ -121,11 +104,8 @@ private:
if
(
!
m_data
)
return
;
free
(
reinterpret_cast
<
void
*>
(
m_data
)
);
Allocator
::
free
(
reinterpret_cast
<
void
*>
(
m_data
),
m_capacity
);
m_data
=
nullptr
;
/// Чтобы избежать double free, если последующий вызов alloc кинет исключение.
if
(
current_memory_tracker
)
current_memory_tracker
->
free
(
m_capacity
);
}
};
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录