背景

我们的新项目有个比较复杂的全区全服交易行系统,其中搜索和推荐是高实时性全区服多维度排序的,并且要支持比较复杂的标签交集查询和属性范围查询的自由组合。 当有订单发生变化时,它不仅仅会影响全服状态下搜索和推荐条件的结果变化,也会同时影响商品维度的聚合,交易行层面的数据聚合。

前言

我们项目组前段时间排查和分析压测环境下的某些业务模块大量索引结构的内存问题。通用的工具比如 jemalloc+jeperf 或者 tcmalloc+gperf 的组合过于底层,一方面开启跟踪开销较高,另一方面也是会产生过多噪音数据影响判断。所以我针对我们的智能指针(包含 std::shared_ptr 和我最近写了个非线程安全的版本的 strong_rc_ptr , 这个后面有空再分享)和STL容器实现了allocator来帮助动态的手动插桩来分析问题。 最终的效果是可以通过一键替换类型申明的Allocator来插入动态控制和插桩统计的能力,这里分享一下手夯标准STL allocator的一些实现细节,方便其他小伙伴如果需要做类似的实现来参考。

背景

这篇分享拖更了好久了。问题起源于去年我们项目组接入 opentelemetry-cpp 的时候,在进程优雅退出的时候偶现超时,虽然可以直接kill进程没啥影响但是退出不“优雅”的话总归会破坏发布流程,增加人工介入的成本。这里记录一下问题可能其他的组件有类似的用法也会有相似的问题。

背景

Opentelemetry-cpp 是可观测领域,opentelemetry (CNCF基金会孵化项目)的C++ SDK接入层opentelemetry 里面主要是分链路跟踪(Trace)、指标(Metrics)、日志(Logs)三大块。 同时 opentelemetry 有一个标准规范文档 opentelemetry-specification ,而SDK实现主要就是来对这个标准规范文档的特定语言实现。 由于日志(Logs)这一块一直处于Experimental阶段,所以很长时间以来 C++ SDK接入层 都没有及时更新跟进规范的变化。

前言

很久很久以前,浮点数的性能和跨平台跨硬件架构一致性是无法获得保证的,所以我们一般在需要强一致性和高性能的游戏服务器中会禁用浮点数,转而使用自己实现的定点数。 这么多年过去了,前段时间想看看现代化硬件下是否仍然有性能问题和是否能够保证一致性,做了些简单的测试,这里记录一下。

前段时间有同事联系我想看看可能推广我之前写的协程库 libcopp,虽然 libcopp 已经用到过好几个项目上,这几年也断断续续地写了一些实现细节的文章,但是也但确实需要系统、概览性地介绍下 libcopp ,所以就有了这篇文章。

前言

之前写了 《协程框架(libcopp)v2优化、自适应栈池和同类库的Benchmark对比》《C++20 Coroutine》 ,但是一直没写 C++20 Coroutine 的测试报告。

现在的草案版本比我当时写 《C++20 Coroutine》 的时候有了一点点更新,cppreference 上有文档了(https://en.cppreference.com/w/cpp/language/coroutines) 。里面列举的标准文档是P0912R5,这个文档目前还没完工,详情可以看他的来源N4775。不过内容上暂时还没有太大的变化,今天我就照着之前的方式来benchmark一波 C++20 Coroutine 吧。

前言

最近的新闻里 C++20 已经确认的内容里已经有了协程组件,之前都是粗略看过这个协程草案。最近抽时间更加系统性的看了下接入和实现细节。

我的测试代码都是在MSVC下开启 /await 选项后测试的,在我本地的Linux clang环境中,可以通过 $LLVM_CLANG_PREFIX/bin/clang++ -std=c++2a -O0 -g -ggdb -stdlib=libc++ -fcoroutines-ts -lc++ -lc++abi -Wl,-rpath=$LLVM_CLANG_PREFIX/lib/ test.cpp 编译和运行。

前段时间看到说Lua 5.4用了一种新的通用随机数算法,替换掉本来内部使用的CRT的随机数引擎。我看了一下大致的实现,CPU和空间复杂度任然保持了一个较低的水平,并且循环节和说是随机性都还不错。我们游戏项目中原本对大量随机数场景的随机数算法使用的是基于线性同余的TAUS88,但是使用过程中发现这个算法分布上还是有一些不是很理想,所以就想把这个新的科研成果也用进我们项目中试试看效果。

开始之前

很多语言的log模块都有一个功能,就是在打log的时候能够追溯调用栈,有的时候对查bug能有点帮助。之前我也想过给我们的log模块加上C++的backtrace的功能,迟迟一直没有做主要是两个原因:一是C++的backtrace在各个平台和编译器上都不太一样,比较冗杂;二是C/C++在编译优化之后,调用行之类的信息和甚至一些函数可能就被优化没了。所以能提供的信息就相当有限。前两天刚好有朋友问有没有提供这个,所以就花了点时间整理了下适配方案。

之前测出来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。

线程安全

前段时间看到了一个完成读比较高的协程库-libgo,里面提供了线程安全的协程实现,并且也是使用锁。本来我并没有给libcopp里的功能加锁的打算,因为上层dispatcher还是比较容易做到安全分发的,所以原来并不保证线程安全。而且线程安全这种问题单元测试比较难写,可能还得碰点运气。但是思来想去,还是为线程安全做点什么吧。反正也不是很复杂。

由于我并没有给utils加互斥锁的跨平台适配,所以先就直接用了自旋锁,来锁住需要考虑线程安全的地方。其实需要加锁的地方并不多,无非是管理器的增删查和task的next函数需要加锁。这些逻辑都很短,功能也很简单,并不会占用太多时间,所以自旋锁的问题也不大。而且以后真发现有问题,换掉也不是什么难事儿。

在写这篇文章前,我突然想到以前流行了一段时间的服务器面试题:当一个BUG只有几百万分之一的概率会出现,怎么办?这个问题在这个BUG里只是毛毛雨而已,因为这次的BUG的出现概率是夸张的三亿分之一

由于之前整理的服务器框架已经完成了,就需要用各种静态分析工具跑一遍。原来的llvm+clang的编译脚本是从GCC那个脚本copy过来然后改的,使用的时候各种问题。所以干醋重新折腾一遍,重写了。

近期仍然在搭建完整的游戏服务器架构。基于atsf4g(asynchronously-tree server framework fo game)的完整解决方案终于接近完成。基本框架之前其实已经做完了,但是之前解决的只是基本的框架层代码,不包含任何特定的交互模型、协议模型、配置服务等等。这回就整理了一个只包含登入登出逻辑的完整工程,另外优化了一些小细节和周边工具的支持。