(20)操作符熔合——响应式Spring的道法术器

AI编译器-图常见优化算法-算子融合 通过将多个逐元素运算融合为一个大的逐元素运算,可以减少内存访问和计算的开销,从而提高性能。多个逐元素运算和批归一化融合:将多个逐元素运算和批归一化层融合为一个大的逐元素运算,减少内存访问和计算的开销。多个逐元素运算和全连接层融合:将多个逐元素运算和全连接层融合为一个大的全连接层,减少内存访问和计算的开销。多个逐元素运算和卷积层融合:将多个逐元素运算和卷积层融合为一个大的卷积层,减少内存访问和计算的开销。多个卷积层和逐元素运算融合:将多个卷积层和逐元素运算融合为一个大的卷积层,减少内存访问和计算的开销。 阅读详情

本系列其他文章见:《响应式Spring的道法术器》。
前情提要:响应式流 | Reactor3快速上手 | 深入理解响应式流规范

2.10 操作符熔合

操作符熔合是响应式编程领域比较前沿的研究话题,目的在于通过将多个操作符以某种方式熔合起来,以达到优化的效果,进而降低开销(比如执行时间,内存)。

以下部分内容参考了 Dávid Karnok 的 Operator-fusion(part1、part2)。

2.10.1 分代的概念

首先介绍一下关于响应式编程库的分代的概念。直至现在,响应式编程库及相关概念仍然在不断的更新和升级。作者根据自己在响应式编程领域的研究经验,将响应式编程库分为四代。

第零代

起初的响应式编程工具主要包括类似java.util.Observable的基于订阅者模式的API,以及那些基于回调的API,如 Swing/AWT/Android 中的 addXXXListener。但是两者都有些共同的不足:不方便组合(就像我们前边的操作链那样)。

第一代

后来,Erik Meijer 和他在微软的团队解决了难以组合的问题,从而诞生了第一代响应式编程库:2010 年左右的 Rx.NET,2011 年的 Reactive4Java,以及 2013 年早期的 RxJava。

其他的语言也陆续基于 Rx.NET 的架构开发了类似的库,但很快大家发现这种架构存在的问题。第一个问题是,最初的 IObservable/IObserver 在纯单线程中实现后,如果使用类似 take() 的操作符,之后的序列无法取消。Rx.NET 通过在诸如 range() 的数据源进行异步,绕开了这个问题。

第二个问题是,当生产者与消费者之间存在一个异步边界(不在同一线程)时,如果消费者消费数据的速度不够快,也会导致问题。这时消费者的代码会非常繁琐,这就是我们前面多次提到的 backpressure 问题。

第二代

RxJava团队针对上边的两个问题设计了一套新的架构。

首先是引入了 Subscriber 类,它能通过 isUnsubscribed() 方法判断是否取消订阅,数据源或者操作符发在数据之前都会调用该方法进行检查。

然后,backpressure 的问题则通过双方协调的方式解决,利用 Producer 接口,Subscriber 告知上游自己能处理数据的量(request() 方法)。

第三个改进是 lift() 函数,使用它可以直接在 Subscriber 之间进行函数式的变换。几乎所有操作符的实现都被重写,改成了利用新的 Operator 接口和 lift() 函数。

第三代

后边的故事本系列文章的读者就熟悉了。响应式编程的兴起使得大家意识到互相之间要兼容。于是来自多个公司的工程师们聚在了一起,设计了一套响应式流(Reactive-Streams)规范,主要成果是 4 个接口,30 条关于这几个接口的规则,以及这几个接口里的 7 个方法。

Reactive-Streams 规范使得响应式编程实现库之间可以相互兼容,从而能够随意切换具体的实现库。

因此基于响应式流的实现属于第三代,它的实现包括 RxJava 2.x,Project Reactor 和 Akka-Streams等。

第四代

在响应式流之上实现一套可组合的库需要完全不同的内部架构,因此 RxJava 2.x 不得不完全重写。作者参与了重写的过程,并发现有些操作符可以通过某种方式进行合并优化,以节省各种开销,例如队列,并发原子操作,以及数据请求等。

经过积极的交流之后,作者及其同事包括其他相应时库的作者创建了一个reactive-streams-commons 库,设计了一套实现上述优化的组件,之后称之为操作符熔合。

第四代的响应式编程库和第三代从外部看起来没多大区别,但其实内部操作符的实现发生了很大的变化。

Reactor3已经是第四代响应式编程库了。

2.10.2 响应式流的生命周期

聊到响应式流的生命周期,就需要再次搬出本章第一节的那个图:

为了方便下边讨论操作符熔合的问题,我们将整个过程分为三个阶段:

  1. 装配期,也就是.subscribe方法调用以前,针对每一个操作符会创建一个FluxXxx对象,并通过类似装饰器模式的方式关联起来;
  2. 订阅期,这是在.subscribe方法调用后,由最后一下操作符向上游依次调用subscribe方法以及向下回调onSubscribe方法的期间,这时候元素还没有发出;
  3. 运行期,这是数据生成并发往下游,且以最多一个终止事件(onError或者onComplete)终止的阶段。

不同的阶段有不同的优化方案,下面具体介绍两种主要的优化方案:宏熔合和微熔合。

2.10.3 宏熔合(macro-fusion)

宏熔合主要发生在装配期。通过2.1节自己动手写了一个简单的操作符实现,我们知道对于每个操作符都会创建一个FluxXxx即响应的Subscription对象。因此如果把连续的多个操作符合并为单个操作符,就可以优化订阅时的开销。这就是宏熔合的主要目标,具体来说有如下几种方式:

1)操作符替换

比如,有几个数据源,在对它们进行concat或merge操作时,如果数据源只发出一个元素,那么就没必要执行操作了,直接发出这个元素就可以了;

在比如,我们使用range生成的数据源并应用subscribeOn()时,对于这种单线程生成的数据源,subscribeOn和publishOn几乎没太大区别,因此可以替换为publishOn,以便引入更多优化。

2)替换为自定义发布者

有些组合出现的操作符可以合并为一个单独的操作符。

比如,just().subscribeOn()或just().flatMap()这样的组合,它们带来的开销(内部队列的创建,调度器 worker 的创建和销毁,多个原子变量的修改)相对于它们发出的数据过高了,尤其just()中只有一个元素的情况下,完全可以合并为一个发布者发出数据。

3)合并相同的操作符

比如如下例子:

    Observable.range(1, 10)
       .filter(v -> v % 3 == 0)
       .filter(v -> v % 2 == 0)
       .map(v -> v + 1)
       .map(v -> v * v)
       .subscribe(System.out::println);

filter和map分别调用了两次,这样代码会比较清晰,但是如果range比较大,那么优化其性能开销带来的收益就很明显了。策略就是对于同一类的操作符进行合并:

对于两个filter(),会把两个 lambda 表达式合并起来:

    Predicate<Integer> p1 = v -> v % 3 == 0;
    Predicate<Integer> p2 = v -> v % 2 == 0;
     
    Predicate<Integer> p3 = v -> p1.test(v) && p2.test(v);

map()也可以进行类似的合并:

    Function<Integer, Integer> f1 = v -> v + 1;
    Function<Integer, Integer> f2 = v -> v * v;
     
    Function<Integer, Integer> f3 = v -> f2.apply(f1.apply(v));

2.10.4 微熔合(micro-fusion)

微熔合发生在订阅期,通过多个操作符共用内部资源和数据结构以减少开销。

微熔合有以下几种形式:

1)Conditional Subscriber

阅读FluxFilter或FluxDistinct的源码发现,当我们使用过滤操作符filter()或者distinct()时,如果被丢弃掉,那么会调用 request(1)。request(1) 会触发原子递增操作,或者是 CAS 循环,大量这样的操作很快就能积累出性能下降。

Conditional Subscriber 的思路是为 Subscriber 增加一个boolean tryOnNext(T v)方法(见Fusable.ConditionalSubscriber),它可以告知上游自己是否会真的消费这个数据。这样在数据被丢弃时,能够跳过原子递增,并继续发射数据,直到实际发出的数据量达到了请求数。

2)同步熔合

在响应式编程库内部,许多地方都需要用到队列以便进行数据缓存。比如,有些拥有输出队列的操作符,和那些需要输入队列的操作符可以共用同一个队列实例,这样就可以节省内存分配。

同步熔合就是采用这一方式进行优化的。对于那些操作符的上游必然是同步的情形,它们可以假装自己是一个队列。

有些用于生成数据源操作符,比如range,fromIterable,fromArray,fromStream和fromCallable,它们都是同步的,而且都有队列的特性。因此它们内部的Subscription就可以实现Queue。而对于会使用队列的操作符比如observeOn(),flatMap(),publish()和zip()来说,如果发现上游的Subscription实现了Queue接口,那就无需创建自己的队列了。

3)异步熔合

有些情况下,数据源也有自己的队列,会在下游发出请求时从中取出数据并发出。与上边的情况类似,这时数据源也可以实现 Queue 接口,然后让后续的操作符直接使用,而不用创建自己的队列。但如果这个操作符也支持同步熔合的话,就需要采用新的协议。

在Reactor中,这种优化通过一个新的接口Fusable.QueuedSubscription来定义。

2.10.5 最后

操作符熔合是降低响应式数据流开销的一个有效途径,可以在保持API不变的情况下把开销降低到接近于常规的 Java Stream 序列(Project Reactor 2.5 M1 降低了 50%+,RxJava 2.x 则降低了 200%+)的水平。

操作符熔合主要是针对相邻的操作符进行优化,如果针对每一对可能相邻使用的操作符都进行优化设计,那将是矩阵式的数量级,因此操作符的熔合更多是一种基于使用情况的考量,尤其主要关注于用户经常使用到的操作符和操作符组合。

也许可以为操作符以及数据序列以某种方式进行建模,在模型上通过图算法自动发现那些可以被熔合的操作符,而这又将是另一个可以研究的话题了。目前操作符熔合仍然处于进行时,关于响应式编程库仍然有性能潜力可以挖掘。为这些大牛们点赞!

(6)Spring WebFlux性能测试——响应式Spring的道法术器 本系列其他文章见:《响应式Spring的道法术器》。 前情提要:Reactor快速上手 | Spring WebFlux快速上手 本文源码 1.4 从负载测试看异步非阻塞的优势 前面总是“安利”异步非阻塞的好处,下面我们就实实在在感受一下响应式编程在高并发环境下的性能提升。异步非阻塞的优势体现在I/O操作方面,无论是文件I/O、网络I/O,还是数据库读写,都可能存在阻塞的情... 阅读详情

相关推荐

(11)照虎画猫深入理解响应式流规范——响应式Spring的道法术器

本系列其他文章见:《响应式Spring的道法术器》。 前情提要:响应式流 | Reactor3快速上手 2 响应式编程之法 上一章本着“快速上手”的原则,介绍了响应式流的概念,以及Reactor 3的使用。这一章,我们基于Reactor 3的实现原理,从《响应式流规范》入手,深入了解响应式流开发库。 2.1 响应式流规范 现代软件对近乎实时地处理数据的需求越来越强烈,...

刘康的专栏 9899

深度学习中的性能加速利器:深入浅出学习算子融合(Operator Fusion)

算子融合(Operator Fusion)就是将这些连续的、可以合并的算子(BN,RELU)在计算层面上融合成一个“超级算子”。在这个融合的Kernel中,conv_result 和 bn_result 都是存在于GPU核心旁边的极快存储中的临时变量,它们未被写入到位于显卡PCB板上的全局显存颗粒中。:指的是一个基本的操作,比如卷积(Conv2d)、批归一化(BatchNorm2d)、激活函数(ReLU)等。:中间数据在GPU芯片内部极高速的缓存中,避免了与缓慢的全局显存进行的往返交互。

AggressiveYu的博客 2972

Spring WebFlux快速上手—响应式Spring的道法术器

1.3.3 Spring WebFlux Spring WebFlux是随Spring 5推出的响应式Web框架。 1、服务端技术栈 Spring提供了完整的支持响应式的服务端技术栈。 如上图所示,左侧为基于spring-webmvc的技术栈,右侧为基于spring-webflux的技术栈, Spring WebFlux是基于响应式流的,因此可以用来建立异步的、非阻塞的、事件驱动的服务。它采用Reactor作为首选的响应式流的实现库,不过也提供了对RxJava的支持。 由于响应式编程的特

m0_68850571的博客 712

论文笔记:DeepFuse: A Deep Unsupervised Approach for Exposure Fusion with Extreme Exposure Image Pairs

论文笔记:DeepFuse: A Deep Unsupervised Approach for Exposure Fusion with Extreme Exposure Image Pairs code:hli1221/Imagefusion_deepfuse: Image fusion based on deepfuse network - Tensorflow (based on ICCV2017: deepfuse), Unofficial (github.com) 年份:2017 会议:ICCV

浅若清风cyf的博客 4809

reactive.响应式Spring的道法术器.专栏相关代码

《响应式Spring的道法术器》专栏相关代码

某银行大数据分析平台之云原生架构

软考架构师论文

doudoudouy的博客 768

tvm学习笔记(五):tvm工作原理

一、总体流程: TVM的工作流程:首先,将网络表示成统一的表示形式(Intermediate Representation),并进行一些可重用的图优化;然后,利用不同的后端生成对应设备代码,如图1所示。 图1 tvm 工...

sinat_31425585的博客 1万+

加密视频如何破解 如何解除机器码绑定的视频

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 如何破解加密...

Q-980767270的博客 5万+

(4)Reactor 3快速上手——响应式Spring的道法术器

boot最新为:2.4.2 里面:spring-boot-starter-webflux 引用的即是,最新的 reactor 3.4.2 <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> <version>3.4.2</version> <scope>compile<

qq120631157的博客 1093

H.264 Profile与Level详解:如何为不同应用场景选择合适的编码配置

本文深入解析了H.264编码标准中的Profile与Level概念,指导开发者如何为不同应用场景选择合适的配置。文章对比了Baseline、Main和High三大Profile的特性与适用场景,并解释了Level如何限定视频的分辨率、帧率与码率上限。通过实战指南,帮助读者在视频通话、在线点播和专业制作等场景中,平衡画质、兼容性与性能,实现高效编码。

rgv2345678的博客 238

健康保险费用预测:可解释机器学习在精算建模中的实战落地

健康保险费用预测是医疗AI与保险科技交叉领域的核心任务,本质是基于临床变量与社会因素对个体医疗支出进行风险量化。其技术原理需兼顾统计建模能力与监管可解释性,XGBoost+SHAP组合因支持特征归因、边际效应分析和临床可干预推演,成为保险精算场景的主流选择。该技术显著提升保费定价精度、降低偿付风险,并支撑DRG费用预警、团险核保辅助及再保险尾部风险评估等关键应用。本文聚焦真实产线中的时间序列验证、临床分层特征工程与监管红线指标(如Bias@90th、Calibration Slope),深入解析如何让模型不

weixin_33702545的博客 306

Reactor-Core源码解析(三)

Reactor-Core源码解析(三) Fuseable接口源码解析 控制变量解析 [?] /** Indicates the QueueSubscription can't support the requested mode. */ int NONE = 0; /** Indicates the QueueSubscription can perform sync-fusion. */ int SYNC = 1; /** Indicates the QueueSubscription can perfo

KevinTen|AI Coding 实战 2408

GCC调试黑科技:-fomit-frame-pointer优化下如何保证栈回溯能力

本文深入探讨了GCC编译器在启用`-fomit-frame-pointer`优化选项时如何保证栈回溯能力。通过分析`.eh_frame`段的结构与DWARF调试信息,提供了从编译选项配置到实战调试的完整解决方案,帮助开发者在性能优化与调试能力之间找到最佳平衡点。

u6v7w8x的博客 904

计算图优化有哪些=>举例说明

计算图优化在提升深度学习模型执行效率上发挥着至关重要的作用。TVM通过多种优化策略,如常量折叠、算子融合、子图分割和内存优化,实现了高效的模型推理能力。相比于框架级和手写代码优化,TVM提供了更高的灵活性和自动化能力,特别适合异构计算环境和不同硬件后端的需求。

kingking44的博客 1025

Python爬虫实战:5分钟搞定Boss直聘数据抓取(附完整代理IP配置)

本文详细介绍了如何使用Python爬虫高效抓取Boss直聘的招聘数据,包括环境配置、反爬策略、数据采集与存储、数据分析等完整流程。通过实战案例,帮助开发者快速掌握招聘市场数据抓取技术,为职业决策提供数据支持。

weixin_30832143的博客 346

4-都2026年了,嵌入式发展前景怎么样,还适合入行吗?

本文系统分析了嵌入式行业的发展前景,指出到2026年嵌入式将迎来由"万物智联"驱动的黄金周期。文章回顾了嵌入式技术的四个演进阶段,从专用计算机到AIoT时代,强调其技术积累的延续性。作者从人才流向、产业智能化转型、高校新增专业等角度论证嵌入式行业的蓬勃发展,并提出"铁三角"模型分析其驱动力和护城河。最后给出职业发展路径建议,认为嵌入式适合长期主义者,在智能汽车、边缘计算等领域有广阔前景。文章建议读者抓住产业升级机遇,选择这一稳定且有复利效应的技术赛道。

指尖动听知识库 389

【AI系统】离线图优化技术

上一篇文章主要回顾了计算图优化的各个组成部分,包括基础优化、扩展优化以及布局和内存优化。这些优化方式在预优化阶段、优化阶段和后优化阶段都有所应用,以提高计算效率。同时,还介绍了 AI 框架和推理引擎在图优化方面的不同应用和侧重点。接下来,我们从计算图优化的各个组成部分开始逐步进行讲解。

ZOMI酱 1280

解锁机器人动态平衡算法:从原理到实践

机器人的动态平衡,是指机器人在运动过程中,面对各种复杂的动态变化和外界干扰,依然能够维持稳定状态,不发生倾倒或失去控制的能力。它就像是一位技艺精湛的舞者,在舞台上翩翩起舞时,无论做出多么复杂的动作,都能巧妙地调整身体姿态,始终保持平衡,展现出优美的舞姿。在实际应用中,机器人的动态平衡对于其稳定运行起着至关重要的作用。以双足机器人为例,当它行走在不平整的地面上时,地面的起伏会使机器人的重心不断发生变化。如果没有良好的动态平衡能力,机器人就很容易因为重心偏移而摔倒,无法完成行走任务。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 3838

(14)Reactor调度器与线程模型——响应式Spring的道法术器

本系列其他文章见:《响应式Spring的道法术器》。 前情提要:Reactor3快速上手 | 深入理解响应式流规范 | Reactor3自定义数据流 本文测试源码 2.4 调度器与线程模型 在1.3.2节简单介绍了不同类型的调度器Scheduler,以及如何使用publishOn和subscribeOn切换不同的线程执行环境。 下边使用一个简单的例子再回忆一下: ...

刘康的专栏 9049

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

(2)响应式流——响应式Spring的道法术器

本系列其他文章见:《响应式Spring的道法术器》。 前情提要: 什么是响应式编程 1.2 响应式流 上一节留了一个坑——为啥不用Java Stream来进行数据流的操作? 原因在于,若将其用于响应式编程中,是有局限性的。比如如下两个需要面对的问题: Web 应用具有I/O密集的特点,I/O阻塞会带来比较大的性能损失或资源浪费,我们需要一种异步非阻塞的响应式的库,而Jav...

刘康的专栏 2万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

(5)Spring WebFlux快速上手——响应式Spring的道法术器

本系列其他文章见:《响应式Spring的道法术器》。 前情提要:响应式流 | lambda与函数式 | Reactor快速上手 1.3.3 Spring WebFlux Spring WebFlux是随Spring 5推出的响应式Web框架。 1)服务端技术栈 Spring提供了完整的支持响应式的服务端技术栈。 如上图所示,左侧为基于spring-webmvc的技...

刘康的专栏 16万+

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

(3)lambda与函数式——响应式Spring的道法术器

1.3 Hello,reactive world 前面两篇文章介绍了响应式编程和响应式流的特性,一味讲概念终是枯燥,还是上手敲一敲代码实在感受一下响应式编程的“手感”吧。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ci4mI1sz-1612524581426)(https://leanote.com/api/file/getImage?fileId=5a893d70ab6441151d00126b)] 这一节,我们先了解一下lambda与函数式(已经了解的朋友可以直接跳到

qq120631157的博客 323

Operator Fusion:让 6 个 Kernel 变成 1 个

本文分析了算子融合(Kernel Fusion)在昇腾NPU上的优化原理与实现。通过将Attention计算中的6个独立Kernel融合为1个,减少了90%的Kernel启动和DDR读写开销,使LLaMA-7B推理延迟降低22%。文章详细介绍了三种融合级别:Element-Wise融合、Pattern融合和Graph-Level融合,对比了融合前后的执行链路差异,并阐述了编译器决策规则。同时指出融合边界条件,如指令Cache限制和L1容量约束。实验数据显示,在SeqLen=4096时,单Attention延

2501_93573368的博客 60

webflux系列--reactor源码(二)

操作符(Operator) 合并多个Flux combineLatest concat concatMap merge repeat cache 行为(behavior) 聚合操作 collect reduce distinct group by scan 其他 错误处理 Backpressure(背压) event retry using dematerialize,materialize 发布和订阅(hot流和cold流) subscribe //subscribe(consumer,

demon7552003的小本本 639

(16)Reactor的测试——响应式Spring的道法术器

本系列其他文章见:《响应式Spring的道法术器》。 前情提要:Reactor3快速上手 | 深入理解响应式流规范 本文测试源码 2.6 测试 在非常重视DevOps的今天,以及一些奉行TDD的团队中,自动化测试是保证代码质量的重要手段。要进行Reactor的测试,首先要确保添加reactor-test依赖。 reactor-test 用 Maven 配置 ...

刘康的专栏 7761
上一篇: (19)Reactor Processors——响应式Spring的道法术器
下一篇: JIRA(6.x/7.x)与Confluence(6.x)反编译破解方法
享学IT
博客等级 码龄12年 1516粉丝 91原创
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值