[求助] 求质数的 nodejs 程序内存溢出,能帮忙分析是哪儿溢出了吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
xiaoming1992
V2EX    程序员

[求助] 求质数的 nodejs 程序内存溢出,能帮忙分析是哪儿溢出了吗?

  •  
  •   xiaoming1992 2020-08-15 16:19:56 +08:00 1954 次点击
    这是一个创建于 1933 天前的主题,其中的信息可能已经有所发展或是发生改变。

    程序功能是将质数依次写入文件, 执行 ts-node main.ts, 代码在下面

    常规判断是否是质数, 是逐个判断 [2, Math.sqrt(num)] 是否能整除目标数, 我的思路是维护一个质数数组, 先从该数组中逐个判断是否能整除, 然后再依次加一, 效率还行(?), 每秒能判断十万个左右, 加上写入文件的话每秒能 1.5 万个左右

    但是跑到 2.2 千万时, 程序报错了, 重新执行, 从 2.2 千万跑到 3.5 千万时又报错了; 就我的渣渣水平来看, 大概是内存溢出? 但是我从头到尾看了我的程序, 除了那个质数数组一直占用内存, 其他的变量应该都能被回收啊, cpu 占用也一直在 50%左右, 不高啊, 求助为什么崩了呢?

    部分报错如下

    [4828:000002AA1DB95410] 818854 ms: Mark-sweep 1753.6 (2051.2) -> 1753.6 (2051.2) ms (average mu = 0.191, current mu = 0.000) last resort GC in old space requeste [4828:000002AA1DB95410] 819628 ms: Mark-sweep 1753.6 (2051.2) -> 1753.6 (2052.2) ms (average mu = 0.207, current mu = 0.227) allocation failure GC in old space r ... FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScr memory 

    ------------ 代码 -----------

    // main.ts import * as fs from "fs" const primeStr = fs.readFileSync("primes.txt", { encoding: "utf-8", flag: "a+", }) const primes = primeStr.split(",").map((n) => +n).filter((n) => n >= 2) let primeLastIdx = primes.length - 1 function checkIsInt(num: number) { return num === num >> 0 } /** * 返回 a 是否是 b 的因子 * * 返回值 true: 是因子 * 返回值 false: 不是因子, 且大于 a 的数都不是因子 * 返回值 1: 不是因子, 但大于 a 的数可能存在因子 */ function isFactorOf(a: number, b: number) { const devided = b / a if (a > devided) { return false } if (checkIsInt(devided)) { return true } return 1 } function checkIsPrime(num: number) { let prime: number for (let i = 0; i <= primeLastIdx; i += 1) { prime = primes[i] const isFactor = isFactorOf(prime, num) if (isFactor === 1) { continue } return !isFactor } for (let i = prime + 1; true; i += 1) { const isFactor = isFactorOf(i, num) if (isFactor === 1) { continue } return !isFactor } } function main() { for (let i = primes[primeLastIdx] + 1 || 2; i < 101000000; i += 1) { process.stdout.write(`\r${i}`) if (checkIsPrime(i)) { primeLastIdx += 1 primes.push(i) fs.writeFileSync("./primes.txt", `${i},`, { encoding: "utf-8", flag: "a+", }) } } } main() 
    第 1 条附言    2020-08-15 23:43:40 +08:00

    ---------------- 新增 bug report ---------------
    bug report, 有兴趣的可以帮忙看看(我是一点看不懂 T_T)

    第 2 条附言    2020-08-16 15:06:27 +08:00

    估计是 #8 所说的原因,文件写入改用:

    const fd = fs.openSync(fileName, "a+") __for (...) { ____if (isPrime(...)) { ________fs.writeSync(fd, "...") ____} __} 

    之后速度很快,也不崩溃了,在十几亿的情况下还能保持每秒100万左右。(我还用C++重写了,结果C++的速度还不如js,果然菜鸟写什么语言都慢。。。)
    执行情况如下:

    >>> 开始执行 加载质数表文件: [质数表.txt]...
    <<< 执行完毕 加载质数表文件: [质数表.txt], 耗时 240 ms

    >>> 开始执行 解析质数表文件...
    <<< 执行完毕 解析质数表文件, 耗时 45.267 秒

    >>> 开始执行 检测 [616399999, 2147483643]...
    100.0%: 2,147,200,001
    <<< 执行完毕 检测 [616399999, 2147483643], 耗时 22 分 42 秒

    共计检测 1,531,083,644 个数, 平均每秒检测 1,123,675

    8 条回复    2020-08-16 00:34:43 +08:00
    crella
        1
    crella  
       2020-08-15 19:30:18 +08:00 via Android   1
    我看不出来什么问题,建议用不同进程执行,每个进程读取指定区间的已知质数群的 txt 文件,运算后再写入文件
    crella
        2
    crella  
       2020-08-15 19:42:37 +08:00 via Android   1
    或者试试 redis ?因为不是编译语言的语言的数组相对来说占内存多很多
    roscoecheung1993
        3
    roscoecheung1993  
       2020-08-15 21:24:35 +08:00   1
    快照了一下,疑似写完文件后会往事件循环队列里放一个异步任务的对象,分配了相当多的 Object
    noe132
        4
    noe132  
       2020-08-15 21:55:41 +08:00   1
    你 stdout 写的太频繁了,影响了执行速度。
    建议每 1024 个数输出一下,能快不少。我这目测有 6-8 倍的提升。

    if (i % 1024 === 0) {
    process.stdout.write(`\r${i}`)
    }

    质数判断也有优化空间,随便改一改似乎能有 1 倍的性能提升

    node 12.16.1 似乎并没有出现内存泄漏
    private bytes 一直稳定在 110MB 左右,
    xiaoming1992
        5
    xiaoming1992  
    OP
       2020-08-15 23:50:26 +08:00 via iPhone
    @crella #1 用多进程, 可能效率还会提升不少, 但由于目前已经存在未知的 bug, 所以不想再做其他优化引入新的变量了。 #2 redis 同理

    @noe132 #4 对对对,我忘了 stdout 也会影响性能了。。。还有方便说说质数判断怎么优化吗?


    @roscoecheung1993 方便说说如何快照任务队列吗?我这边想试试看看。毕竟看我的程序好像没有哪儿用到异步了,怎么会分配异步任务呢。。。
    xiaoming1992
        6
    xiaoming1992  
    OP
       2020-08-15 23:55:32 +08:00
    @noe132 原来一直是 stdout 影响了速度, 我改成 `if (i % 20000 === 0)` 后, 速度直接飙升到将近 1 千万 每秒。。。是我年轻了, 我直觉以为 stdout 性能没问题呢
    learningman
        7
    learningman  
       2020-08-16 00:12:30 +08:00/span>   1
    质数算法那么多。。。线性筛,埃氏筛,一堆
    roscoecheung1993
        8
    roscoecheung1993  
       2020-08-16 00:34:43 +08:00   1
    @xiaoming1992 跑的时候给 node 传参 --inspect,在浏览器里连上 inspector 可以捕获堆快照和调用栈

    异步这个可能是 stdout.write 或者 fs.writeFileSync 触发的某些回调,没细看。
    感觉也可能是算的比 IO 快太多,导致 IO 堆积了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1069 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 17:40 PVG 01:40 LAX 09:40 JFK 12:40
    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