<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          60億次for循環(huán),原來這么多東西

          2020-09-06 02:39

          起因

          • 有人在思否論壇上向我付費(fèi)提問
          • 當(dāng)時覺得,這個人問的有問題吧。仔細(xì)一看,還是有點(diǎn)東西的

          問題重現(xiàn)

          • 編寫一段Node.js代碼
          var?http?=?require('http');
          ??
          http.createServer(function?(request,?response)?{
          ????var?num?=?0
          ????for?(var?i?=?1;?i?????????num?+=?i
          ????}
          ????response.end('Hello'?+?num);
          }).listen(8888);
          • 使用nodemon啟動服務(wù),用time curl調(diào)用這個接口
          • 首次需要7.xxs耗時

          • 多次調(diào)用后,問題重現(xiàn)

          • 為什么這個耗時突然變高,由于我是調(diào)用的是本機(jī)服務(wù),我看CPU使用當(dāng)時很高,差不多打到100%了.但是我后面發(fā)現(xiàn)不是這個問題.

          問題排查

          • 排除掉CPU問題,看內(nèi)存消耗占用。
          var?http?=?require('http');

          http
          ??.createServer(function(request,?response)?{
          ????console.log(request.url,?'url');
          ????let?used?=?process.memoryUsage().heapUsed?/?1024?/?1024;

          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'start',
          ????);
          ????console.time('測試');
          ????let?num?=?0;
          ????for?(let?i?=?1;?i???????num?+=?i;
          ????}
          ????console.timeEnd('測試');
          ????used?=?process.memoryUsage().heapUsed?/?1024?/?1024;
          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'end',
          ????);
          ????response.end('Hello'?+?num);
          ![](https://imgkr2.cn-bj.ufileos.com/13455121-9d87-42c3-a32e-ea999a2cd09b.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=E3cF2kymC92LifrIC5IOfIZQvnk%253D&Expires=1598883364)

          ![](https://imgkr2.cn-bj.ufileos.com/1e7b95df-2a48-41c3-827c-3c24b39f4b5b.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=%252FANTTuhgbpIsXslXMc1qCkj2TMU%253D&Expires=1598883362)

          ??})
          ??.listen(8888);
          • 測試結(jié)果:
          • 內(nèi)存占用和CPU都正常
          • 跟字符串拼接有關(guān),此刻關(guān)閉字符串拼接(此時為了快速測試,我把循環(huán)次數(shù)降到5.9億次
          • 發(fā)現(xiàn)耗時穩(wěn)定下來了

          定位問題在字符串拼接,先看看字符串拼接的幾種方式

          • 一、使用連接符 “+” 把要連接的字符串連起來
          var?a?=?'java'
          var?b?=?a?+?'script'

          * 只連接100個以下的字符串建議用這種方法最方便

          • 二、使用數(shù)組的 join 方法連接字符串
          var?arr?=?['hello','java','script']
          var?str?=?arr.join("")
          • 比第一種消耗更少的資源,速度也更快

          • 三、使用模板字符串,以反引號( ` )標(biāo)識

          var?a?=?'java'
          var?b?=?`hello?${a}script`
          • 四、使用 JavaScript concat() 方法連接字符串
          var?a?=?'java'
          var?b?=?'script'

          var?str?=?a.concat(b)

          五、使用對象屬性來連接字符串

          function?StringConnect(){
          ????this.arr?=?new?Array()
          }

          StringConnect.prototype.append?=?function(str)?{
          ????this.arr.push(str)
          }

          StringConnect.prototype.toString?=?function()?{
          ????return?this.arr.join("")
          }

          var?mystr?=?new?StringConnect()

          mystr.append("abc")
          mystr.append("def")
          mystr.append("g")

          var?str?=?mystr.toString()

          更換字符串的拼接方式

          • 我把字符串拼接換成了數(shù)組的join方式(此時循環(huán)5.9億次)
          var?http?=?require('http');

          http
          ??.createServer(function(request,?response)?{
          ????console.log(request.url,?'url');
          ????let?used?=?process.memoryUsage().heapUsed?/?1024?/?1024;

          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'start',
          ????);
          ????console.time('測試');
          ????let?num?=?0;
          ????for?(let?i?=?1;?i???????num?+=?i;
          ????}
          ????const?arr?=?['Hello'];
          ????arr.push(num);
          ????console.timeEnd('測試');
          ????used?=?process.memoryUsage().heapUsed?/?1024?/?1024;
          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'end',
          ????);
          ????response.end(arr.join(''));
          ??})
          ??.listen(8888);
          • 測試結(jié)果,發(fā)現(xiàn)接口調(diào)用的耗時穩(wěn)定了(注意此時是5.9億次循環(huán))
          • 《javascript高級程序設(shè)計》中,有一段關(guān)于字符串特點(diǎn)的描述,原文大概如下:ECMAScript中的字符串是不可變的,也就是說,字符串一旦創(chuàng)建,他們的值就不能改變。要改變某個變量的保存的的字符串,首先要銷毀原來的字符串,然后再用另外一個包含新值的字符串填充該變量

          就完了?

          • +直接拼接字符串自然會對性能產(chǎn)生一些影響,因?yàn)樽址遣豢勺兊?,在操作的時候會產(chǎn)生臨時字符串副本,+操作符需要消耗時間,重新賦值分配內(nèi)存需要消耗時間。
          • 但是,我更換了代碼后,發(fā)現(xiàn),即使沒有字符串拼接,也會耗時不穩(wěn)定
          var?http?=?require('http');

          http
          ??.createServer(function(request,?response)?{
          ????console.log(request.url,?'url');
          ????let?used?=?process.memoryUsage().heapUsed?/?1024?/?1024;

          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'start',
          ????);
          ????console.time('測試');
          ????let?num?=?0;
          ????for?(let?i?=?1;?i?????//???num++;
          ????}
          ????const?arr?=?['Hello'];
          ????//?arr[1]?=?num;
          ????console.timeEnd('測試');
          ????used?=?process.memoryUsage().heapUsed?/?1024?/?1024;
          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'end',
          ????);
          ????response.end('hello');
          ??})
          ??.listen(8888);
          • 測試結(jié)果:
          • 現(xiàn)在我懷疑,不僅僅是字符串拼接的效率問題,更重要的是for循環(huán)的耗時不一致
          var?http?=?require('http');

          http
          ??.createServer(function(request,?response)?{
          ????console.log(request.url,?'url');
          ????let?used?=?process.memoryUsage().heapUsed?/?1024?/?1024;

          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'start',
          ????);
          ????let?num?=?0;
          ????console.time('測試');
          ????for?(let?i?=?1;?i?????//???num++;
          ????}
          ????console.timeEnd('測試');
          ????const?arr?=?['Hello'];
          ????//?arr[1]?=?num;
          ????used?=?process.memoryUsage().heapUsed?/?1024?/?1024;
          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'end',
          ????);
          ????response.end('hello');
          ??})
          ??.listen(8888);

          • 測試運(yùn)行結(jié)果:
          • for循環(huán)內(nèi)部的i++其實(shí)就是變量不斷的重新賦值覆蓋
          • 經(jīng)過我的測試發(fā)現(xiàn),40億次50億次的區(qū)別,差距很大,40億次的for循環(huán),都是穩(wěn)定的,但是50億次就不穩(wěn)定了.
          • Node.jsEventLoop:
          • 我們目前被阻塞的狀態(tài):

          • 我電腦的CPU使用情況

          優(yōu)化方案

          • 遇到了60億次的循環(huán),像有使用多進(jìn)程異步計算的,但是本質(zhì)上沒有解決這部分循環(huán)代碼的調(diào)用耗時。
          • 改變策略,拆解單次次數(shù)過大的for循環(huán):
          var?http?=?require('http');

          http
          ??.createServer(function(request,?response)?{
          ????console.log(request.url,?'url');
          ????let?used?=?process.memoryUsage().heapUsed?/?1024?/?1024;

          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'start',
          ????);
          ????let?num?=?0;
          ????console.time('測試');
          ????for?(let?i?=?1;?i???????num++;
          ??????for?(let?j?=?0;?j?????????num++;
          ??????}
          ????}
          ????console.timeEnd('測試');
          ????const?arr?=?['Hello'];
          ????console.log(num,?'num');
          ????arr[1]?=?num;
          ????used?=?process.memoryUsage().heapUsed?/?1024?/?1024;
          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'end',
          ????);
          ????response.end(arr.join(''));
          ??})
          ??.listen(8888);

          • 結(jié)果,耗時基本穩(wěn)定,60億次循環(huán)總共:

          推翻字符串的拼接耗時說法

          • 修改代碼回最原始的+方式拼接字符串
          var?http?=?require('http');

          http
          ??.createServer(function(request,?response)?{
          ????console.log(request.url,?'url');
          ????let?used?=?process.memoryUsage().heapUsed?/?1024?/?1024;

          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'start',
          ????);
          ????let?num?=?0;
          ????console.time('測試');
          ????for?(let?i?=?1;?i???????num++;
          ??????for?(let?j?=?0;?j?????????num++;
          ??????}
          ????}
          ????console.timeEnd('測試');
          ????//?const?arr?=?['Hello'];
          ????console.log(num,?'num');
          ????//?arr[1]?=?num;
          ????used?=?process.memoryUsage().heapUsed?/?1024?/?1024;
          ????console.log(
          ??????`The?script?uses?approximately?${Math.round(used?*?100)?/?100}?MB`,
          ??????'end',
          ????);
          ????response.end(`Hello`?+?num);
          ??})
          ??.listen(8888);
          • 測試結(jié)果穩(wěn)定,符合預(yù)期:

          總結(jié):

          • 對于單次循環(huán)超過一定閥值次數(shù)的,用拆解方式,Node.js的運(yùn)行耗時是穩(wěn)定,但是如果是循環(huán)次數(shù)過多,那么就會出現(xiàn)剛才那種情況,阻塞嚴(yán)重,耗時不一樣。
          • 為什么?

          深度分析問題

          • 遍歷60億次,這個數(shù)字是有一些大了,如果是40億次,是穩(wěn)定的
          • 這里應(yīng)該還是跟CPU有一些關(guān)系,因?yàn)?/span>top查看一直是在升高
          • 此處雖然不是真正意義上的內(nèi)存泄漏,但是我們?nèi)绻谝粋€循環(huán)中不僅要不斷更新i的值到60億,還要不斷更新num的值60億,內(nèi)存使用會不斷上升,最終出現(xiàn)兩份60億的數(shù)據(jù),然后再回收。(因?yàn)镚C自動垃圾回收,一樣會阻塞主線程,多次接口調(diào)用后,CPU占用也會升高)
          • 使用for循環(huán)拆解后:
          ?for?(let?i?=?1;?i???????num++;
          ??????for?(let?j?=?0;?j?????????num++;
          ??????}
          ????}
          • 只要num60億即可,解決了這個問題。

          哪些場景會遇到這個類似的超大計算量問題:

          • 圖片處理
          • 加解密
          ?

          如果是異步的業(yè)務(wù)場景,也可以用多進(jìn)程參與解決超大計算量問題,今天這里就不重復(fù)介紹了

          ?

          推薦閱讀


          1、力扣刷題插件

          2、你不知道的 TypeScript 泛型(萬字長文,建議收藏)

          3、TypeScript 類型系統(tǒng)

          4、immutablejs 是如何優(yōu)化我們的代碼的?

          5、typeScript 配置文件該怎么寫?

          6、前端換膚的N種方案,請收下

          7、【校招面經(jīng)分享】好未來-北京-視頻面試


          ?

          關(guān)注加加,星標(biāo)加加~

          ?

          如果覺得文章不錯,幫忙點(diǎn)個在看唄






          瀏覽 52
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  性感美女啪啪视频 | 欧美大鸡吧操逼 | JJ视频在线观看 | 在线国产干女 | 人妻无码成人短视频 |