感觉这次写的库能比 js 原生数组方法要快 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
hupo0
V2EX    Javascript

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

  •  2
     
  •   hupo0 2021-03-06 16:44:41 +08:00 4436 次点击
    这是一个创建于 1688 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

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

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

    真要用在后端处理大数据,那还是像 Python 那样用 C 做类库,让 js 调用吧,纯 js 再努力也提升不了太多
    darknoll
        24
    darknoll  
       2021-03-07 11:59:43 +08:00
    没有意义,前端无需注重效率,数据量大了应该是分页之类的,反正 Iterable 对象我都用 for of,我也知道 for 循环速度最快。
    wxsm
        25
    wxsm  
       2021-03-07 23:27:09 +08:00 via iPhone
    从算法角度来说,1 次循环,3 次循环,n 次循环,时间复杂度都是 O(n),并没有实质区别。
    cenbiq
        26
    cenbiq  
       2021-03-08 12:27:49 +08:00
    不禁感慨,C#的 Linq 真是有远见
    hupo0
        27
    hupo0  
    OP
       2021-03-08 13:47:21 +08:00
    @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  
       2021-03-08 14:10:31 +08:00
    @hupo0 用 yield 肯定会产生额外性能损耗嘛。js 是有 Generator 但是我接触下来应用很少,反观 C# IEnumerable 和 IEnumerator 几乎是必不可少的东西,no-stream 类似 rx 管道吧,收集操作最后一次循环执行,无非就是这些方法了。
    不过还是得夸 linq,想当初换到 js 和 java 各种不习惯。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2771 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 37ms UTC 13:00 PVG 21:00 LAX 06:00 JFK 09:00
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86