RxJS的另外四种实现方式(三)——性能最高的库

Wesley13
• 阅读 527

接上篇 RxJS的另外四种实现方式(二)——代码最小的库(续)

代码最小的库rx4rx-lite虽然在性能测试中超过了callbag,但和most库较量的时候却落败了,于是我下载了most库,要解开most库性能高的原因。 我们先上一组测试数据,这是在我的windows10 上面跑的

dataflow for 1000000 source events

lib

op/s

samples

rx4rx-lite

11.29 op/s ± 1.47%

(56 samples)

rx4rx-fast

22.56 op/s ± 1.77%

(57 samples)

cb-basics

9.56 op/s ± 1.73%

(49 samples)

xstream

5.37 op/s ± 0.68%

(30 samples)

most

17.32 op/s ± 1.93%

(82 samples)

rx 6

6.28 op/s ± 3.10%

(35 samples)


经过我的不懈努力终于把性能超过了most库。 我先介绍一下fast库的工作原理,下一篇文章我再介绍如何从most库中找到性能提升的要领。

在fast库中,我们开始使用一个基类作为一切操作符的父类,名为Sink。

class Sink {
    constructor(sink, ...args) {
        this.defers = new Set()//用于存放需要释放的操作
        this.sink = sink
        this.init(...args)
        if (sink) sink.defers.add(this)//用于释放的连锁反应
    }
    init() {

    }
    //是否连锁释放
    set disposePass(value) {
        if (!this.sink) return
        if (value)
            this.sink.defers.add(this)
        else this.sink.defers.delete(this)
    }
    //数据向下传递
    next(data) {
        this.sink && this.sink.next(data)
    }
    //完成/error事件向下传递
    complete(err) {
        this.sink && this.sink.complete(err)
        this.dispose(false)
    }
    error(err) {
        this.complete(err)
    }
    //释放即取消订阅功能
    dispose(defer = true) {
        this.disposed = true
        this.complete = noop
        this.next = noop
        this.dispose = noop
        this.subscribes = this.subscribe = noop
        defer && this.defer() //销毁时终止事件源
    }
    defer(add) {
        if (add) {
            this.defers.add(add)
        } else {
            this.defers.forEach(defer => {
                switch (true) {
                    case defer.dispose != void 0:
                        defer.dispose()
                        break;
                    case typeof defer == 'function':
                        defer()
                        break
                    case defer.length > 0:
                        let [f, thisArg, ...args] = defer
                        if (f.call)
                            f.call(thisArg, ...args)
                        else f(...args)
                        break
                }
            })
            this.defers.clear()
        }
    }
    subscribe(source) {
        source(this)
        return this
    }
    subscribes(sources) {
        sources.forEach(source => source(this))
    }
}

为了性能,代码量稍微有点多了。原本传入next和complete函数,现在变为传入sink对象,这里十分类似向Observable传入Observer对象。但是与rxjs不同的是,我们的Observable仍然是一个函数,我们看一个从数组构造Observable的代码

exports.fromArray = array => sink => {
    sink.pos = 0
    const l = array.length
    while (sink.pos < l && !sink.disposed) 
        sink.next(array[sink.pos++])
    sink.complete()
}

这个pos为什么不直接定义一个变量呢?let pos = 0这是常规做法,这里把变量定义到了对象的属性上面,纯粹是为了提高一点点性能,经过测试发现,直接访问(读写操作)局部变量,比访问对象的属性要慢一些。

由于大部分的操作符都是相同的调用方式,所以可以抽象成一个函数

exports.deliver = Class => (...args) => source => sink => source(new Class(sink, ...args))

take操作符就变成了这样

class Take extends Sink {
    init(count) {
        this.count = count
    }
    next(data) {
        this.sink.next(data)
        if (--this.count === 0) {
            this.defer()
            this.complete()
        }
    }
}
exports.take = deliver(Take)

而我们的subscriber就变成了这样

exports.subscribe = (n, e = noop, c = noop) => source => {
        const sink = new Sink()
        sink.next = n
        sink.complete = err => err ? e(err) : c()
        source(sink)
        return sink
    }

至此fast库的基本构建逻辑已经展示完毕。 至于为什么这么快,就请听下回分解。 (未完待续)

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
2年前
RxJS的另外四种实现方式(后记)—— 同时实现管道和链式编程
目录RxJS的另外四种实现方式(序)(https://my.oschina.net/langhuihui/blog/2051754)RxJS的另外四种实现方式(一)——代码最小的库(https://my.oschina.net/langhuihui/blog/2051770)RxJS的另外四种实现方式(二)——代码最小的库(续)
Wesley13 Wesley13
2年前
RxJS的另外四种实现方式(五)——使用生成器实现
接上一篇RxJS的另外四种实现方式(四)——性能最高的库(续)(https://my.oschina.net/langhuihui/blog/2071675)js的生成器一般情况下使用场景很少,开发者接触的不是很多。不了解的可以先行查看js语法了解。这里把其中的执行顺序图解一下调用方
Wesley13 Wesley13
2年前
RxJS的另外四种实现方式(四)——性能最高的库(续)
接上一篇RxJS的另外四种实现方式(三)——性能最高的库(https://my.oschina.net/langhuihui/blog/2054887)上一篇文章我展示了这个最高性能库的实现方法。下面我介绍一下这个性能提升的秘密。首先,为了弄清楚Most库究竟为何如此快,我必须借助其他工具。比如chrome的devtools性能分析,刚开始
Stella981 Stella981
2年前
Redis进阶应用:Redis+Lua脚本实现符合操作
!(https://oscimg.oschina.net/oscnet/835168748db35a7a20a2e4f148db89ce4f8.png)一、引言Redis是高性能的keyvalue数据库,在很大程度克服了memcached这类key/value存储的不足,在部分场景下,是对关系数据库的良好补充。得益于超
Wesley13 Wesley13
2年前
Java 8:如何使用流方式查询数据库?
Speedment是使用ORM方式操作数据库的一种选择,以前我们需要100行操作数据库的Java代码,在Java8中,可能只需要一行代码。在90年代末,我使用Java开发数据库应用的时候,许多代码逻辑都需要自己来编写,比如捕获异常、类型转换等,经过许多改动,最后这些代码变得难以维护和扩展。!Java8:如何使用流方式查询
Stella981 Stella981
2年前
Golang
golang官方为我们提供了标准的json解析库–encoding/json,大部分情况下,使用它已经够用了。不过这个解析包有个很大的问题–性能。它不够快,如果我们开发高性能、高并发的网络服务就无法满足,这时就需要高性能的json解析库,目前性能比较高的有jsoniterator和easyjson。现在我们需要引进一个高性能的json解析库
Stella981 Stella981
2年前
OneAPM 技术公开课:北京,北京!
随着互联网行业的高速发展,数据库已经是绝大多数IT应用的核心因素,虽然数据库种类繁多,但是多层体系结构以及SOA的发展,使得应用逻辑的实现前移。数据库的性能与其功能相比较,变得越来越重要了。而对每个开发者或者DBA而言,都应该用更精深的方式来实现数据库性能的优化和改进。本次公开课由应用性能管理领军企业OneAPM主办,特别邀请了来
Stella981 Stella981
2年前
RxJS的另外四种实现方式(六)——使用Stream类实现
接上一篇RxJS的另外四种实现方式(五)——使用生成器实现(https://my.oschina.net/langhuihui/blog/2120113)该实现方式与之前几种不同的,该实现方式仅针对Nodejs环境。在Nodejs环境中,提供了Stream类,包括Readable、Transform、Writeable等子类都是可扩展的。从字面
Wesley13 Wesley13
2年前
Sql优化器究竟帮你做了哪些工作?
上一篇,我们介绍了《DB——数据的读取和存储方式》(https://my.oschina.net/u/1859679/blog/1581379),这篇聊聊sql优化器的工作。关系型数据库的一大优势之一,用户无需关心数据的访问方式,因为这些优化器都帮我们处理好了,但sql查询优化的时候,我不得不要对此进行关注,因为这牵扯到查询性能问题。有经验的程序
子桓 子桓
11个月前
Java性能分析软件分享
Java性能分析软件分享JProfiler13mac激活啦,适用于Java开发人员和企业用户,可帮助他们识别和解决Java应用程序中的性能问题,提高应用程序的性能和稳定性。JDBC,JPA和NOSQL的数据库分析数据库调用是业务应用程序中性能问题的主要原因