推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
hupo0

感觉这次写的库能比 js 原生数组方法要快

  •  2
     
  •   hupo0 · Mar 6, 2021 · 4879 views
    This topic created in 1921 days ago, the information mentioned may be changed or developed.

    no-stream 似乎比 js 原生数组方法快

    正在等待被锤的忐忑心理中。

    28 replies    2021-03-08 14:10:31 +08:00
    eason1874
        1
    eason1874  
       Mar 6, 2021
    不太懂。是循环展开的意思吗?话说现在 JS 不是都要编译了吗,这个在编译时不会自动优化吗?
    hupo0
        2
    hupo0  
    OP
       Mar 6, 2021
    @eason1874 应该不叫循环展开,是把多次循环合成一个。只见过 c++ 和 rust 有这类优化,而且对代码有不少限制,可能高级语言要考虑闭包什么的。
    cyberpoint
        3
    cyberpoint  
       Mar 6, 2021
    比如程序员为了方便快捷就会写这种代码:const foo = bar.filter(...).map(...).reduce(...)
    这样就会有 3 次遍历,但是其实这些...的过程是可以在一次遍历中完成的
    muzuiget
        4
    muzuiget  
       Mar 6, 2021
    明明就是写法问题,这种 [...xxx].map(xxx).filter(xxx).slice(xxx) 我为什么不直接用 for,也是一次循环。
    kkocdko
        5
    kkocdko  
       Mar 6, 2021
    是这样的,我记得 rust 的迭代器有这种优化
    hupo0
        6
    hupo0  
    OP
       Mar 6, 2021
    @cyberpoint 表达到位
    Jirajine
        7
    Jirajine  
       Mar 6, 2021 via Android   ❤️ 1
    这个应该不是语言的优化吧,只要方法实现上不返回另一个数组,而是返回一个 lazy evaluation 的 iterator 就可以链式调用多次只遍历一次了。
    muzuiget
        8
    muzuiget  
       Mar 6, 2021
    谁白了这种链式求值,就是方便而已,不在乎这点性能损失,真遇到巨大数组,就应该专门写优化代码。
    supermao
        9
    supermao  
       Mar 6, 2021
    这种不需要猜,你自己做个 benchmark 不就出来了?
    supermao
        10
    supermao  
       Mar 6, 2021
    @supermao 原来已经做了。。。
    Rorysky
        11
    Rorysky  
       Mar 6, 2021
    @muzuiget 这不就是 java stream 的写法么
    hupo0
        12
    hupo0  
    OP
       Mar 6, 2021
    @Jirajine iterator 转成另一个 iterator,一般是包多一层,该做的“结束检查”还是不会漏的,还是会有额外的性能损耗。实际用 benchmark 测也会发现比较慢。
    jones2000
        13
    jones2000  
       Mar 6, 2021   ❤️ 1
    太高深了, 看不懂, 学了 2 年 js, 只会用 for(var i=0;i<data.length;++i) .......
    musi
        14
    musi  
       Mar 6, 2021   ❤️ 1
    纠正一下,这不叫比“ js 原生数组方法要快”
    js 可没让你本来遍历一次的事用三次遍历完成
    geelaw
        15
    geelaw  
       Mar 6, 2021 via iPhone
    这个问题和数组遍历几遍没关系,主要区别在于内存分配。

    Array.map 每次都要分配新的数组,可以想象 no-stream 的 map 只是变换迭代器,当然快。当然这不能怪 Array,毕竟功能不同,要写出值得比较的代码可以对 mf 复合自己 map_count 次,再变换成 reducer,然后直接在 data 数组上用 reduce 。

    而且好好写 for 循环不香吗?
    kuunnnn
        16
    kuunnnn  
       Mar 6, 2021   ❤️ 1
    这不就是延迟计算吗,lazy.js 有做这个的,lodash 好像也有类似的
    shyangs
        17
    shyangs  
       Mar 6, 2021
    為什麼不用 lodash 的_.chain()
    molika
        18
    molika  
       Mar 6, 2021 via iPhone
    transduce 而已 clojure/clojureScript N 年前就这么玩了 话说 js 的 map reduce 啥的竟然不是惰性的 transduce ? 吃惊
    hupo0
        19
    hupo0  
    OP
       Mar 7, 2021
    @shyangs lodash 没用过,我才知道 chain 。看了下源码,他用一个 Wrapper 的抽象,在求值前,把中间的 actions 合成一个。虽然感觉大家想做的事情一样,但实现上效率不那么高。不过 lodash 自带的东西多,容错好,本身不用太注重效率。

    实际 benchmark 跑下来,速度和数组自带的方法差不多,但是内存上应该会更优越。
    hupo0
        20
    hupo0  
    OP
       Mar 7, 2021
    @geelaw 比较的代码确实有的斟酌的地方,但是太难找到合适的,就粗暴点只比较了 map 和 reduce 一起用的情况。

    就是不想好好写 for 循环。
    hupo0
        21
    hupo0  
    OP
       Mar 7, 2021
    @musi 所以用了似乎,实际上,如果要提高代码的表达,很多时候不会选择把逻辑都挤在一个循环里,至少我是这样的。
    civet
        22
    civet  
       Mar 7, 2021 via iPhone
    @molika js 不是天生就是为函数式编程服务的,而是社区各路大神引领了这种潮流,实际上就是底层没实现,写法先出现
    Rocketer
        23
    Rocketer  
       Mar 7, 2021 via iPhone
    js 聊性能的意义不是特别大,毕竟主要用途是前端,数组大小不会超过 100 条。相比较而言,节约代码提高开发效率更受欢迎。

    真要用在后端处理大数据,那还是像 Python 那样用 C 做类库,让 js 调用吧,纯 js 再努力也提升不了太多
    darknoll
        24
    darknoll  
       Mar 7, 2021
    没有意义,前端无需注重效率,数据量大了应该是分页之类的,反正 Iterable 对象我都用 for of,我也知道 for 循环速度最快。
    wxsm
        25
    wxsm  
       Mar 7, 2021 via iPhone
    从算法角度来说,1 次循环,3 次循环,n 次循环,时间复杂度都是 O(n),并没有实质区别。
    cenbiq
        26
    cenbiq  
       Mar 8, 2021
    不禁感慨,C#的 Linq 真是有远见
    hupo0
        27
    hupo0  
    OP
       Mar 8, 2021
    @cenbiq 看看所谓的 Zero cost abstraction - 刘雨培的文章 - 知乎
    https://zhuanlan.zhihu.com/p/24975048
    吐槽 C# - 刘雨培的文章 - 知乎
    https://zhuanlan.zhihu.com/p/30653282

    参考这两篇回答,17 年的 C#还没有对 linq 进行优化。虽然用迭代器也是相当于只遍历一遍,但是迭代器“调用”迭代器的过程还是会有额外的性能损耗。完美一点的是,能做到跟 C++一样,把迭代器 yield 的逻辑内联到一个循环里。

    想来 no-stream 的做法,是函数调用层面,把"yield"部分通过函数包函数的方式合并到一个循环里。虽然也是有函数调用方面的开销,但目前来看,会比 JS 的 Generator 还节省性能些。

    从现有的 C++和 Rust 的案例来看,其实正道还是 iterator,似乎这样的结构更容易优化。linq 也是在正道上,只是需要引擎对这部分进行优化。与之对应的是 JS 的 Generator 。可惜就是目前他们还很拉胯。
    cenbiq
        28
    cenbiq  
       Mar 8, 2021
    @hupo0 用 yield 肯定会产生额外性能损耗嘛。js 是有 Generator 但是我接触下来应用很少,反观 C# IEnumerable 和 IEnumerator 几乎是必不可少的东西,no-stream 类似 rx 管道吧,收集操作最后一次循环执行,无非就是这些方法了。
    不过还是得夸 linq,想当初换到 js 和 java 各种不习惯。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5925 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 89ms · UTC 02:09 · PVG 10:09 · LAX 19:09 · JFK 22:09
    ♥ Do have faith in what you're doing.