Go Apps中热路径的迭代优化

在萨姆萨拉(Samsara),我们向客户提供实时数据,同时每分钟从数以千计的连接设备中提取数百万个事件。 支持此功能的软件是雷声,这是内部构建的开源GraphQL框架。 Thunder由多个部分组成,包括一个称为sqlgen的SQL生成器。 这篇文章介绍了我们如何向sqlgen添加功能,分配增加了33%,内存增加了66%,以及sqlgen一些优化后如何返回到基线数。

什么是sqlgen?

sqlgen是一种轻量级的伪ORM,它将数据库表映射到它们的Go表示形式,并减少了样板代码。 在深入研究添加的功能或其设计之前,让我们对sqlgen进行基本的了解。

给定的表和相应的模型:

内存使用量大幅度降低了17%,这意味着我们现在使用的内存仅比基线多5.6%。 任务完成!

其他优化

我们还考虑了其​​他优化,但并未实现。 通过将基准与使用基本SQL驱动程序进行比较,可以使基准更具参考价值。 我们还可以在初始运行时预先计算自定义类型,从而在后续代码路径上节省CPU周期。

我们可以做很多其他的优化。 在快速移动和快速编写代码之间始终要进行权衡。 由于我们已经实现了将原始内存使用率控制在10%以内的目标,因此我们现在很高兴将其称为。

自从改进了分配之后,我们发布了另一个生产金丝雀版本,以测试新实现的性能。 CPU使用率和内存消耗实际上与我们的master分支相同。

自从开始优化路径以来,我们将GraphQL服务器的内存使用量减少了50%,以匹配添加新功能之前的使用情况。 我们甚至降低了读取路径分配,这将导致随着时间的推移减少垃圾收集。

最重要的是,我们能够提供对JSON字段的sqlgen支持!

我们学到了什么?

构建和优化此功能教会了我们:

  • 堆分配很昂贵。 即使值本身很小,重复的,短暂的堆分配也特别昂贵,因为它们需要时间来进行垃圾回收。
  • 在热路径上包装值可能会导致上述问题。
  • sync.Pool可以帮助我们在热路径上重用分配。
  • 基准非常有用,预先建立基准可以帮助避免性能下降。
  • 无法替代生产数据。

交给你

作为程序员,像这样的战斗故事始终是很棒的学习经历,对于那些参与其中的人以及我们可以告诉他们的故事而言。 你有自己的故事要讲吗? 我们希望在下面的评论中听到它!

如果您想尝试我们的开源GraphQL框架,可以在GitHub上找到雷声。 我们也希望知道您的使用经验。

最后,如果您正在寻找工作,轮回还面临着更多类似的挑战,我们正在招聘!


特别感谢所有审阅有问题代码的人员:陈昌平,Jelle van den Hooff,Stephen Wan和Will Hughes。 特别感谢这篇文章的主要编辑Kavya Joshi。