6个库让你写出同步代码跑出异步速度摘要性能优化的新思路很长时间以来, 在我们谈及性能之际, 有一个无法避开的话题便是多线程。面对数据处理、网络爬取或者I/O密集型任务所存在的性能瓶颈, 好多开发者会下意识地寻求多线程方案, 指望借助并发去加快脚本运行。然而, 存在的全局解释器锁GIL以及由此产生的上下文切换开销, 常常致使多线程代码不但没有带来预期中的性能提高, 反而有可能成为性能的“瓶颈”或者“拖累”。可是, 时代已然发生了变化, 当下, 一些具革命性的库正起着改变这一情形的作用。存在这样一些库, 它们于底层展开了深度优化工作, 优化所利用的常常是凭借Rust、C语言来达成, 或者是运用高效的异步I/O模型这种方式, 进而致使它们原生的执行速度快到了足以将手动管理并发的必要性给抵消掉的程度。这些库让开发者编写呈现直观、同步特点的代码, 然而却能够以如同呈现“暴风雪”般的速度去处理数据。这儿会深度讲述那六个致使作者彻底摒弃多线程的库或者工具, 并且阐释它们怎样借由底层的机制达成性能的迅猛提升, 给你铺就一条在不增添代码繁杂程度的状况下, 极大提高性能的全新道路句号。一、性能瓶颈的传统解法与新挑战多线程的困境于深入知悉这般高性能库之前, 我们首先得弄明白为何其中的多线程并非那能解决一切问题, 的药。的性能硬伤GIL与上下文切换的性能并不以“原始速度”著称。这主要归咎于两个核心问题一个名为全局解释器锁的装置也就是 GIL, 它作出这种确保, 即在任意给定的时刻之内, 仅仅会有单独的一个线程能够于虚拟机里执行字节码。而这所蕴含的意义在于, 针对于那种CPU属于密集型的任务而言, 多线程是没有办法去借助多核所具备的优势的, 相反地, 会由于GIL所带来的限制以及线程之间非常频繁的切换也就是上下文切换所产生的开销, 从而致使速度被拖慢。切换上下文所产生的开销: 即便置身于I/O密集型任务里, 多线程尽管能够起到作用缘由在于当一个线程正等候I/O之际, GIL能够释放予另一个线程, 然而管理线程、同步数据像是锁、信号量以及线程自身的切换均会造成不容小觑的开销。以往的时候, 在脚本运行迟缓之际, 我们时常本能地去求助多线程, 以此来应对数据处置、网页信息采集或者输入输出密集型工作。然而, 就如同作者所领悟到的那种具有革新性质的知识表明: 有的情况下, 你压根儿不一定要借助线程去达成速度提升。新时代的解决方案库内嵌的并行优化高性能库问世了, 实际上是这般模样, 把并发方面存在的复杂性, 从应用领域这一层面, 推到了库的基底之处。它们借助下面这些方式达成令人惊叹的速度:下面我们将逐一介绍这六个“高性能黑科技”。二、数据分析的“一级方程式”告别的慢速困境对于那些从事处理大规模数据集工作的开发者而言, 这属于标准配置, 然而它的性能瓶颈正愈发明显地呈现出来。它的出现, 就好像是从“家庭轿车升级至F1赛车”这种情况。它是一款依据Rust构建而成的库, 专门针对性能以及并行化进行了优化。它的API和别的类似, 极为便于上手, 不过它在无需手动管理线程的情况下就能达成极高的速度。为何如此之快的速度优势来源于其底层设计和实现机制简单示例自动并行化操作使用时开发者只需编写简洁的链式同步代码如下所示import polars as pl df pl.read_csv(sales.csv) result ( df.lazy() .filter(pl.col(region) Europe) .group_by(product) .agg(pl.col(revenue).sum()) .collect() )这段代码执行的速度, 相较于多线程而言会更快, 这是由于它于内部自动地对所有操作进行了并行化处理。从而开发者获取到了高性能, 然而却并不需要去管理任何线程。应用场景三, JSON解析里有个“闪电侠”, 它是内置JSON模块, 存在性能短板。要是你曾经处理过那种规模很大的JSON文件, 或者做过数量众多的JSON序列化以及反序列化相关操作, 那你肯定特别清楚内置的json模块到底有多迟缓。它是专为这个而产生的, 它是一个借助Rust予以实现的JSON库, 它的性能相较于内置的json模块或者ujson要快上2至10倍。极速背后的秘密通过以下优化实现了性能的碾压简单示例毫秒级完成大型JSON解析曾需要多线程来解析的那种庞大的JSON文件, 眼下借助其一实现了在毫秒级能够同步达成做完:import orjson data {user: Aashish, score: 99} serialized orjson.dumps(data) deserialized orjson.loads(serialized)异步I/O的“增压器”, 其应用场景的第四个方面, 是默认事件循环存在性能局限。面向I/O密集型任务而言的库, 以及其内部所设置的事件循环, 是一种极好且相当优异卓越的解决办法。然而, 我们原本能够施展拳脚的更大潜力, 却是可以做得更棒、更为精巧妥善周全妥善, 以超越这一情况的。是一个替代品, 可用于事件循环, 它基于libuv构建, libuv它是Node.js所依赖的同一个C语言库。的性能提升效果作者把它替换进而置于后端之时, 响应时间出现了下降, 降至原来的40%到50%这个幅度范围, 整个此过程, 既不需要增添线程, 又不需要额外的代码。为何能大幅提速简单示例一行代码实现性能飞跃只需一行代码, 就能把默认循环替换成, 使你的异步代码立刻“装上超速引擎”:import asyncio import uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())应用场景五, 存在一种无需对原代码做修改的“加速魔法”, 即为PyPy运行时, 并不去更换代码, 而仅仅是更换其运行的环境。偶尔, 增强性能的最终诀窍并非更换一个库, 而是更换一个运行之时的环境。存在一种即时编译JIT的实现, 此为PyPy。对于CPU密集类型的代码而言, 它可促成两倍至四倍的速度提升, 有可能还远超这个范围 , 还有更多。PyPy的加速机制适用条件要是你的代码属于纯粹编写, 同时对有关C语言扩展的依赖程度不高, 那么PyPy会明显战胜标准的运行时。简单示例直接运行获得速度只需用pypy3命令替换来运行你现有的脚本pypy3 my_script.py就这么简单无需多线程无需异步只有速度。场景六, PyPy的应用讲的是函数级的“原生指令”, 它是Numba科学计算时性能方面至关重要的工具。Numba是个堪称“作弊神器”之物件, 它具备把函数编译为经优化后机器码之本事。仅仅添加一个装饰器, 开发者便能使得字节码, 在LLVM那样一种流行的编译器基础设施之下, 被编译成为达到优化目的和满足需要条件的那种运行原本编码指令。这里的LLVM便是流行的被称为编译器基础设施的东西。Numba的加速原理简单示例一个装饰器实现数十倍加速只需在函数前添加njit装饰器from numba import njit import numpy as np njit def compute(arr): total 0 for x in arr: total x ** 2 return total print(compute(np.arange(1_000_000)))相较纯的情况而言, 这段代码的运行速度要比仅仅是纯的那种快上数十倍, 同样地此代码的运行过程中, 并不需要线程, 也不需要异步, 仅仅存在着一个装饰器。Numba的应用场景七, 分布式计算的那种“无感扩展”, Ray告别了手动管理并发会有的痛苦。Ray是这样一个库, 针对分布式计算专门得到布置, 然而就算停于单打独斗的机器条件之下, 它依旧能够供应出超乎寻常等级的卓有成效功效结果。Ray借助直观的API, 把底层的线程隐藏起来, 把底层的进程进行抽象, 甚至还把底层的分布式节点给藏起来屏蔽掉。Ray的效率优势Ray让开发者能够去定义actor也就是持久化对象, 还能定义task即无状态函数, 这些任务以及actor会自动在多核环境下, 甚至是在多台机器上展开扩展以及调度工作, 它给出了一种取代手动处理并发的办法。简单示例自动并行任务执行会自动并行执行所有任务的是下面的这段代码片段, 开发者对于任何线程逻辑、锁或者同步问题都不需要去关心。import ray ray.init() ray.remote def square(x): return x * x results ray.get([square.remote(i) for i in range(1_000)])Ray的应用场景八、为什么是时候放弃手动多线程了多线程, 于理论层面而言, 具备很强的特性, 然而, 在实际开展操作期间, 却给开发环节以及维护方面带来了诸多的负担:有竞态条件, 也就是Race, 还有死锁, 其调试过程复杂, 存在GIL的限制, 并且有着高昂的维护成本。犹如本文所阐述的那般, 现代的库, 尤其是那些借助Rust、C或者异步I/O的库, 把并发转变成了隐式的状态。它们将性能的提升推进到了库的内部实现层面这儿, 使得开发者能够专注于业务逻辑部分, 而并非同步机制方面。话简短来讲: 假设库能够更为帮你去处理并行化方面, 那么你就不存在再借着自己动用手脚, 手去编写并行代码的这种必要性。进阶组合与协同如果你想将性能提升到更高的层次可以将这些库进行组合当, 你的技术栈, 能够, 与性能, 协同, 而不是, 对抗性能时, 多线程, 就会, 彻底, 沦为, 最后的, 选择, 而不是, 必需品。九、总结速度与简洁性并行不悖曾经, 于往昔岁月之际, 若去追寻性能, 常常就意味着要去接纳复杂性, 诸如线程, 还有锁、队列这一类, 以及形形色色会让人倍感头疼的诸多问题。眼下恰当的工具能够于幕后替你达成全部并发事项。诸如PyPy、Numba以及Ray等这六个库证明确实可以一并兼具优雅的代码构建以及宛如闪电般快捷的速度, 与此同时也无需去牺牲开发者的那种幸福感。那么, 下次哪时你发觉脚本运转迟缓, 请莫再惯常性抓取线程咯, 而是要去觅寻出一个更为聪慧、更具优化性的库, 。并非是同时开展诸多事情, 方才称得上具备真正的速度, 而是唯独把一件事情, 做到极为卓越出色的那种程度, 称得上真正有速度, 这才是关键所在。