libcopp v2的第一波优化完成
之前测出来libcopp还有一些列优化点,但是要破坏之前的API,所以整理了一下优化的想法和方案。
预留空间和合并分配
之前有太多的堆内存分配了,导致很多碎片。那么第一个想法就是协程对象可以分配在栈上,runner也可以分配在栈上。然后还可以加一个自定义预留长度。每个对象对齐到sizeof(long),总长度对齐到64 Bytes。
之前测出来libcopp还有一些列优化点,但是要破坏之前的API,所以整理了一下优化的想法和方案。
之前有太多的堆内存分配了,导致很多碎片。那么第一个想法就是协程对象可以分配在栈上,runner也可以分配在栈上。然后还可以加一个自定义预留长度。每个对象对齐到sizeof(long),总长度对齐到64 Bytes。
本来是没想写这个对比。无奈之前和call_in_stack的作者聊了一阵,发现了一些libcopp的改进空间。然后顺便看了新的boost.context的cc部分的代码,有所启发。想给libcopp做一些优化,主要集中在减少分配次数从而减少内存碎片;在支持的编译器里有些地方用右值引用来减少不必要的拷贝;减少原子操作和减少L1cache miss几个方面。
之后改造了茫茫多流程和接口后出了v2版本,虽然没完全优化完,但是组织结构已经定型了,可以用来做压力测试。为了以后方便顺便还把cppcheck和clang-analyzer的静态分析工具写进了dev脚本。然后万万没想到的是,在大量协程的情况下,benchmark的结果性能居然比原来还下降了大约1/3。
在写这篇文章前,我突然想到以前流行了一段时间的服务器面试题:当一个BUG只有几百万分之一的概率会出现,怎么办?这个问题在这个BUG里只是毛毛雨而已,因为这次的BUG的出现概率是夸张的三亿分之一。
最近看了下最新版本的cmake的文档,很惊喜地发现他已经支持直接设置Android和OSX的一些变量了,然后有瞄了一眼NDK,发现里面现在也停工官方的cmake支持。
其实我那几个特别是工具类得开源项目一致都有维护和更新,但是每次更新得量和要点并不怎么突出所以一致也没写点什么。但是偶尔吗也会碰到一些稍微值得记录的东西,但是又不多所以很多都遗漏掉了。
这两天在做服务器框架的C的接口导出和C#的接入。之所以要做这么个东西是因为之前的服务器框架(atsf4g-co)已经完成了通信层面和基本设计模式的细节部分,而且基本算是最大化性能了吧。但是现在的项目的战斗引擎是从以前Unity游戏上抽象而来的,全部由C#编写。再加上最近再考虑接入实时战斗,这样就不能像之前一样用一个简单的通信方式了,必须使用一个高效并且实时性更高通信机制。需要能够处理好比较高的集中式的组播和容灾的通信方式。于是就有了把之前的C++的框架抽离出API来驱动逻辑的想法。这样也比较容易地兼顾开发成本和性能之间地权衡。
那么抽离出框架地目的是抽象出应用底层,这个刚好是atapp做的事,而且atapp的层面对外暴露的接口数量也比较少,使用比较简单,所以索性就直接对它下手了。
最近一直没什么时间整理近期碰到的问题,今天思考了一下之前碰到的一个临时处理的BUG,顺便写点东西清理一下思路。
其实严格来说这个BUG更应该是一个流程试用问题,不过这个问题应该是需要能在协程库里检测并抛出错误来。
由于之前整理的服务器框架已经完成了,就需要用各种静态分析工具跑一遍。原来的llvm+clang的编译脚本是从GCC那个脚本copy过来然后改的,使用的时候各种问题。所以干醋重新折腾一遍,重写了。
近期仍然在搭建完整的游戏服务器架构。基于atsf4g(asynchronously-tree server framework fo game)的完整解决方案终于接近完成。基本框架之前其实已经做完了,但是之前解决的只是基本的框架层代码,不包含任何特定的交互模型、协议模型、配置服务等等。这回就整理了一个只包含登入登出逻辑的完整工程,另外优化了一些小细节和周边工具的支持。
好久没写blog了。最近空闲时间都在加紧完成atframework框架。总算是搞完了,并且搭建了一个静态介绍页。以后有空慢慢加文档进去。
主页: https://atframe.work/ Github: https://github.com/atframework/
目前这个项目已经是一个小型游戏服务器的完整框架,包括服务器间实例的管理、客户端和服务器的内部协议和通信机制。并且实现了纯C的port。 然后https://github.com/atframework/atsf4g-co/tree/master/sample/atgw_cli_inner_hello里提供了c++的客户度端的代码示例,https://github.com/atframework/AtgwInnerCli-CSharp里提供了C#的客户端代码示例。 使用的都是同一个纯C的协议porting。这样无论cocos还是unity框架接入都不困难了。其他的框架也可以直接用这个纯C的porting来接入。
这个问题起源于以前给客户端写的一个log模块,然后里面为了线程安全且多线程下不互相写乱,并且因为这些系统基本都用比较高版本的编译器,都支持C++11了,所以就用了C++11的TLS功能。
近期跟了一下pbc的lua-binding的一个老BUG,起源是我们客户端报了一个奇怪的问题,我们游戏里的某些功能的optional字段,服务器并没有下发数据,但是客户端竟然能读到。
之前写了个C++的协程框架libcopp,底层使用的是boost.context实现,然后剥离了对boost的依赖。然而这样意味着我必须时常跟进boost.context的更新。
顺带提一下这个协程库已经在我们线上服务器版本中使用了。
从最初的boost版本(我忘了从哪个版本开始了)一直到1.60版本,boost.context的变化都不大,都只是补全一些新的架构和体系结构,还有就是修复一些小细节的BUG,再就是增加了对valgrind的支持(之前写过一个Merge记录提到过)。新增的功能也只有execution_context(现在叫execution_context_v1),这个东西我的libcopp里其实包含了这个功能,并且本身做得比它要功能丰富,所以没有接入的必要。另外在1.60版本的时候尝试使用Windows里的fiber(当然默认是关闭的),在1.61版本里被移除了。这些细节都不是特别重要,主要还是1.61版本的变化。
最近在分布式系统一致性方面,Raft算法比较火啊。所以就抽时间看了下这个算法。
之前已经有Paxos算法,用于解决分布式系统最终一致性问题,而且已经有了zookeeper这个成熟的开源实现。那么这个Raft算法有啥用呢?按照Raft官网的说法,这个算法的错误容忍和性能和Paxos算法类似,但是拥有更加简单易懂的设计。
看过Paxos算法的童鞋们都知道,这货复杂地和屎一样,为了实现去中心化而考虑了各种复杂的边界条件和时序下的可靠性。而Raft算法则根据实际应用中的需要,简化了设计模型,不采用去中心化设计,而是自动选举中心节点,并且在各种情况和时序下可以保证能够正确的选举出中心节点并保证数据的一致性。而且也正是由于能够选举出唯一的主节点(Leader)使得整个通信流程非常地简单,并且易于理解和维护。
那么它是如何做到这些的呢?
Raft的基本设计可以参照官网介绍 https://raft.github.io/
官方网站上的图例可以点击节点,然后模拟节点crash或者超时或者收到请求时的通信流程。其实也是一个javascript的简单实现,有利于我们理解Raft算法的流程。
经过茫茫长时间的编写+过年在家无聊补充和修正单元测试,再加上这两天的整理,终于把以前的这个关于服务器通信中间件的基本功能和相应的单元测试完成啦。还是可以热烈庆祝一下的。
使用Markdown写blog已经很久了,近期接触并且看了下流传已久的gitbook平台,感觉做得确实不错。、
之前写blog的时候一直用得是stackedit,是因为stackedit的对Markdown做了很多扩展,功能很强大,有自动目录、流程图、时序图等等,然后可以浏览器直接开很方便。但实际上这些功能写出的东西虽然不错,但是放到比如github上的时候,github不支持。目前大多数平台对Mardown的扩展都只是到了和github差不多的地步,没有到stackedit的程度。这也导致同样写得东西,复制到github或者其他的平台的时候还得过一遍样式,比较麻烦。而且这些扩展的功能也用得不太多。另外stackedit时不时被墙然后访问很不稳定也是挺麻烦的一件事儿。
再来说这个gitbook,看中他是觉得它做了一个可持续集成的功能。就是github push完以后可以通知gitbook然后让gitbook自动构建文档内容。这点和比如jenkins和travis等等的CI系统很像。然后支持构建成pdf、epub(开源电子书格式)、mobi(kindle电子书格式)和在线书籍。然后版式也挺漂亮,还支持模板,引用等等,感觉确实蛮适合出版发行的。虽然目前为止Markdown的功能丰富程度比起Latex还差不少,但是上手难度也比Latex低不少。还是非常有潜力的,而且gitbook支持用javascript写得插件,以后变数也可以很多。
所以我决定也尝尝鲜,首先是吧blog里的文章记录转移过来,然后后面的开源项目的wiki或者文档也用这玩意构建。因为我用Markdown写博文的时候本来就备份了一份在github,所以有现成的仓库,然后把目录结构和配置调整成gitbook所支持的就可以了。话说好像已经很多国内的文档和开源书籍用这个生成了。