map容器哪个效率高:指针还是内存池

z1z2z3z4 2008-12-18 05:43:40
现在需要用STL容器储存一些数据,对效率有一定要求。数据大小在1K左右。
容器可能采用map或hash_map,但现在不打算讨论这些容器的查找效率。
这里只讨论在容器中添加元素时,以什么方式添加,效率更高。
下面是可能采用的三种方式(伪代码):

1、STL元素+指针
===========================================================================
struct ITEM
{
int a;
int b;
int size;
char *data;
};
map<int, ITEM> DATA;//声明为储存ITEM对象,对实际数据是指针
int main()
{
char *data = new char[1000];//用new分配内存,相对于内存池方式慢了不少
ITEM item;//在栈内存区域构造对象,非常快
DATA.insert(item);//STL分配内存,构造对象,复制对象(浅拷贝,只复制了少数几个int,效率应该有保障)
//最后,栈内存的对象被释放,可以忽略
}
===========================================================================
2、纯指针
===========================================================================
struct ITEM
{
int a;
int b;
int size;
char data[1000];
};
map<int, ITEM*> DATA;//声明为储存指针
int main()
{
ITEM *item = new ITEM;//用new分配内存,相对于内存池方式慢了不少
DATA.insert(item);//STL分配内存,构造对象,复制指针
}
===========================================================================
3、纯STL元素,使用内存池(因STL不支持1K大小的内存池,所以内存池要另外实现)
===========================================================================
struct ITEM
{
int a;
int b;
int size;
char data[1000];
};
map<int, ITEM> DATA;//声明为储存实际对象
int main()
{
ITEM item;//在栈内存区域构造对象,非常快。但该相对包含1K数据
DATA.insert(item);//内存池分配内存,构造对象,复制对象。包含1K数据
//栈内存的对象释放,可以忽略
}
===========================================================================

对象储存后,还有使用时的效率问题,以及删除。
DX觉得哪种方式更好?
...全文
848 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
狂妄Beyond 2011-01-17
  • 打赏
  • 举报
回复
mark
effective100 2008-12-21
  • 打赏
  • 举报
回复
个人感觉,无论那种操作,像1k这样大的数据,最好少用栈内存,因为栈内存很有限。运行时,栈通常有某些预先设定的大小,而且是依赖平台的。
taodm 2008-12-19
  • 打赏
  • 举报
回复
我能给的建议就是动手而不是动嘴。“放弃全局内存池,采用局部内存池,以此大幅度减少锁的影响”,“大幅”二字只不过是你的臆测。
z1z2z3z4 2008-12-19
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 taodm 的回复:]
哎,基本上任何7*24工作的程序都应该自己接管内存管理的。所以,stl有没有用内存池就没啥意义了。
[/Quote]
一直想偷懒不开发内存池,看来,越是不希望发生的事,就越是可能会发生。。。

这个贴提供了一些经验,主要是锁的方面:
http://topic.csdn.net/u/20070722/22/5758B905-0C42-4854-97FD-4852C996E595.html
总结就是:
一个全局的内存池。采用临界区作为锁,在单核下内存池非常快。但在多核下,性能大幅度降低,只是比new/delete快一点。

我的初步想法是:放弃全局内存池,采用局部内存池,以此大幅度减少锁的影响,这样的内存池比new/delete快一个数量级或许是有可能的。

另外,既然内存池是局部的、特定应用使用的,那我想进一步把对象的大小作为池内存的大小,也就是一个内存刚好装一个对象,避免内存浪费。这是不是“对象池”?

您有何建议?
z1z2z3z4 2008-12-19
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 redleaves 的回复:]
特定情况下,内存池可以把性能提高一个数量级以上...
一般情况下,自己做内存管理都会比系统的内存管理快,STLPort就是个例子.
[/Quote]
服务器使用多核CPU或多CPU较常见。

在多线程情况下,需要用锁。在多核CPU或多CPU情况下,锁的效率可能会较大地影响内存池的性能。

这个帖实现了一个非常快的内存池,但最后在多线程多CPU核心情况下,性能只是比new/delete快一点。

http://topic.csdn.net/u/20070722/22/5758B905-0C42-4854-97FD-4852C996E595.html
Qlaiaqu 2008-12-19
  • 打赏
  • 举报
回复
引来大牛无数,学习,还没有深入STL
z1z2z3z4 2008-12-19
  • 打赏
  • 举报
回复
按 ugg 的提示,现在增加第四种和第五种方法:

1、STL元素+指针
===========================================================================
struct ITEM
{
int a;
int b;
int size;
char *data;
};
map <int, ITEM> DATA;//声明为储存ITEM对象,对实际数据是指针
int main()
{
char *data = new char[1000];//用new分配内存,相对于内存池方式慢了不少
ITEM item;//在栈内存区域构造对象,非常快
DATA.insert(item);//STL分配内存,构造对象,复制对象(浅拷贝,只复制了少数几个int,效率应该有保障)
//最后,栈内存的对象被释放,可以忽略
}
===========================================================================
2、纯指针
===========================================================================
struct ITEM
{
int a;
int b;
int size;
char data[1000];
};
map <int, ITEM*> DATA;//声明为储存指针
int main()
{
ITEM *item = new ITEM;//用new分配内存,相对于内存池方式慢了不少
DATA.insert(item);//STL分配内存,构造对象,复制指针
}
===========================================================================
3、纯STL元素,使用内存池(因STL不支持1K大小的内存池,所以内存池要另外实现)
===========================================================================
struct ITEM
{
int a;
int b;
int size;
char data[1000];
};
map <int, ITEM> DATA;//声明为储存实际对象
int main()
{
ITEM item;//在栈内存区域构造对象,非常快。但该相对包含1K数据
DATA.insert(item);//内存池分配内存,构造对象,复制对象。包含1K数据
//栈内存的对象释放,可以忽略
}
===========================================================================
4、STL元素+内存池(STL元素也是使用内存池)
===========================================================================
struct ITEM
{
int a;
int b;
int size;
char *data;
};
map <int, ITEM> DATA;//声明为储存ITEM对象,对实际数据是指针
int main()
{
char *data = new char[1000];//重载new,用内存池分配内存,性能取决于内存池实现(可预期非常快)
ITEM item;//在栈内存区域构造对象,非常快
DATA.insert(item);//STL分配内存,构造对象,复制对象(浅拷贝,只复制了少数几个int,效率应该有保障)
//最后,栈内存的对象被释放,可以忽略
}
===========================================================================
5、纯指针+内存池(STL元素也是使用内存池)
===========================================================================
struct ITEM
{
int a;
int b;
int size;
char data[1000];
};
map <int, ITEM*> DATA;//声明为储存指针
int main()
{
ITEM *item = new ITEM;//重载new,用内存池分配内存,性能取决于内存池实现(可预期非常快)
DATA.insert(item);//STL分配内存,构造对象,复制指针
}
===========================================================================
逸学堂 2008-12-19
  • 打赏
  • 举报
回复
TO ugg

LZ何不使用第三种方式中的内存池方法+第二种方法,结合两者的长处

哦,应该是这样:
在容器中储存对象指针,而对象中的1K数据用内存池。
这应该是新的方法。谢谢提供了这个思路,我一直没想到!

就是这样
taodm 2008-12-19
  • 打赏
  • 举报
回复
哎,基本上任何7*24工作的程序都应该自己接管内存管理的。所以,stl有没有用内存池就没啥意义了。
z1z2z3z4 2008-12-19
  • 打赏
  • 举报
回复
TO ugg

LZ何不使用第三种方式中的内存池方法+第二种方法,结合两者的长处

哦,应该是这样:
在容器中储存对象指针,而对象中的1K数据用内存池。
这应该是新的方法。谢谢提供了这个思路,我一直没想到!
redleaves 2008-12-19
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 z1z2z3z4 的回复:]
最近看到资料说,只有SGI的STL实现了内存池,其它的STL实现包括VC下的都并没有实现内存池。这个请DX确认一下?
[/Quote]
SGI的原版不知道,至少STLPort是有自己的内存池的,VC下的分配器好像直接用的new/delete...STLPort的性能很大程度上都得益于此.
z1z2z3z4 2008-12-19
  • 打赏
  • 举报
回复
最近看到资料说,只有SGI的STL实现了内存池,其它的STL实现包括VC下的都并没有实现内存池。这个请DX确认一下?
redleaves 2008-12-19
  • 打赏
  • 举报
回复
楼主的三种方式,需要复制的数据量完全不一样,能达到的功能也不同.就性能来说第二个方式应该是最快的.数据量最少.但它描述的内容也是最少,如果要每一项都指向唯一一个对象.那就要手工创建新对象,并不见得比3快....

另外:
[Quote=引用 11 楼 lann64 的回复:]
第二种。
至于第三种自己实现内存池,很怀疑能比new快多少。要是觉得new操作符多构造了一次,可以直接调用operator new来分配[/Quote]
特定情况下,内存池可以把性能提高一个数量级以上...
一般情况下,自己做内存管理都会比系统的内存管理快,STLPort就是个例子.
z1z2z3z4 2008-12-19
  • 打赏
  • 举报
回复
LZ何不使用第三种方式中的内存池方法+第二种方法,结合两者的长处
想不出如何结合?
如果整个对象用指针,就是第二种了。如果1K数据部分用指针,其余用STL内存池,就是第一种了。
但最近看到资料说,只有SGI的STL实现了内存池,其它的STL实现包括VC下的都并没有实现内存池。这个请DX确认一下?

关于map容器的效率,它效率高低是受其结构和算法决定。它与本帖提出的问题不相关,请勿再讨论它了。

关于三种方式,目前我初步觉得,性能差异和两个因素相关:

1、用指针的(一和二)的,用new/delete申请释放内存的效率是主要的决定因素。

2、用内存池的(三)的,复制的开销是主要因素。在添加元素时要复制,请注意,删除元素时,容器中的其它元素有可能需要进行旋转以求平衡,此时会产生很多复制。这是主要性能开销。

所以我也觉得,存储大的数据,用第二种方式应该是最快的,如果存储小的数据,第三种最好。

若有不同看法,尽可严重批评指正!
taodm 2008-12-19
  • 打赏
  • 举报
回复
实测!最简单也最有说服力的方法!
才3种方法而已,都试一下是很快的。至少你可以试前面2种,发现效率确实还不够就再试第三种嘛。
逸学堂 2008-12-19
  • 打赏
  • 举报
回复
看到上面有些人的回答,感觉太恐怖的。
套用一句老话,“知之为知之,不知为不知,是知也”。
LZ以及各位别尽信之.
关于红黑树实现的,
http://www.cnblogs.com/abatei/archive/2008/12/17/1356565.html
用C#语言实现的,不过理论描述比较清楚.

LZ何不使用第三种方式中的内存池方法+第二种方法,结合两者的长处,为啥非要分开.
yuyunliuhen 2008-12-19
  • 打赏
  • 举报
回复
没研究这么深 MARK
z1z2z3z4 2008-12-19
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 z1z2z3z4 的回复:]
2、用内存池的(三)的,复制的开销是主要因素。在添加元素时要复制,请注意,删除元素时,容器中的其它元素有可能需要进行旋转以求平衡,此时会产生很多复制。这是主要性能开销。
[/Quote]
这个说法仿佛是严重错误的,谁来纠正一下
wap21 2008-12-19
  • 打赏
  • 举报
回复
up
redleaves 2008-12-19
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 z1z2z3z4 的回复:]
服务器使用多核CPU或多CPU较常见。
在多线程情况下,需要用锁。在多核CPU或多CPU情况下,锁的效率可能会较大地影响内存池的性能。
这个帖实现了一个非常快的内存池,但最后在多线程多CPU核心情况下,性能只是比new/delete快一点。
[/Quote]
同步锁的问题当然是无可避免的.无论如何实现,锁的最小开销是有限度的.在内存分配中尤其明显(Win32下,CriticalSection的开销就占一次分配的30%以上)
所以唯一的方案是改进设计,从而避免锁的出现.但这并不是内存管理的问题.内存管理的目标只是提供开销(性能,内存,碎片等)尽可能小,管理尽可能方便的机制.如果你的设计中大量的锁总线来分配内存而造成性能瓶颈,那就属于设计不当了.为了避免这种问题,你可以采取一些中间手段来达成.比如你所说的局部内存池,再比如你可以采取批量分配的方式等.
关键是要选取一种适合的方式.唯有实践,分析,别无它法.
加载更多回复(13)

33,311

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 新手乐园
社区管理员
  • 新手乐园社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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