이번에는 실제 개발하면서 유용하게 사용하고 있는 것들에 대해서 소개를 해볼까 한다.
1. std::allocator
stl container 내부에서 heap 메모리 할당시에 사용하는 할당자를 을 직접 구현할 수 있도록 도와주는 class template 이다. 처음 접할 땐 이걸로 뭘할 수 있는지 바로 떠오르긴 힘들지만 개발을 하다보니 이 template 을 통해 아주 많은 것들을 할 수 있다는것을 알게 되었었다.
referece site : https://en.cppreference.com/w/cpp/memory/allocator
사용방법은 간단하다.
allocator 에 선언되어있는 member function 들을 재정의하여 template class 를 작성해주고
stl container 에 allocator 에 넣어주면 된다.
ex)
namespace md
{
template <typename T>
class allocator
{
public:
// type definitions
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
// rebind allocator to type U
template <class U>
struct rebind {
typedef allocator<U> other;
};
// return address of values
pointer address(reference value) const {
return &value;
}
const_pointer address(const_reference value) const {
return &value;
}
/* constructors and destructor
* - nothing to do because the allocator has no state
*/
allocator() throw() {}
allocator(const allocator&) throw() {}
template <class U>
allocator(const allocator<U>&) throw() {}
~allocator() throw() {}
// return maximum number of elements that can be allocated
size_type max_size() const throw() {
return std::numeric_limits<std::size_t>::max() / sizeof(T);
}
// allocate but don't initialize num elements of type T
pointer allocate(size_type num, const void* = 0) {
// print message and allocate memory with global new
pointer ret = (pointer)(::operator new(num * sizeof(T)));
return ret;
}
// initialize elements of allocated storage p with value value
void construct(pointer p, const T& value) {
// initialize memory with placement new
new((void*)p)T(value);
}
// destroy elements of initialized storage p
void destroy(pointer p) {
// destroy objects by calling their destructor
p->~T();
}
// deallocate storage p of deleted elements
void deallocate(pointer p, size_type num) {
// print message and deallocate memory with global delete
::operator delete((void*)p);
}
template <class U> bool
operator==(allocator<U> const&) const
{
return NVS_TRUE;
}
template <class U> bool
operator!=(allocator<U> const&) const
{
return NVS_FALSE;
}
};
}
std::vector<int, md::allocator> tintVector
위 코드에서 중요한 부분은 allocate / deallocate 부분이다. 이부분에 내가 사용할 할당자를 사용하면된다.
2. new / delete overloading
위 allocator 는 선언된 container 에만 적용된다면 new / delete overloading 은
프로젝트 전체의 new / delete 에 적용된다고 보면된다.
referece site
new : en.cppreference.com/w/cpp/memory/new/operator_new
delete : en.cppreference.com/w/cpp/memory/new/operator_delete
사용방법은 역시 간단하다.
ex ) 가장 단순한 new /delete 에 대한 overloading 방법
void* operator new ( std::size_t count )
{
return malloc(count);
}
void operator delete ( void* ptr )
{
free(ptr)
}
이러한 사용방법들은 referece site 를 뒤져보면 아주 간단하게 알 수 있다.
3. 활용 방안
이 part 가 중요하다고 생각한다. 사실 실제 개발 할 때는 저것을 아는것도 중요하지만 어떻게 활용하느냐가 더 중요하기 때문이다.
2가지 정도의 활용방안을 제시한다.
1) 개선된 할당자의 사용.
우리들은 많은 할당자 library 를 알고 있다. tcmalloc 이라던가 boost 에 있는 memory pool 이라던가 등등
tcmalloc 이야 library link 만 해주면 알아서 다 해주지만 library 에서 제공되는 function 을 직접 사용해야하는 경우가 있다. 이경우 위 allocate / deallocate 에 구현만 해주면 사용이 가능하다.
간략한 개선된 할당자 소개
tcmalloc : https://github.com/google/tcmalloc
멀티쓰레드 환경에서 alloc / free 에 성능이 아주 개선됨.
boost memory pool : https://www.boost.org/doc/libs/1_65_1/doc/html/interprocess/managed_memory_segments.html
fragmentation 이 발생하는 OS 에서 사용하기 좋음. 성능은 그닥..
intel IPP : https://software.intel.com/content/www/us/en/develop/tools/integrated-performance-primitives.html
intel chip 에서만 사용가능한 개선된 function 들. malloc / free 뿐만 아니라 memset / memcpy 등 유용하게 사용할 수 있는 것들이 많다.
2) 메모리 사용량 수집.
긴말 필요없이 코드 먼저 보자.
std::atomic<std::size_t> g_totalSize = 0;
pointer allocate(size_type num, const void* = 0)
{
size_type size = num * sizeof(T) + sizeof(size_type);
g_totalSize += size;
size_type* ret = ((size_type*)(::operator new(size));
*ret = size;
return (pointer)++ret;
}
void deallocate(pointer p, size_type num)
{
size_type* size_ptr = p;
--size_ptr;
g_totalSize -= *size_ptr;
::operator delete((void*)size_ptr);
}
자 위와 같이 구현을 하면 g_totalSize 에 현재 사용중인 memory size 가 측정이 된다.
그리고 new / delete 에도 적용이 가능하기에 container 뿐만 아니라 전체 사용 heap memory 도 실시간으로 알 수 있다.
고객에서 발생한 문제가 직접 재현해보았을 때 재현이 되지 않는 경우가 많다.
여러가지 tool 을 이용하여 메모리 사용량을 측정할 순 있지만 재현이 되지 않으면 문제원인을 찾을 수 없다.
그래서 이러한 코딩을 해놓으면 log 를 통해 메모리관련 문제가 있었는지를 확인하는데 큰 도움을 준다.
여러가지 활용방안이 더 있을 것이고 2번 항목을 기본으로 변형해가면 될 것이다. ex) memory overrun 검출.
위 방안을 알아두면 디버깅이 어려운 개발환경에서 도움이 되기에 알아두길 추천한다.
'지식이 늘었다 > C,C++' 카테고리의 다른 글
[Linux] CPU 사용량/사용률 구하기 (0) | 2022.09.08 |
---|---|
fopen_s EINVAL return 에 대한 고찰 ( filename 관련 ) (0) | 2020.08.07 |