惰性策略的allocator

Vegertar 2011-08-02 02:09:24
又再看Robert Sedgewick的算法了,看到里面一句,
“能够利用与应用相关的特定知识的程序,常常比用于同样任务的通用程序更高效。内存分配也遵从此定律。一个必须处理不同大小的存储请求的算法不可能知道我们将一直请求某种大小的内存块,因此也不能利用这一点。”

以前看的时候居然被略过去了,然后就写了这么一个分配器,估摸着对链表这类增删频繁的数据结构的存储分配的性能有些改善。大家看看,这东西有啥子实用价值没?


template <typename T>
class lazy_alloc : public std::allocator<T>
{
public:
typedef typename std::allocator<T>::pointer pointer;
typedef typename std::allocator<T>::size_type size_type;

~lazy_alloc()
{
typedef
typename std::map<size_type, std::stack<pointer> >::iterator
map_iter;

map_iter beg = pool.begin(), end = pool.end();
while (beg != end) {
std::stack<pointer> &store = beg -> second;
while (!store.empty()) {
std::allocator<T>::deallocate(store.top(), beg -> first);
store.pop();
}

++beg;
}
}

pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
{
std::stack<pointer> &store = pool[n];
if (store.empty())
return std::allocator<T>::allocate(n, hint);

pointer p = store.top();
store.pop();
return p;
}

void deallocate(pointer p, size_type n)
{
pool[n].push(p);
}

private:
std::map<size_type, std::stack<pointer> > pool;
};

...全文
215 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
Vegertar 2011-08-04
  • 打赏
  • 举报
回复
第三天了,该结贴了。
2011-08-03
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 vegertar 的回复:]
由于长期潜水, 这次冒泡还是要散个分
[/Quote]
唔……打劫,打劫~
Vegertar 2011-08-03
  • 打赏
  • 举报
回复
由于长期潜水, 这次冒泡还是要散个分
Vegertar 2011-08-03
  • 打赏
  • 举报
回复

// main.cpp

#include "lazy_alloc.hpp"
#include <cassert>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <functional>
#include <list>

using namespace std;

int main(int argc, char *argv[])
{
assert(sizeof(int) == sizeof(float));
#ifndef N
int n = 10;
#else
int n = N;
#endif // N

#ifndef STD
list<int, lazy_alloc<int> > l1;
#else
list<int> l1;
#endif // STD
for (int i = 0; i < n; ++i)
l1.push_back(i);
l1.remove_if(bind2nd(modulus<int>(), 2));

#ifndef STD
list<float, lazy_alloc<float> > l2;
#else
list<float> l2;
#endif // STD
for (int i = 0; i < n; ++i )
l2.push_back(i + 0.1F);

#ifdef OUTPUT
copy(l1.begin(), l1.end(), ostream_iterator<int>(cout, " "));
cout << endl;
copy(l2.begin(), l2.end(), ostream_iterator<float>(cout, " "));
#endif // OUTPUT
cout << endl;
}



今天脑子清醒了。
这种搭建在new/delete上的lazy deallocate,其性能,要跟标准分配器比较起来,实在是很纠结啊。
1) g++ -O2 -Wall -DN=10000000 main.cpp -o lazy
2) g++ -O2 -Wall -DN=10000000 -DSTD main.cpp -o std

去除共有的一些开销影响极少的操作,仔细比较一下
1) 1500万次 operator new,1500万次循环{ stack::top , stack::pop , operator delete } , size为1500万的stack生成和销毁

2) 2000万次operator new, 2000万次operator delete

系统层里实在不好说,依赖new/delete
用户层,
1) 就看stack了。虽然top,和pop的开销是常量级的,但是1500万次的循环跳转,精确起来也是很难度量的。而stack本身,它的生成开销应该比较大,默认的deque在push*时没有块间复制,到底产生多少次operator new还是实现相关,但是其维护的块的释放,这个应该也算O(1)吧,也许。。

2) 500万次 operator new,500万次operator delete。这个还是实现相关。

现在看来,1)和2)开销的比较,果然是极为纠结啊!!!
然后我就进行实际测试。虽然局部特征很严重,但是-------------
在我的机器,重启-运行,重启-运行,结果2)要比1)快了0.5秒

P.S.我真傻,真的。我单知道标准库已经是一个轮子,还妄想在轮子上包一层壳让车子跑得更快。
P.P.S.查看了下记录,两年来冒泡不足十次,现在看情形,要继续潜水了。

价值不大,为了帖子的完整性,代码仍然贴上。

// lazy_alloc.hpp

#ifndef LAZH_ALLOC_HPP
#define LAZH_ALLOC_HPP

#include <limits>
#include <map>
#include <stack>

class lazy_alloc_base
{
class pool_t // class for RAII
{
public:
~pool_t()
{
typedef
std::map<size_t, std::stack<void *> >::iterator
map_iter;

map_iter beg = cache.begin(), end = cache.end();
while (beg != end) {
std::stack<void *> &store = beg -> second;
while (! store.empty()) {
operator delete(store.top());
store.pop();
}

++beg;
}
}

std::stack<void *> & operator[](const size_t &n)
{
return cache[n];
}

private:
std::map<size_t, std::stack<void *> > cache;
};

protected:
static pool_t pool;
};

lazy_alloc_base::pool_t lazy_alloc_base::pool;

template <typename T>
class lazy_alloc;

template <>
class lazy_alloc<void>
{
public:
typedef void * pointer;
typedef const void * const_pointer;
typedef void value_type;

template <typename U>
struct rebind { typedef lazy_alloc<U> other; };
};


template <typename T>
class lazy_alloc : public lazy_alloc_base // now any instance of type lazy_alloc will be sharing the same pool
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T * pointer;
typedef const T * const_pointer;
typedef T & reference;
typedef const T & const_reference;
typedef T value_type;

template <typename U>
struct rebind { typedef lazy_alloc<U> other; };

pointer address(reference value) const
{
return &value;
}

const_pointer address(const_reference value) const
{
return &value;
}

lazy_alloc() throw ()
{ }

lazy_alloc(const lazy_alloc &) throw ()
{ }

template <typename U>
lazy_alloc(const lazy_alloc<U> &) throw ()
{ }

~lazy_alloc() throw ()
{ }

size_type max_size() const throw ()
{
return std::numeric_limits<size_type>::max() / sizeof(value_type);
}

void construct(pointer p, const value_type &value)
{
new(p)value_type(value);
}

void destroy(pointer p)
{
p -> ~T();
}

pointer allocate(size_type n, lazy_alloc<void>::const_pointer hint = 0)
{
std::stack<void *> &store = pool[n];
if (store.empty())
return (pointer)operator new(sizeof(n * sizeof(value_type)));

pointer p = (pointer)store.top();
store.pop();
return p;
}

void deallocate(pointer p, size_type n)
{
pool[n].push(p);
}
};

template <typename T, typename U>
bool operator==(const lazy_alloc<T> &, const lazy_alloc<U> &) throw ()
{ return true; }

template <typename T, typename U>
bool operator!=(const lazy_alloc<T> &, const lazy_alloc<U> &) throw ()
{ return false; }

#endif // LAZH_ALLOC_HPP

谁学逆向工程 2011-08-02
  • 打赏
  • 举报
回复
祝你好运
2011-08-02
  • 打赏
  • 举报
回复
不知道这个 Allocator 和 free list 实现的内存池比起来怎么样?

我的感觉是 free list 实现的内存池可以很方便地把许多小内存分配合并成一个较大的内存分配请求,调用底层分配函数的次数比较少,也不用缓存每个分配出去的指针。

P.S. 其实 glibc 的 malloc/free 函数在分配小内存的时候就是用的内存池,所以通常情况下我还是比较相信标准库的内存分配效率的~

P.P.S. 楼主貌似有相当长一段时间木有冒泡了啊……
Vegertar 2011-08-02
  • 打赏
  • 举报
回复
用list试了下,发现析构次数过于频繁。

近一年没写C++了,果然是够烂的。

唔,今天programming的时间已经结束,帖子放这,欢迎大家拍砖,明天12点再来。

Vegertar 2011-08-02
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 hpsmouse 的回复:]
Allocator 只要符合 requirements 就行了,不用继承的……

而且那个 std::map 是不是应该 static 一下……
[/Quote]

原本用继承只是想少写一些代码的,写迭代器是写习惯了,一行行的typedef实在是枯燥。

本拟写空的COPY-CTOR的,听你这么一说,static对存储器的利用率更高一些。
healer_kx 2011-08-02
  • 打赏
  • 举报
回复
咕~~(╯﹏╰)b
老朽不行了,都是早年试图学习的粗浅的东西,Lazy这个,不知道有意义没? 个人感觉不搞服务器,浏览器本身这些项目,Allocator的意义都不是很显著。
pengzhixi 2011-08-02
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 healer_kx 的回复:]
引用 4 楼 babilife 的回复:

allocator今天还看了一篇博客,争论激烈,地址忘了,不过我看见甘草了,哈哈

?? ...
[/Quote]
如果我没猜错,是陈硕的一篇关于allocator的博文
healer_kx 2011-08-02
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 babilife 的回复:]

allocator今天还看了一篇博客,争论激烈,地址忘了,不过我看见甘草了,哈哈
[/Quote]
?? ...
2011-08-02
  • 打赏
  • 举报
回复
Allocator 只要符合 requirements 就行了,不用继承的……

而且那个 std::map 是不是应该 static 一下……
Vegertar 2011-08-02
  • 打赏
  • 举报
回复
使用了成员数据,却没有显式的copy-ctor,果然是乱七八糟啊。
Vegertar 2011-08-02
  • 打赏
  • 举报
回复
据说C++和JAVA都很牛的甘草某某某。。。
Vegertar 2011-08-02
  • 打赏
  • 举报
回复
缺陷比较多,果然还是得按标准规范来写。

哥一直都在自言自语。
至善者善之敌 2011-08-02
  • 打赏
  • 举报
回复
allocator今天还看了一篇博客,争论激烈,地址忘了,不过我看见甘草了,哈哈
Vegertar 2011-08-02
  • 打赏
  • 举报
回复
唔,发现一个很严重的bug,rebind 类也被继承了,真实的分配器又重新被定向到了std::allocator了。

方便而又麻烦的继承。
Vegertar 2011-08-02
  • 打赏
  • 举报
回复
呃,是语文经验。
Vegertar 2011-08-02
  • 打赏
  • 举报
回复
第一句话就有语法错误了,多年的语言经验,汗。。。
讲述memcached 安装使用等 目录: 第一章:memcached 介绍....................................................................................................................1 1.1 memcached 是什么?.............................................................................................................1 1.2 什么是 NoSQL?...................................................................................................................1 1.3 谁在用 memcached?............................................................................................................1 第二章:memcached 基本使用............................................................................................................2 2.1 linux 下编译 memcached..................................................................................................... 2 2.1.1:准备编译环境............................................................................................................2 2.1.2: 编译 memcached..................................................................................................... 2 2.2 memcached 的启动...............................................................................................................3 2.3 memcached 的连接...............................................................................................................5 2.4 memcached 的命令...............................................................................................................5 第三章 memcached 的内存管理与删除机制.................................................................................. 9 3.1:内存的碎片化.......................................................................................................................9 3.2: slab allocator 缓解内存碎片化........................................................................................... 9 3.3 系统如何选择合适的 chunk?............................................................................................. 9 3.4 固定大小 chunk 带来的内存浪费...................................................................

64,648

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧