免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 26717 | 回复: 50
打印 上一主题 下一主题

std::string多线程下为什么不coredump? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-07-31 09:21 |只看该作者 |倒序浏览
我使用gcc3.3.3版本。
std::string低层使用内存池,即第一次使用std::string对象的时候,会申请960字节的内存池(一个长度为16的数组,每个里面的内存长度分别为8、16、24、32.....128,等差队列和=((8+12/2 )*15=960)。以后再次使用string,则直接从池中申请。这点可以通过valgrind验证。
同样做性能测试,string对象的分配性能明显远远好于malloc,下面的确存在内存池。

在多线程环境下测试,同样valgrind验证内存池的大小没有变化,也就是多线程下,每个线程中的string对象都是从共同的内存池中申请内存,但是性能测试证明,这个低层内存池没有加锁,string对象的分配性能远好于加解锁性能。简单想,低层内存池也不可能加锁 ,作为标准库,想跨平台,也不可能有加锁的实现。

但是,为什么多线程下同时分配string对象,程序不coredump,为什么啊 为什么????????????????????
来个高手指点下啊。

论坛徽章:
0
2 [报告]
发表于 2008-07-31 11:31 |只看该作者
现在的stl实现大多是线程安全的吧,包括vc的。
http://www.sgi.com/tech/stl/thread_safety.html

论坛徽章:
0
3 [报告]
发表于 2008-07-31 12:30 |只看该作者
对,看起来是线程安全的。从测试结果不coredump就知道。

关键问题就是它如何实现了线程安全,它并没有加锁??
实现代码里有pthread_mutex_t相关代码,测试感觉并没有加,std::string的对象分配性能远好于加解锁操作。
实现代码里也有pthread_spin_lock相关代码,这个应该是多核下使用的吧。
有类似如下的汇编__asm__ __volatile__ ("lock;......",看起来也是一种高效的锁实现。

为什么性能这么好的线程安全实现在其它内存池里找不到痕迹?apr里 boost里 loki里都没有,多线程下,它们都有明显的锁开销。

std::string下的allocator如何实现了线程安全?????

[ 本帖最后由 voipexplore 于 2008-7-31 12:46 编辑 ]

论坛徽章:
0
4 [报告]
发表于 2008-07-31 13:04 |只看该作者
sgi默认的allocator就是thread-safe
http://www.sgi.com/tech/stl/Allocators.html
看了一下libstdc++的代码。是用gcc的__gthread_mutex_lock函数保证的。
而__gthread_mutex_lock在不同平台上是不同实现的。
x86是CPU的原子操作,比如win32上的InterlockedIncrement.
arm-linux上就是用的pthread_mutex_lock了。

论坛徽章:
8
CU大牛徽章
日期:2013-04-17 10:59:39CU大牛徽章
日期:2013-04-17 11:01:45CU大牛徽章
日期:2013-04-17 11:02:15CU大牛徽章
日期:2013-04-17 11:02:36CU大牛徽章
日期:2013-04-17 11:02:58技术图书徽章
日期:2013-12-04 10:48:50酉鸡
日期:2014-01-03 10:32:30辰龙
日期:2014-03-06 15:04:07
5 [报告]
发表于 2008-07-31 13:20 |只看该作者
1、stl内存池有很多种实现,目标是尽量优化小对象的内存分配;你的数据显然没有真正反映gcc的内部实现,建议自己看看源码。



2、请注意,string是一个typedef;它是basic_string的一个特化版。
   basic_string的最后一个模板化参数用于指定内存池;默认内存池一般是全局公用的。

   另外,不仅是basic_string,凡声称自己可以支持多线程的stl组件,都会通过traits(似乎是这个词儿)自动在多线程环境下加入锁保护——当然,你也可以手工去掉它。
   具体实现各家皆有细微差别,看源码即知。


3、关于性能,论坛上颇有一番“一群阔人说要读经,嗡的一声一群狭人也说要读经”的气象。


先反问一下:知道“锁操作慢”是搞什么的人才有资格说的吗?

——对那美克星的亚光速飞船来说,F22比猎豹眼里植物生长的速度还慢。



看下数据(原始数据来自IBM;是在下为写另一帖子找的,现在原文不在,就暂时不给链接了):

对PIII 700来说,这款CPU每时钟周期可以执行两条整数指令,它的时钟周期是1.4ns。
这款CPU有两种原子操作指令,消耗时间分别是7xns和14xns的样子(具体数字忘了)。
作为对比,一旦此CPU要访问的数据不在cache中,那么哪怕只从内存中读取一个字节,也需要100多ns。

换言之: 一次原子操作,需要消耗的时间基本和一次cpu二级缓存未命中相当。



再做个对比:现在最好的磁盘,平均寻道时间大约是9ms不到,仅仅是在两个相邻磁道间移动磁头就需要消耗2ms;然后,它还需要等待对应扇区转到磁头下——对于10000转的硬盘,这个时间(平均等待时间)是3ms。

加上其他种种延迟,缓存未命中时,从磁盘读取一个字节平均需要至少21ms。
这段时间,足够老掉牙的PIII 700执行30万条原子指令(或3000万条整数指令)。



进一步的,锁的实现可分为两种:
一种是简单调用原子操作指令实现基本逻辑,由用户负责调度算法;另一种则还要通知操作系统内核,以将合适的进/线程挂起或恢复执行。

于是,一个锁操作,全部消耗可能是一次函数调用(可以被inline优化掉)+一个原子指令;也可能是一次用户态-内核态-用户态切换+若干次函数调用+一个原子操作。

这点开销,与一个本该inline而没有inline的函数所造成额外消耗基本在同一个数量级上。



总结:
1、锁速度慢是那些有能力玩优化玩到单条机器指令级的算法大佬们才有资格说的(或者说:只有当你可以准确估算出自己的代码将消耗多少个指令周期、有多少次CPU二级缓存未命中时,谈论锁对性能的影响才有意义——这时候,你也往往必须自己用CPU原始原子操作指令完成保护以提高性能了)。

2、对普通用户来说,请记住这句话:“和普通函数调用相比,锁操作基本没有额外消耗”。

3、论坛上那些不分情况不看场合张嘴就是“锁很慢”“xxx操作效率低”的牛人,他们其实对速度、性能、效率等等几乎毫无概念,只是人云亦云而已。切莫被他们误了。

论坛徽章:
8
CU大牛徽章
日期:2013-04-17 10:59:39CU大牛徽章
日期:2013-04-17 11:01:45CU大牛徽章
日期:2013-04-17 11:02:15CU大牛徽章
日期:2013-04-17 11:02:36CU大牛徽章
日期:2013-04-17 11:02:58技术图书徽章
日期:2013-12-04 10:48:50酉鸡
日期:2014-01-03 10:32:30辰龙
日期:2014-03-06 15:04:07
6 [报告]
发表于 2008-07-31 13:37 |只看该作者
看看下面这个数据,不知楼主是靠什么实验数据断定gcc的多线程实现没有使用锁的?


http://bbs.chinaunix.net/viewthr ... ;extra=&page=27
并发200用户:
$time tt 200

real    0m3.551s
user    0m3.400s
sys     0m0.130s
$time tt1 200

real    0m6.034s
user    0m5.900s
sys     0m0.120s

根据你的代码,200 × 100000 = 2千万次加解锁操作,仅仅耗用了 6.034 - 3.551 = 2.483秒
有兴趣的可以算算一次加锁需要多少时间消耗。

————————————————————
我自己算出来吧:
2千万次加解锁,4千万次系统调用,2.483秒:这个数据意味着,在那台NB服务器上,一次加锁或解锁调用仅消耗了0.000,000,062,075秒。

平均62.075纳秒——包括用户态-系统态切换以及线程调度。

这就是加、解锁的实际消耗——你测得出来吗?用什么方法测的?

论坛徽章:
0
7 [报告]
发表于 2008-07-31 13:51 |只看该作者
呵呵,正好,各位看下下面的代码测试性能有问题吗?
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;

struct timeval begin;
struct timeval end;
struct timeval interval;
struct timeval base;

gettimeofday(&begin,0);
for(i=0;i<EXECUTE_NUM;i++)
{

}
gettimeofday(&end,0);
timersub(&end,&begin,&base);

gettimeofday(&begin,0);
for(i=0;i<EXECUTE_NUM;i++)
{
        pthread_mutex_lock(&fastmutex);
        pthread_mutex_unlock(&fastmutex);
}
gettimeofday(&end,0);
timersub(&end,&begin,&interval);
timersub(&interval,&base,&interval);
printf("fast lock[%d.%d]\n",interval.tv_sec,interval.tv_usec);

gettimeofday(&begin,0);
for(i=0;i<EXECUTE_NUM;i++)
{
        char *wop=new char(11);
        //delete wop;
}
gettimeofday(&end,0);
timersub(&end,&begin,&interval);
timersub(&interval,&base,&interval);
printf("malloc[%d.%d]\n",interval.tv_sec,interval.tv_usec);

gettimeofday(&begin,0);
for(i=0;i<EXECUTE_NUM;i++)
{
        string sdsd(pp);
}
gettimeofday(&end,0);
timersub(&end,&begin,&interval);
timersub(&interval,&base,&interval);
printf("string construct[%d.%d]\n",interval.tv_sec,interval.tv_usec);
结果执行时间比例是:  762:949:743
base的时间是36。

请看上述的测试有问题吗?
人云亦云的事情还没干过,整天忙着写代码,低劣的经验谈从来不屑一顾,就像我不能相信锁的开销不能测试一样。

忘了说明,这里EXECUTE_NUM我取的是10000,再大100倍对测试的结果没影响,类似的。

[ 本帖最后由 voipexplore 于 2008-7-31 13:53 编辑 ]

论坛徽章:
8
CU大牛徽章
日期:2013-04-17 10:59:39CU大牛徽章
日期:2013-04-17 11:01:45CU大牛徽章
日期:2013-04-17 11:02:15CU大牛徽章
日期:2013-04-17 11:02:36CU大牛徽章
日期:2013-04-17 11:02:58技术图书徽章
日期:2013-12-04 10:48:50酉鸡
日期:2014-01-03 10:32:30辰龙
日期:2014-03-06 15:04:07
8 [报告]
发表于 2008-07-31 13:57 |只看该作者
那么,楼上的,string内部内存分配有无加锁,您又是怎么测的?

论坛徽章:
8
CU大牛徽章
日期:2013-04-17 10:59:39CU大牛徽章
日期:2013-04-17 11:01:45CU大牛徽章
日期:2013-04-17 11:02:15CU大牛徽章
日期:2013-04-17 11:02:36CU大牛徽章
日期:2013-04-17 11:02:58技术图书徽章
日期:2013-12-04 10:48:50酉鸡
日期:2014-01-03 10:32:30辰龙
日期:2014-03-06 15:04:07
9 [报告]
发表于 2008-07-31 14:01 |只看该作者
哦,刚注意,就是下面那个string sdsd(pp) 和 new比较吗?

new本身就有锁,不然多线程下你的堆还不被捣毁了。谢谢。

论坛徽章:
0
10 [报告]
发表于 2008-07-31 14:02 |只看该作者
可以再说下,循环里加i++语句,它本身的测试结果是11,
和base吻合,base是3次普通指令,是36。

系统调用,我取得pipe做测试,结果是21928。

可以看出,系统调用的开销之大,尽量使用缓存,少调系统调用,另我的机器是2.6内核linux,thread库已经改变 ,phread不阻塞,不陷入内核,没有系统调用的开销那么大。

同样i++和malloc的测试结果也说明,单线程下,内存池引入对性能提高的重要性。
但是string的测试结果,如果加锁,那么它的测试结果一定大于锁开销,而我从没有哪一次测试结果,string的结果能略高于加解锁操作??
如果哪位了解string的线程安全如何实现的,请赐教,多谢。。。。。。。。。。。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP