如何写出好看的C++代码

常跟人说,今年是我编程第十年了,听起来很夸张,不过是真的。经过2010、15和17年,现在算有自己的编码习惯和风格;但是每次回头看前一阵的代码,都会有很多地方不满意,甚至确信不该那么写;或许是因为懈怠,或许是并未熟记本应坚持的好习惯。

360离职前交接代码,老大帮我把1、2、3、4、5、6、7改成enum结构体,用XXXX_BIT代替;甚为惭愧,于是把一坨if-else改成switch-case,具体内容抽象成函数。写go parser那段时间,扶摇教给我如何写规范的git commit log,以及scala如何优雅的关闭文件描述符。这些细节让我印象深刻,好看的代码是如此重要,列一些点提醒自己时刻牢记。

机器学习到word2vec

最近发现,NLP(自然语言处理)很适合机器学习入门,适用场景丰富,功能强大,蕴藏着丰富的数学知识;又不至于让人陷入过多的细节,进而怀疑人生。最早看fastText,意外的清晰易懂;随后踏进交叉熵和SVM的泥沼,差点淹死。入门选择格外重要,PRML太墨迹了,陷进数学细节说的就是这本书;上来就看LDA,可能会疯。

word2vec作为优秀的词向量生成工具,自带精炼源码,背后的思想也具备很强的通用性。希望能借这篇文章,详述几个问题:机器学习是什么以及如何解决实际问题、词向量的价值、word2vec的原理,最好还能把损失函数、LR、神经网络和降纬这几个常见概念说清楚;内容皆以我的理解为基础,不一定标准且精确(尽量不跑偏),算一篇入门小总结。

数据服务开发经验

有状态服务或者说数据服务,上线遇到问题很棘手,回滚无济于事;而且数据加载通常都很慢,部署时间长;最终导致不敢修改代码,谨小慎微;服务质量也是能忍就忍,不愿意深度优化。在我负责顺风车LBS以来,感受愈加强烈;区别于无状态服务,数据服务的几个方面需要格外关注。(此处假设数据服务类似redis基于内存,数据量大到需要磁盘存储,关注点会有所不同。)

RocketMQ原理分享

之前在组内分享过一次RocketMQ,虽然写了PPT,还是口述+黑板画图,现场分享效果更佳。RocketMQ架构清晰,只有NameServer、Broker、Producer和Consumer四个组件,通过共享commitlog,即使数十万topic,依然能保持高性能;是通过工程巧妙解决业务难题的典范。RocketMQ真正做到了“简单”,例如重复消费这种功能,完全抛给消费侧;通过构建逻辑队列,每个消息都会指定唯一队列,解决消息顺序的问题。

下面介绍整体架构和四个组件,然后分析一条消息从诞生到被消费的全过程,从中了解RocketMQ设计的精妙。

通用共享内存

共享内存使用便捷,功能强大,但是会有几个显著的问题阻碍大家使用:

1、增删不当,造成内存泄漏;共享内存依赖一个全局的key,没有好的清理手段。

2、内存大小不好控制,动态管理则会引入更多复杂的情况;即使是加个header,一次性的动态大小,写起来也非常麻烦。

3、同样由于key的存在,共享内存管理容易混乱,万一冲突后果很严重。

3、语言和框架越来越强大,大多数人没有直接管理内存的经验,心里恐惧,不敢操作。

thrift 编码解码详解

thrift结构体按照一定格式编码成字符串,供网络传输;通常上层会对解码和编码的调用做封装,把object编码为string,从string解码object:

boost::shared_ptr<TMemoryBuffer> mem_buffer(new TMemoryBuffer());
mem_buffer->resetBuffer(
    reinterpret_cast<uint8_t*>(const_cast<char*>(buffer.c_str())),
    buffer.size());
TBinaryProtocol protocol(mem_buffer);
data.read(&protocol);

boost::shared_ptr<TMemoryBuffer> mem_buffer(new TMemoryBuffer());
TBinaryProtocol protocol(mem_buffer);
data.write(&protocol);
buffer = mem_buffer->getBufferAsString();

spdlog阅读记录

spdlog_impl.h: 用户接口

registry.h: logger封装接口

using registry =  registry_t<std::mutex> 
class registry_t {
private:
std::unordered_map<std::string, std::shared_ptr<logger>> _loggers; // name -> logger
}

迟到的Windows安全总结

我已经快两年没用过Windows了,从13年低,到17年中,莫名其妙的开始,无法忍受的结束。从来也没想过要一直研究Windows内核,也许是基于惯性,也可能是人性的懒惰,却在前几年一直做这么一件事情。在吉胜实习实在是消耗了太多热情,后期只感到疲惫——那些无聊的商业竞争,对用户赤裸裸的不在乎,让我感到伤心。去360的第一天,就感到不舒服;我的同事们都很不错,老大也对我非常好;但是一天天下来,我觉得我不能再继续下去了,不能把一生都消耗在抢夺浏览器入口上。

go的几点不足

使用go开发快一个月了,之前只是写写demo,真正在线上使用,才发现诸多不便(go真的兼具简洁和强大,还轮不到说它的坏话),吐槽一些别扭的地方。

1、if + 表达式。

只能表达式开头,如果想在特定条件下执行 “计算表达式 + 判断表达式结果”,似乎只能在外层加if。而C和C++可以随意加条件,任何有返回值的表达式都可以放到if的判断条件,加上逗号表达式,功能很强大。而go的这点不足,在维护代码时非常不方便。

poolboy——Erlang进程池

poolboy是Erlang进程池,应用广泛,代码短小精悍(400行不到)。PostgreSQL的Erlang客户端就用到了poolboy,其他DB库也有用,比如redis等。由于足够简洁,所以坑不少,这篇文章做了总结。任何进程池的目的都是很明确的——预先创建特定数目的进程,执行反复的一次性工作,随后接受直接提供服务。用Erlang实现进程池更是易如反掌,只需要按照OTP规范写一个监控树就行。

poolboy使用简单,官方README和例子说的很清楚。简要列一下工作流程:

poolboy:start/2(start_link/2) -> poolboy gen_server process
poolboy:init -> poolboy_sup -> N worker_process
poolboy:transaction -> select one worker_process from pool and execute function

下面详细看源码。