Sentry 是一个帮助在线业务进行监控及错误分析的云服务,它每月处理超过十亿次错误。我们已经能够扩展我们的大多数系统,但在过去几个月,Python 写的 source map 处理程序已经成为我们性能瓶颈所在。(译者:source map 就是将压缩或者混淆过的代码与原始代码的对应表)

从上周开始,基础设施团队决定调查 source map 处理程序的性能瓶颈。——我们的Javascript 客户端已经成为我们最受欢迎的程序,其中一个原因是我们通过 source map 反混淆 JavaScript 的能力。然而,处理操作不是没有代价的。我们必须获取,解压缩,反混淆然后反向扩张,使 JavaScript 堆栈跟踪可读。

当我们在 4 年前编写了原始处理流水线时,source map 生态系统才刚刚开始演化。随着它成长为一个复杂而成熟的 source map 处理程序,我们花了很多时间用 Python 来处理问题。

截至昨天,我们通过 Rust 模块替换我们老的 Python 的 souce map 处理模块,大大减少了处理时间和我们的机器上的 CPU 利用率。

为了解释这一切,我们需要先理解 source map 和用 Python 的缺点。

Python 的 Source Maps

随着我们的用户的应用程序变得越来越复杂,他们的 source map 也越来越复杂。在 Python 中解析 JSON 本身是足够快的,因为它们只是字符串而已。问题在于反序列化。每个 source map token 产生一个 Python 对象,我们有一些 source map 可能有几百万个 token。

将 source map token 反序列化的问题使得我们为基本 Python 对象支付巨大的成本。另外,所有这些对象都参与引用计数和垃圾收集,这进一步增加了开销。处理 30MB source map 使得单个 Python 进程在内存中扩展到~ 800MB,执行数百万次内存分配,并使垃圾收集器非常忙碌(译者注:token 是短生命周期对象,有新生代就好多了,这时候就体现出我大 Java 的优势了)。

由于这种反序列化需要对象头和垃圾回收机制,我们能在 Python 层做改进的空间非常小。

Rust 的 Source Maps

在调查发现问题在于 Python 的性能缺陷后,我们决定尝试 Rust source map 解析器的性能,这是为我们的 CLI 工具编写的。在将 Rust 解析器应用于问题很大的 source map 之后,其表明单独使用该库进行解析可以将处理时间从 > 20 秒减少到 < 0.5 秒。这意味着即使忽略任何优化,只是将 Python 解析器替换为 Rust 解析器就可以缓解我们的性能瓶颈。

我们证明 Rust 确实更快后,就清理了一些 Sentry 内部 API,以便我们可以用新的库替换原来的实现。这个 Python 库命名为 libsourcemap,是我们自己的 Rust source map 的一个薄包装。

优化结果

部署该库后,专门用于 source map 处理的机器压力大大降低。

最糟糕的 source map 处理时间减少到原来的十分之一。

更重要的是,平均处理时间减少到~ 400 ms。

JavaScript 是我们最受欢迎的项目语言,这种变化达到了将所有事件的端到端处理时间减少到~ 300 ms。

在 Python 中 嵌入 Rust

有很多方法可以暴露 Rust 库给 Python。我们选择将 Rust 代码编译成一个 dylib,并提供一些 ol’C 函数,通过 CFFI 和 C 头文件暴露给 Python。有了 C 语言头文件,CFFI 生成一些 shim( shim 是一个小型的函数库,用于透明地拦截 API 调用,修改传递的参数、自身处理操作、或把操作重定向到其他地方),可以调用 Rust。这样,libsourcemap 可以打开在运行时从 Rust 生成的动态共享库。

这个过程有两个步骤。第一个是在 setup.py 运行时配置 CFFI 的构建模块:

在构建模块之后,头文件通过 C 预处理器来处理,以便扩展宏( CFFI 本身无法执行的过程)。此外,这将告诉 CFFI 在哪里放置生成的 shim 模块。所有完成的之后,加载模块:

下一步是编写一些包装器代码来为 Rust 对象提供一个 Python API,这样能够转发异常。这发生在两个过程中:首先,确保在 Rust 代码中,我们尽可能使用结果对象。此外,我们需要处理好 panic,以确保他们不会跨越 DLL 边界。第二,我们定义了一个可以存储错误信息的帮助结构 ; 并将其作为 out 参数传递给可能失败的函数。

在 Python 中,我们提供了一个上下文管理器:

我们有一个特定错误类( special_errors)的字典,但如果没有找到具体的错误,将会抛一个通用的 SourceMapError。

从那里,我们实际上可以定义 source map 的基类:

在 Rust 中暴露 C ABI

我们从包含一些导出函数的 C 头开始,如何从 Rust 导出它们? 有两个工具:特殊的# [no_mangle] 属性和 std :: panic 模块 ; 提供了 Rust panic 处理器。我们自己建立了一些 helper 来处理这个:一个函数用来通知 Python 发生了一个异常和两个异常处理 helper,一个通用的,另一个包装了返回值。有了这个,包装方法如下:

boxed_landingpad 的工作方式很简单。它调用闭包,用 panic :: catch_unwind 捕获 panic,解开结果,并在原始指针中加上成功值。如果发生错误,它会填充 err_out 并返回一个 NULL 指针。在 lsm_view_free 中,只需要从原始指针重新构建。

构建扩展

要实际构建扩展,我们必须在 setuptools 中做一些不太优雅的事情。幸运的是,在这件事上我们没有花太多时间,因为我们已经有一个类似的工具来处理。

这个做法最方便的部分是源代码用 cargo 编译,二进制安装最终的 dylib,消除任何最终用户使用 Rust 工具链的需要。

那些做得好,那些没做好?

我在 Twitter 上被问到:“ Rust 会有什么替代品?”说实话,Rust 很难替代。原因是,除非你想用性能更好的语言重写整个 Python 组件,否则只能使用本机扩展。在这种情况下,对语言的要求是相当苛刻的:它不能有一个侵入式运行时,不能有一个 GC,并且必须支持 C ABI。现在,我认为适合的语言是 C,C++ 和 Rust。

哪方面工作的好:

结合 Rust 和 Python 与 CFFI。有一些替代品,链接到 libpython,但构建更复杂。

在老一些的 CentOS 版本使用 Docker 来构建可移植的 Linux 容器。虽然这个过程是乏味的,然而不同的 Linux 发兴版和内核之间的稳定性的差异使得 Docker 和 CentOS 成为可接受的构建解决方案。

Rust 生态系统。我们使用 crates.io 的 serde 反序列化和 base64 库,两个库工作非常好。此外,mmap 支持使用由社区 memmap 提供的另一库。

哪方面工作的不好:

迭代和编译时间真的可以更好。我们每次更改字符时都编译模块和头文件。

setuptools 步骤非常脆弱。我们可能花了更多的时间来使 setuptools 工作。幸运的是,我们以前做过一次,所以这次更容易。

虽然 Rust 对我们的工作帮助很大,毫无疑问,有很多需要改进。特别是,用于导出 C ABI(并使其对 Python 有用)的基础设施应该有很大改进空间。编译时间也不是很长(译者的话,不是很长的意思是可能够我沏杯茶,怀念 go 的编译速度)。希望增量编译将有所帮助。

下一步

其实我们还有更多的改进空间。我们可以以更高效的格式启动缓存,比如一组存储在内存中的结构体而不是使用解析 JSON。特别是,如果与文件系统缓存配对,我们几乎可以完全消除加载的成本,因为我们平分了索引,这可以使用 mmap 非常有效。

鉴于这个好的结果,我们很可能会评估 Rust 更多在未来处理一些 CPU 密集型的业务。然而,对于大多数其他操作,程序花更多的时间等待 IO。

小结

虽然这个项目取得了巨大的成功,但是我们只花了很少的时间来实现。它降低了我们的处理时间,它也将帮助我们水平扩展。Rust 一直是这个工作的完美工具,因为它允许我们将昂贵的操作使用本地库完成,而且不必使用 C 或 C ++(这不太适合这种复杂的任务)。虽然很容易在 Rust 中编写 source map 解析器,但是使用 C / C++ 来完成的话,代码更多,且没那么有意思。

我们确实喜欢 Python,并且是许多 Python 开源计划的贡献者。虽然 Python 仍然是我们最喜欢的语言,但我们相信在合适的地方使用合适的语言。Rust 被证明是这项工作的最佳工具,我们很高兴看到 Rust 和 Python 将来会带给我们什么。

译者注:不熟悉 source map 的同学请看阮一峰的这篇文章

http://www.ruanyifeng.com/blog/2013/01/ javascript_source_map.html

媒体合作请联系:

邮箱:xiangxiaoqing@stormorai.com

程序语言python的优化版_从20秒到0.5秒:一个使用Rust语言来优化Python性能的案例...相关推荐

  1. python 速度 memmap_从20秒到0.5秒:一个使用Rust语言来优化Python性能的案例

    <从20秒到0.5秒:一个使用Rust语言来优化Python性能的案例>要点: 本文介绍了从20秒到0.5秒:一个使用Rust语言来优化Python性能的案例,希望对您有用.如果有疑问,可 ...

  2. 转 从20秒到0.5秒:一个使用Rust语言来优化Python性能的案例

    注: 转自 微信公众号"高可用架构":从20秒到0.5秒:一个使用Rust语言来优化Python性能的案例 导读:Python 被很多互联网系统广泛使用,但在另外一方面,它也存在一 ...

  3. Bitcoin.com开发人员正创建一个用Rust语言编写的BCH全节点

    近日,Bitcoin.com开发人员Gabriel Cardona在推特表示,自己正创建一个用Rust语言编写的BCH全节点,目的是为了改变完整节点的范式,让BCH代码库更加安全.编程人员技术贡献更加 ...

  4. python调用rust_转 从20秒到0.5秒:一个使用Rust语言来优化Python性能的案例

    导读:Python 被很多互联网系统广泛使用,但在另外一方面,它也存在一些性能问题,不过 Sentry 工程师分享的在关键模块上用另外一门语言 Rust 来代替 Python 的情况还是比较罕见,也在 ...

  5. python教程简易版_简洁的十分钟Python入门教程

    [简介] Python是一种动态解释型的编程语言.Python可以在Windows.UNIX.MAC等多种操作系统上使用,也可以在Java..NET开发平台上使用. [特点] 1 Python使用C语 ...

  6. 笨办法学python第五版_笨办法学python PDF下载|笨办法学python第五版 电子版附目录_最火软件站...

    笨办法学Python第五版PDF电子版是专门为初学Python的朋友准备的一款电子图书资料,可以帮助你更好的学习Python编程知识,该电子书每章节都附带了常见问题回答以及练习题目,能够帮助学习者快速 ...

  7. python设置剪辑版_一文教会你用Python实现最有效的剪切板实时监控

    前言 上网浏览网页的时候,看见好的内容免不了要使用复制粘贴,但是我们看到的内容.心里想要的内容和实际粘贴后的内容往往不一致.数据的获取始于复制,终于粘贴,那么问题来了,在这中间系统做了哪些操作,我们怎 ...

  8. python实现抢劵_双十一福利丨机械键盘、蓝牙音箱送送送!Python/UI/Unity多场微课解剖双十一套路!...

    福利来了 别等双十一 多场微课get双十一全套路 机械键盘送送送! 福 利 来 了 双十一福利第一波 - 11/1-11/11 - 机械键盘送送送 有品位的IT侠 早已get起双十一的新姿势 11月1 ...

  9. python大神推荐_大神推荐!!!高评分书籍轻松带你入门Python

    上次讲到了初学者阶段必看的书籍.今天给大家继续讲讲 进阶者阶段 当你对Python有了一定的了解,并在生活或者工作中使用过一段时间,那么你就可以考虑进阶相关的书籍,请看下面这3本: <Pytho ...

最新文章

  1. Web架构师必备能力
  2. 开源 java CMS - FreeCMS2.3 会员组管理
  3. MySql 修改外键 支持级联删除
  4. 如何在Eclipse中显示行号
  5. 查看某个端口是否链接超时
  6. JS中Promise函数then的奥秘探究
  7. label包裹input,点击label响应两次解决方法
  8. windows系统文件和dll文件
  9. can总线一帧多少字节多少位_一条CAN报文到底有多少位?-汽车电子-与非网
  10. ArcGIS案例学习笔记4_2_水文分析批处理地理建模
  11. mgo02_mongodb启动警告处理
  12. STM32 Cubemax(十五) —— 串级PID以控制电机角度值为例
  13. PhotoShop简单案例(3)——利用ps实现多图合成(多张小图片拼成一张大图片)
  14. 新支点“B4G+5G”聚合路由器多链路图传技术巩固安防监控行业
  15. supervised domain adaption
  16. 如果要你向别人推荐电影,你最先想到的是那些呢?
  17. [数据结构]线性表——顺序存储
  18. 如何选定搭建个人独立博客工具
  19. 计算机实战项目、毕业设计、课程设计之 含论文+辩论PPT+源码等]微信小程序基于微信小程序的美容预约+后台管理系统
  20. “无任何网络提供程序接受指定的网络路径”问题的几个解决方法

热门文章

  1. VS2013 启用 angularjs 智能提示 Intelligence
  2. 杭电1420 Prepared for New Acmer
  3. 杭电1715大菲波数
  4. 随想录(一种powerpc编译学习的方法)
  5. php 根据一个数据组的id 取得另一个数据组对应的名称,怎样从两个spss的数据文件中选出id相同的数据,合并成一个文件...
  6. b g opencv读入的图片 r,OpenCV 读取摄像头并显示图像的R、G、B三个通道的直方图...
  7. html dw map,DW十六 map标签
  8. 计算机专业考研学习,计算机专业考研133分复习经验谈
  9. java rsa enc 源码_RSA加解密源码 | 学步园
  10. edui 富文本编辑_React中使用UEditor百度富文本的方法