@阿凡
Server S把Client A的NAT信息发给Client B,然后B直接给A的NAT发一条信息即可,具体原因可以再看一下文中对Cone NAT的介绍。
说的不错,pool的方式存在的问题是挺多。不过对于pool的效率高不到哪去的观点我不敢认同,针对大量小数据的分配,有时候还必须得使用pool的方式,不然ptmalloc、tcmalloc、jemalloc等不会维护复杂的内存分配方式@chipset
如果我没记错的话malloc自始至终都有自己的一套pool策略,所以我所说的malloc性能提升并非指的这个。
不过第三方稳定高效的allocator实现可能是标准库作者放弃pool的一个原因@egmkang
re: 论epoll的使用[未登录] peakflys 2014-08-06 18:53
@呆贼
“如果socket有写事件,不要直接放入epoll中”?
你的意思应该是epoll不监听写事件吧?
如果是这个意思的话,你说的可以解决LT模式下CPU空转的问题,不过会引入新的问题,也就是频繁地修改epoll事件(刚开始EPOLL_OUT不监听,后来send失败后Add EPOLL_OUT事件,等监听发送完毕,还要再Del掉EPOLL_OUT事件).
已修正,谢谢@wencan
@zdd 两者没有本质区别。
*(Derive*)&b 先取地址,强转地址类型,然后再取内容,同汇编实现基本是一一对应起来的;
(Derive&)b是使用C式的强转直接把内容转成Derive的引用,编译器帮你翻译成的汇编代码实现应该和上面的是一样的。
仅作例子讲解@Richard Wei
@zuhd
如果使用成熟的网络库大可不必特别关注连接超时的问题,但是如果是自己按实际需要重新写的网络层,那么网络层的容错性和健壮性就需要通过很多指标来考核,前期连接超时如果很多的话,就需要排查一下双方网络层代码是否有异常。
最后一个例子不是什么坑,只不过是ISO未定义的情况,其实要实现基类指针正确找到派生类this偏移的方式很多种,记下大小,加上固定偏移就是一种方法,当然还有其他很多种方法,应该没什么特别的复杂,这里顶多告诫大家要慎用,因为是标准未定义的行为,crash or not crash,compiler decides it。建议博主修改之。
刚才构造了一个例子,如下:
#include <cstdio>

class Base
{
public:
Base(const int a=10) : a(a)
{
printf("Base constructor,this addr: %p\n",this);
}
virtual ~Base()
{
printf("Base destructor, a=%d, this addr: %p\n",a,this);
}
int a;
};
class Derive : public Base
{
public:
Derive(const int a=20,const int b=20) : Base(a),b(b)
{
printf("Derive constructor,this addr: %p\n",this);
}
virtual ~Derive()
{
printf("Derive destructor, b=%d, this addr: %p\n",b,this);
}
int b;
};
int main(int argc, char* argv[])
{
Base *pb = new Derive[3];
delete [] pb;
printf("over\n");
return 0;
}
特意在基类和派生类里各加了一个成员变量,来增大两者可能的指针偏移。
结果证明 delete 多态的指针后,内存正常释放,而每个派生类对象的this指针也均无差错!
编译器:g++ (GCC) 4.4.6 20120305 (Red Hat 4.4.6-4)
对于博主最后一个例子所说的“但是Base*类型的指针式不能正确计算出Derived数组的10个析构函数需要的this指针的位置的”,请问为什么不行呢?难点在哪?
re: 关于hash_map的一点感悟 peakflys 2013-05-14 17:38
呵呵,这种代码大点的系统,很多都是存在的,而潜在的错误可能还没爆发出来@egmkang
@syd
其实不然,编译器产生的构造函数很多时候是nontrivial的,
例如:成员变量是含有构造函数的类类型,含有virtual function等等
@jxhgzs
请提供你的编译器版本,因为这是C++98标准规定的操作,如果你的编译器产生的代码行为同文中描述有出入,只能说明你的编译器没按照98标准来实现。
@noname
文中的结论是基于C++98标准的编译器验证结果,而VS2012是根据最新的C++11标准实现的,新标准对于这种情况的规定有没有变动尚未验证。
PS:你提供的汇编代码应该是和文中的结论吻合的吧?
看多了就很自然了@zuhd
@tangfu
其实这个不是编译器相关的,标准化委员会细化规则的程度大大超过你的想象。
摘自:ISO/IEC 14882:2003(E) 5.3.4 - 15
— If the new-initializer is omitted:
— If T is a (possibly cv-qualified) non-POD class type (or array thereof), the object is default-initialized(8.5). If T is a const-qualified type, the underlying class type shall have a user-declared default constructor.
— Otherwise, the object created has indeterminate value. If T is a const-qualified type, or a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of const-qualified type, the program is ill-formed;
— If the new-initializer is of the form (), the item is value-initialized (8.5);
恩,这个网站提供了很好的C++参考文档@Wilbur
其实现在国内很多公司的笔试题都很学究,真正考察在工程项目中使用的比较多的那些语法、算法或者一些编程技巧很少、很浅……@zgpxgame
re: 论epoll的使用 peakflys 2013-02-26 16:28
经过讨论最终结论我认为:ET模式在网络层方面的效率肯定比LT要高。
主要表现在:
1、网络IO比较小时,send buffer表现为一直可写,如果网络主循环没有延时操作的话,epoll_wait每次调用都会马上有事件返回,导致不必要的CPU空耗,如果加入延时处理,对于一些实时性要求比较高的操作会受到影响,必须耗费额外的逻辑处理。
2、在网络IO比较大,尤其是连接数比较多的时候,每次epoll_wait调用时LT模式肯定比ET模式多,因为之后需要对ready list 进行遍历处理,如果处理逻辑比较复杂,或者之前反馈的事件数LT比ET多很多的话,这时候效率差异就比较明显了。

ET模式在网络主循环处理的效率肯定比LT模式要高,至于高多少,视具体应用和具体实现而定。当然ET模式的代价就是增加了网络层的逻辑处理复杂度,必须手动维护fd当前的状态,在数据发送时也不能像LT模式那样直接丢到fd应用层的buffer中。当然武器是好武器,关键还是看用的人,如果非要把宝剑当菜刀使,那也只能沉默了……
其实这个问题我之前在一篇blog里已经讨论过(http://www.cppblog.com/peakflys/archive/2012/08/26/188344.aspx)
我现在的结论是:ET模式在网络层方面的效率确实比LT要高。
主要表现在:
1、网络IO比较小时,send buffer表现为一直可写,如果网络主循环没有延时操作的话,epoll_wait每次调用都会马上有事件返回,导致不必要的CPU空耗。
2、在网络IO比较大,尤其是连接数比较多的时候,每次epoll_wait调用时LT模式肯定比ET模式多,因为之后需要对ready list 进行遍历处理,如果处理逻辑比较复杂,或者之前反馈的事件数LT比ET多很多的话,这时候效率差异就比较明显了。

还是那句话,ET模式在网络主循环处理的效率肯定比LT模式要高,至于高多少,视具体应用和具体实现。当然ET模式的代价就是增加了网络层的逻辑处理复杂度,必须保证时刻知道fd当前的状态。
re: Libjingle库 综述 peakflys 2013-01-31 11:21
文章黑体字部分还有图片来自Google的英文开发文档,文档首页见:https://developers.google.com/talk/libjingle/index @免费空间
re: 关于最优无限循环的考证 peakflys 2013-01-09 13:15
恩?@花
re: 关于最优无限循环的考证 peakflys 2013-01-06 14:07
在硬件性能和编译器优化能力不断提升的今天,程序员的门槛一降再降,甚至出现了半月培训即可入岗的事情,但是如果想要写出优质高效的代码,就一定要熟悉你所使用的编译器,不能完全寄望于编译器的优化,养成良好的书写习惯才是王道。@zgpxgame
re: 关于最优无限循环的考证 peakflys 2013-01-05 17:05
上面的汇编代码是建立在GCC编译未加-O优化,VS用的也是debug版本,目的是为了说明 所谓广为流传的for(;;)比while(1)效率更好的说法是编译器相关的,当然GCC和VS下编程同意网上说的无限循环优先选用for(;;),至于VS的release版本,还有其他编译器平台,有兴趣的可以自己去验证一下。@Lo
其实这种情况实现的方法有很多,例如backtrace等等,但是这种情况在服务器上试问有什么意义?通过这种方法打出来的东西远远没有OS dump出的程序core文件包含的信息多……
PS,这种情况在C/S架构的C端才有相应的应用价值,通过网络 把远端客户端dump出的信息传送过来,便于客户端查错而已。
ack是TCP机制提供的一种接收反馈,对端接收发送的数据包会自动反馈回一个ack,这个过程上层app是没有参与的,所以单从一个ack来看是不知道对端app有没有取到刚才传送的数据,但是也有一个办法可以大体上猜到对端的使用,就是根据多个ack反馈,由窗口大小的改变来揣测。举个例子,对端窗口大小为65530个字节,我现在传过去10个字节,对端回复一个ack,但是这个ack不代表刚才传的10个字节对端app已经取到,但是如果我现在再传10个字节,如果对端回复的ack中window大小是65520,这时我可以猜出刚才传的数据已经被对端app取到了,这也就是原文中的意思:对端反馈回来ack,只代表对端接收到了数据,不代表上层应用程序取到了数据。@Sunny
re: 未来软件结构(讨论帖) peakflys 2012-12-14 11:22
对于嵌入式这一块没有特别关注,所以也就没有说,不过据我了解,近一两年随着嵌入式硬件的性能提升,很多嵌入式程序都已经不用汇编或者C开发了,不过5年或者10年之内感觉嵌入式 应该没有什么大的变化,个人意见……@haskell
re: 未来软件结构(讨论帖) peakflys 2012-12-10 10:44
LO说的没错,cpu连同GPU散热导致的手机温度升高,暂时是没有什么好的解决办法,不过相比于以Browser为轴心扩展各种软件 为用户带来的便利性和傻瓜化的操作感而言,那个问题5年内应该会得以解决,办法当然很多:摩尔效应继续,器件能效比提高,新型移动散热辅材的出现等等……@LO
re: 未来软件结构(讨论帖) peakflys 2012-12-07 10:03
贴中是我前天无聊时结合自己的想法写出来的,大家可以提出自己未来的软件结构,也欢迎大家拍砖
re: 未来软件结构(讨论帖) peakflys 2012-12-07 10:01
现在部分手机型号已经和平板无异,而平板和PC的区别,这个还真不好严格区别@alex
re: 未来软件结构(讨论帖) peakflys 2012-12-07 09:58
续航能力确实是现在移动设备的短板,这个我表示认同,但是这一两年来手机电池电量从700mA/h一路飙升到3300mA/h(摩托刀锋系列),未来5年这个还真不好说。退一步讲,就算电池技术没有什么突破,但是现在的ipad mini都可以达到12小时的连续使用,未来的手机完全可以做成折叠式的平板(折叠式屏幕已在三星Galaxy部分机型上使用),这样功能即强劲,携带也便捷,电池电量也可通过扩大体积的形式规避@LO
re: 未来软件结构(讨论帖) peakflys 2012-12-06 14:00
“有”和“普及”相差很远,上面我的5年后的猜想是B/S成为主流,依据有二:一则因为现在的电子设备不具有上网功能的基本可以直接判死刑,而联网的话 浏览器可以说是必不可少的,移动设备的互联互通,通过浏览器是最直接、最方便的;二则因为现在各种软件的微型化,甚至可以作为一个插件来使用,这样用户操作也方便化,傻瓜化,符合软件发展的方向,符合大众使用的最终需求,而之所以说大概5年就可以实现,是因为现在很多厂商好像都有这样的共识,例如Google,可以说google对chrome的期望很高的,无论是在平板还是手机或是PC,现在chrome的表现都还不错,而chrome的设计就是为此而生的,它采用一标签一进程的形式,一则可以加快网页等的执行速度,二则可以方便插件化的程序高效、安全运行,当然这样的代价自然是内存的极大占用很大,浏览器自带进程管理器,可以随时杀浏览器开启的进程,俨然一个小生态系统 @bill gates
呵呵,滑动窗口英文名称是“sliding-window”是TCP协议的一部分,因其大小根据对方的ACK来动态调整,故称之为滑动窗口,这个称呼应该是很贴切很形象的,并且目前几乎所有关于网络的书籍都会翻译成滑动窗口。recvbuf和sendbuf是为了说明程序中设置的buffer大小和滑动窗口的关系我特意加上去的,不然估计有不少人像我之前一个同事一样只知道设置接收缓冲区和发送缓冲区,而不知道它们真正执行的过程 @kaka
re: UDP,TCP打洞技术 peakflys 2012-11-27 12:35
不错,讲的很详细,但是如果是转载的,希望博主能够把转载的源地址附上。
24那个值看一下汇编 或者 info frame 看一下堆栈帧也可以看出来 但是你计算的不对,上面的编译环境使用的寄存器是rbp、rsp、rip(从上面汇编也可以看出来) 这些都是64位的,24的具体计算过程是这样的:a数据扩展成4个int是16个字节,然后加上test函数调用时压进堆栈的rbp 8个字节,总共是24个字节,这个位置就是调用test函数后的返回地址,修改内容即可,具体可以用gdb调试看一下 @zuhd
re: 论epoll的使用 peakflys 2012-09-02 11:36
@zuhd
移出的代价必然大于轮询的代价,但是如果LT模式不做写事件优化的话,是在一定程度上影响效率的(影响的程度和挂载的socket数量有关),这种影响首先表现在轮询的次数上,其次(也应该说主要)是你的发送函数上调用上,因为不管有没有消息需要发送,只要send buffer不满,写事件都会触发,你所封装的发送函数都会调用。
re: 论epoll的使用 peakflys 2012-08-30 15:48
恩,看来服务器性能还不错,我们单网关设计是5K连接,不过使用的是线程池,单个线程挂512个socket,在加上网络主循环有相应优化,所以LT模式影响不大,留给上层逻辑很大的扩展空间 @唐诗
re: 论epoll的使用 peakflys 2012-08-30 14:11
恩,唐诗兄在socket上加入标记位的办法是可以很好解决ET模式的写问题(上述代码中唐诗兄应该加上write之后 0==n 的情况,及时断掉正常中断的socket,而不是认为写正常,马上调整发送缓存)
谢谢唐诗兄的指教,不过如果使用LT模式,唐诗兄会发现更简单,呵呵。不知道你们一个网络主线程挂载多少socket? @唐诗
re: 论epoll的使用 peakflys 2012-08-30 07:34
谢谢唐诗的回复和指教,你指出的
a问题:在文中写ET模式时已经说了一部分,不过没有说写事件处理完之后,send buffer仍然可写时怎么处理,因为这个本身就是我认为的ET模式弊端之一,因为挺麻烦也挺易出错。
b问题:当时写这篇短文时确实没有特别考虑,不过在评论里面 春秋十二月仁兄指出了这个问题,唐诗兄给出的方法是每次把数据写完之后把它移出epoll监听队列,以后有新的写数据时再加入写事件到队列,不过个人感觉这种方法不是很理想,除了自己写着难受之外,因为从2.6.10内核之后 epoll内部队列的数据结构变成了RB_TREE,游戏中写数据很频繁(尤其是大规模玩家在线时),这样频繁的调整RB_TREE,性能损耗应该会不小。我在给春秋十二月仁兄的回复中给出了我的大致解决方法(参看上面评论),有什么不完整或者不对的地方,还请唐诗兄指教或者邮件交流 peakflys@gmail.com
至于唐诗兄说的et要比lt简单,这个可能是用et用的多了,很多细节错误已经有了自己固定成熟的解决方案了才说出这样的结论。ET如果保证每次触发的事件都可以及时有效的处理完全(当然 个人认为不容易,有时候还要自己处理一些本该TCP处理的东西)ET模式还是可以作为首选的,否则会表现出通讯过程中应用上层各种诡异的问题…… @唐诗
re: 论epoll的使用 peakflys 2012-08-28 10:51
@春秋十二月
谢谢春秋仁兄的指教,我是这么认为的。send buffer不满时触发的写事件,应该不至于引起CPU的占用过高(OS里本身也有很多纳秒级的死循环),如果过高说明轮询时的处理函数太耗CPU了,应该是可以优化的,另外轮询时间也可以设置的长一些,当然有些应用需要这么准确、及时。如果这样的话,我认为可以这样改进:在一次网络主循环里调用两次epoll_wait,第一次是及时的(例如1ms)用于处理读和错误事件,第二次是稍微长的(例如30~50ms,视情况定)用于处理读、写等事件。为了达到这种效果,我们可以 封装两种send方式,一种是使用epoll触发的写,另外一种是紧急的立即写(当然写时可以调用poll等检测一下是否可写)。这样效率应该跟得上了,复杂度和出错成都也没有ET模式高。
re: 论大小端 peakflys 2012-08-20 09:56
对的,那几个函数就是为了在网络字节序(也就是大端)和本地字节序间转换的,不过它提供的只有32位和16位的数值转换,实际项目中还会用到其他格式的数值类型,为了保证格式上的统一,一般都自己封装转换方法(其实那几个函数的真实实现 也是用到上面的方法实现的)@时间矢
re: 论大小端 peakflys 2012-08-20 09:48
64位和32位道理一样,挺好写的,自己实现就okay了,呵呵 @zaccheo
挺好的方法,无论是从空间上还是时间上 都是不错的算法
re: 编译器背后的小故事 peakflys 2012-07-30 11:01
@zgpxgame
例一的结果自然是发生在隐式转换之前的值溢出,但 关键应该是上面提到的GCC编译器做运算时如果后面运算值没有超过32位的,都会用32位寄存器做运算,只有你运算值本身有大于32位的或者程序中强制转换成大于32位的(如例一后来改的那样),编译器才会有64位寄存器的参与。就如同如果上面是把两个unsigned short的最大值,即65535+65535赋给一个unsigned int值,程序运行正常,不会发生溢出。
PS string强转去除const属性,在实际应用中会出现,但是一般项目中都不会强制改变它的内容,上面例二仅仅是测试类常量和基本值常量直接些许的差别。
re: 编译器背后的小故事 peakflys 2012-07-30 10:48
@ntest 而object需要占用内存空间???
这位仁兄的意思是const int 没有内存的占用,只有寄存器的占用?上面例二加上汇编码是为了说明,const int入栈时 没有从内存取值,应该是编译器优化直接从类似的符号表(同C中的define一样)里取出数值。
re: 关于hash_map的一点感悟 peakflys 2012-07-24 18:29
@likun原因很简单,上面我也说过,operator++ 操作是从_M_cur开始,优先_M_cur->_M_next,为空时遍历vector直至找到一个_M_cur不为空的节点,遍历vector时需要取它对应的桶位置(参砍上面hash_map取值过程),_M_bkt_num_key(key)中key的值是修改后的值,假如你改的键值,通过此函数得到的桶位置在你当前元素之前,这样就造成了死循环。
<2021年12月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

公告

人不淡定的时候,就爱表现出来,敲代码如此,偶尔的灵感亦如此……

常用链接

留言簿(4)

随笔分类

随笔档案

文章档案

搜索

最新评论

阅读排行榜

评论排行榜