免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: voipexplore
打印 上一主题 下一主题

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

论坛徽章:
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
21 [报告]
发表于 2008-07-31 15:03 |只看该作者
刚又想到一点,回去验证下:

pthread_mutex_lock是C库函数,除非写成宏,否则是一定有至少一次函数调用开销的;

而stl实现是C++库,只要加上inline就可以免除函数调用开销。


相对来说,函数调用开销是相当大的,至少也应该是CPU原子操作指令的两、三倍吧(两次跳转,几乎一定会导致cache失效——这还不考虑参数压栈等开销)。

一次(封装过的)锁调用,恐怕至少80%以上的开销都花在原子操作指令之外的地方了……
如果能inline,显然可以是得到极大的性能提升的——哪怕stl使用的是和pthread_mutex_lock一模一样的内部实现。

论坛徽章:
0
22 [报告]
发表于 2008-07-31 15:14 |只看该作者
同等条件下func开销是32,微不足道.

论坛徽章:
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
23 [报告]
发表于 2008-07-31 17:08 |只看该作者
晕,思路走偏了


1、某大佬说过,不使用同步机制就不可能写出正确的多线程程序。(我想不访问任何公有数据的多线程程序应该还是可以写正确的吧)
要同步,就不可能不用到原子操作。
所以,要么gcc的stl根本不支持多线程,要么它就确实用了锁。



2、既然要一定要使用锁,那么出现性能差距只有两个可能:
a、算法牛,锁用得恰如其分(刚看过loki,为跨平台,简单调用了操作系统提供的接口而已:没什么牛的)
b、根本就没有执行锁

刚才思路一直偏a上了。



3、那么,只剩一种可能: 根本就没有执行锁操作。

但,多线程程序,没有同步机制,如何保证正确性?

那么,如果根本就没有访问共享数据呢?

内存池那里不用看,我自己实现过,一旦执行到,锁绝对逃不掉。

那么,楼主的代码是否真的一定会导致string用到内存池?

看这里:
for(i=0;i<EXECUTE_NUM;i++)
{
        string sdsd(pp);
}

显然,sdsd是一个局部变量,它分配于栈上,这里不涉及内存池;
然后,pp可能是另一个string或char *;如果是string,那么这个拷贝动作只有在一方被改写时才会真正执行——也就是说,直到此时才会有内存分配动作;此前大伙一直在用同一块存储区。

另外,根据c++作用域定义,sdsd在for循环中应该仅生成了一次吧?
那么,即使它真的动用了内存池,这点消耗也还不至于显示出来。
想要频繁触发它的构造/析构动作,除非用{}把它括起来。
不过,在上面的代码中,即使频繁触发了它的构造/析构动作,内存分配也不会发生。

[ 本帖最后由 shan_ghost 于 2008-7-31 17:34 编辑 ]

论坛徽章:
0
24 [报告]
发表于 2008-07-31 19:17 |只看该作者
晕倒 看主贴,已经说过 用valgrind证实过了的确是调用了malloc。
兄弟 写个main函数,里面只有string aa("123");用valgrind跑下好不好,
不要想当然,水平够高了才能想当然。
其实不用valgrind,我也知道,它会潜在调用malloc,就是怕你看不明白,才特意说明了一下,
还怀疑。
多线程分配string对象,也证实了分配的内存大小是一样的,证实了使用的同一个内存池。

里面的pp是char *,知道stl里面引用计数,迟写技术用的多。

哦,漏反驳了一句,那个什么根据c++作用域什么的只生成一次?
晕,这么明显的大括号在哪里放着。

写个小程序吧:
class A{public: A(){printf("hello\n"}};
int main(){int i=0;for(i=0;i<3;i++){A a;}return 0;}
我就不测试看结果了,这么简单的结果,我就不说了,跑跑看看,不要想当然。

。。。。。。。。。。。。。不多说了。。。。。。。。。。。。。。。。。。

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

论坛徽章:
0
25 [报告]
发表于 2008-07-31 20:45 |只看该作者
我一直都没懂多线程不安全指的是什么,那像下面的代码
void *fn(void *)
{
string s = “123”
vector<int> a;
a.push_back(1);
s[2] = 5;
cout<<s;
cout<<a[0];
}
是否是安全的?

论坛徽章:
0
26 [报告]
发表于 2008-07-31 21:02 |只看该作者
csdn上查到的
摘录:  
  在所有的主流STL实现方案中,几乎所有的容器都是线程安全的:  
   
  1).一个线程读写一个实例时,另一个线程可以读写另一个实例。  
   
  2).多个线程可以在同时读同一个container。  
   
  3).多个线程写同一个container时,你应该负责安排互斥性操作。  
   
     
   
  一个特例是std::string。在一些STL的实现厂商(包括MS   VC6),使用的是带引用计数的string!   这就意味着可能有两个std::string的实例共享着同一块底层数据。这使得前面说的第一个规则被打破!  
   
     
   
  看一下这样的代码:  
   
  string   s1=   “abcd”;  
   
  string   s2   =   s1;  
   
     
   
  在引用计数的实现版本中,这几句话意味着:先分配一块内存给”abcd”,一个引用计数的数;s1和s2都将引用这块内存,引用计数将为2。引用计数的本意是在把strings传出functions时优化copy行为。  
   
  但是这种算法并不是线程安全的!  
   
  如果你将s2传给另一个线程,那么就很可能有两个线程企图修改这同一块内存!那将会有不可预料的行为发生。  
   
  理论上,你可以在两个线程之间增加线程同步,但是这个同步的代价将会大于你从引用计数中获得的好处!  
   
  这就是为什么主流的STL厂商不再使用引用计数的原因。比如,Dinkumware   STL   shipped   with   VC7

论坛徽章:
0
27 [报告]
发表于 2008-07-31 21:08 |只看该作者
楼上的问题和该贴无关,不是讨论多线程操作相同string对象的问题。
是标准库内存池线程安全的问题,以及string对象分配背后的性能开销问题。

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

struct timeval begin;
struct timeval end;
struct timeval interval;
struct timeval ba ...


LZ的性能比较是不合理的 (new的内存没有delete),试一试下面的比较。我相信结果跟string是很近似的。
gettimeofday(&begin,0);
for(i=0;i<EXECUTE_NUM;i++)
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//char *wop=new char(11);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//delete wop;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char* wop = (char*)malloc(11);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free(wop);
}
gettimeofday(&end,0);
timersub(&end,&begin,&interval);
timersub(&interval,&base,&interval);
printf("malloc[%d.%d]\n",interval.tv_sec,interval.tv_usec);

论坛徽章:
0
29 [报告]
发表于 2008-08-01 09:14 |只看该作者
关键的对比不是string和malloc,是string和加解锁........................................
valgrind的测试,只是证实下面内存池的存在,所以猜测低层内存池在线程安全和性能之间的权衡...............

论坛徽章:
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
30 [报告]
发表于 2008-08-01 09:24 |只看该作者
原帖由 voipexplore 于 2008-7-31 19:17 发表



晕倒 看主贴,已经说过 用valgrind证实过了的确是调用了malloc。 //究竟是哪里调用的malloc?

//这里是会分配几次内存:_alloc本身就需要分配内存;然后要为123分配空间(通过_alloc)
兄弟 写个main函数,里面只有string aa("123");用valgrind跑下好不好

//不好意思,正是哥们你用想当然的方法去研究string的效率,在下只是指出几个疑点而已。
//解决这几个疑点之前,在下不能相信你给出的任何结果
不要想当然,水平够高了才能想当然。
其实不用valgrind,我也知道,它会潜在调用malloc,就是怕你看不明白,才特意说明了一下,
还怀疑。
多线程分配string对象,也证实了分配的内存大小是一样的,证实了使用的同一个内存池。

里面的pp是char *,知道stl里面引用计数,迟写技术用的多。


//非常不好意思,需要把这个小东西跑跑的是你——敬告阁下,千万不要想当然
哦,漏反驳了一句,那个什么根据c++作用域什么的只生成一次?
晕,这么明显的大括号在哪里放着。

写个小程序吧:
class A{public: A(){printf("hello\n"}};
int main(){int i=0;for(i=0;i<3;i++){A a;}return 0;}
我就不测试看结果了,这么简单的结果,我就不说了,跑跑看看,不要想当然。

//或者我不得不给你科普一下:C++标准里,整个循环体构成一个作用域,循环不结束,作用域不结束。
//初始化语句在这个作用域里只会执行一次——这是常识。
//如果还不明白,拜托阁下给我解释下下边这个循环的执行过程:
for (int i = 0; i >= 100; i++)
{
    int j = 0;
    j += i;
    printf("%d, %d",i,j);
};

最后:string、alloc等stl元素的实现都比较复杂,且每个厂商都会有自己的考虑。想测出点什么,想办法绕开所有无关的优化点是极其重要的。
在下从一开始不相信阁下有设计出检验string内部内存分配性能的用例的资质、到后面被你的大话蒙蔽、再到重新认识您没有这个能力,走了一个无聊的大圈。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP