日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

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

 

作者: Peter 譚老師

轉(zhuǎn)發(fā)鏈接:https://mp.weixin.qq.com/s/Vs8CwdGJ6JUyH_Kp0H5l4Q

起因

  • 有人在思否論壇上向我付費(fèi)提問(wèn)

 

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

 

  • 當(dāng)時(shí)覺得,這個(gè)人問(wèn)的有問(wèn)題吧。仔細(xì)一看,還是有點(diǎn)東西的

問(wèn)題重現(xiàn)

  • 編寫一段Node.js代碼
var http = require('http');
  http.createServer(function (request, response) {
    var num = 0
    for (var i = 1; i < 5900000000; i++) {
        num += i    }    response.end('Hello' + num);
}).listen(8888);
  • 使用nodemon啟動(dòng)服務(wù),用time curl調(diào)用這個(gè)接口
60億次for循環(huán),原來(lái)這么多東西

 

  • 首次需要7.xxs耗時(shí)
  • 多次調(diào)用后,問(wèn)題重現(xiàn)
60億次for循環(huán),原來(lái)這么多東西

 

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

問(wèn)題排查

  • 排除掉CPU問(wèn)題,看內(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('測(cè)試');
    let num = 0;
    for (let i = 1; i < 5900000000; i++) {
      num += i;    }    console.timeEnd('測(cè)試');
    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);
  • 測(cè)試結(jié)果:
  • 內(nèi)存占用和CPU都正常
  • 跟字符串拼接有關(guān),此刻關(guān)閉字符串拼接(此時(shí)為了快速測(cè)試,我把循環(huán)次數(shù)降到5.9億次)
60億次for循環(huán),原來(lái)這么多東西

 

  • 發(fā)現(xiàn)耗時(shí)穩(wěn)定下來(lái)了

定位問(wèn)題在字符串拼接,先看看字符串拼接的幾種方式

  • 一、使用連接符 “+” 把要連接的字符串連起來(lái)
var a = 'JAVA'
var b = a + 'script'

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

  • 二、使用數(shù)組的 join 方法連接字符串
var arr = ['hello','java','script']
var str = arr.join("")
  • 比第一種消耗更少的資源,速度也更快
  • 三、使用模板字符串,以反引號(hào)( ` )標(biāo)識(shí)
var a = 'java'
var b = `hello ${a}script`
  • 四、使用 JavaScript concat() 方法連接字符串
var a = 'java'
var b = 'script'
var str = a.concat(b)

五、使用對(duì)象屬性來(lái)連接字符串

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方式(此時(shí)循環(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('測(cè)試');
    let num = 0;
    for (let i = 1; i < 590000000; i++) {
      num += i;    }    const arr = ['Hello'];
    arr.push(num);    console.timeEnd('測(cè)試');
    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);
  • 測(cè)試結(jié)果,發(fā)現(xiàn)接口調(diào)用的耗時(shí)穩(wěn)定了(注意此時(shí)是5.9億次循環(huán))
60億次for循環(huán),原來(lái)這么多東西

 

  • 《javascript高級(jí)程序設(shè)計(jì)》中,有一段關(guān)于字符串特點(diǎn)的描述,原文大概如下:ECMAScript中的字符串是不可變的,也就是說(shuō),字符串一旦創(chuàng)建,他們的值就不能改變。要改變某個(gè)變量的保存的的字符串,首先要銷毀原來(lái)的字符串,然后再用另外一個(gè)包含新值的字符串填充該變量

就完了?

  • 用+直接拼接字符串自然會(huì)對(duì)性能產(chǎn)生一些影響,因?yàn)樽址遣豢勺兊?,在操作的時(shí)候會(huì)產(chǎn)生臨時(shí)字符串副本,+操作符需要消耗時(shí)間,重新賦值分配內(nèi)存需要消耗時(shí)間。
  • 但是,我更換了代碼后,發(fā)現(xiàn),即使沒(méi)有字符串拼接,也會(huì)耗時(shí)不穩(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('測(cè)試');
    let num = 0;
    for (let i = 1; i < 5900000000; i++) {
    //   num++;
    }
    const arr = ['Hello'];
    // arr[1] = num;
    console.timeEnd('測(cè)試');
    used = process.memoryUsage().heapUsed / 1024 / 1024;
    console.log(
      `The script uses approximately ${Math.round(used * 100) / 100} MB`,
      'end',
    );    response.end('hello');
  })  .listen(8888);
  • 測(cè)試結(jié)果:
  • 現(xiàn)在我懷疑,不僅僅是字符串拼接的效率問(wèn)題,更重要的是for循環(huán)的耗時(shí)不一致
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('測(cè)試');
    for (let i = 1; i < 5900000000; i++) {
    //   num++;
    }
    console.timeEnd('測(cè)試');
    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);
  • 測(cè)試運(yùn)行結(jié)果:
  • for循環(huán)內(nèi)部的i++其實(shí)就是變量不斷的重新賦值覆蓋
  • 經(jīng)過(guò)我的測(cè)試發(fā)現(xiàn),40億次跟50億次的區(qū)別,差距很大,40億次的for循環(huán),都是穩(wěn)定的,但是50億次就不穩(wěn)定了.
  • Node.js的EventLoop:
60億次for循環(huán),原來(lái)這么多東西

 

  • 我們目前被阻塞的狀態(tài):
  • 我電腦的CPU使用情況

優(yōu)化方案

  • 遇到了60億次的循環(huán),像有使用多進(jìn)程異步計(jì)算的,但是本質(zhì)上沒(méi)有解決這部分循環(huán)代碼的調(diào)用耗時(shí)。
  • 改變策略,拆解單次次數(shù)過(guò)大的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('測(cè)試');
    for (let i = 1; i < 600000; i++) {
      num++;      for (let j = 0; j < 10000; j++) {
        num++;      }    }    console.timeEnd('測(cè)試');
    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é)果,耗時(shí)基本穩(wěn)定,60億次循環(huán)總共:

推翻字符串的拼接耗時(shí)說(shuō)法

  • 修改代碼回最原始的+方式拼接字符串
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('測(cè)試');
    for (let i = 1; i < 600000; i++) {
      num++;      for (let j = 0; j < 10000; j++) {
        num++;      }    }    console.timeEnd('測(cè)試');
    // 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);
  • 測(cè)試結(jié)果穩(wěn)定,符合預(yù)期:

總結(jié):

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

深度分析問(wèn)題

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

哪些場(chǎng)景會(huì)遇到這個(gè)類似的超大計(jì)算量問(wèn)題:

  • 圖片處理
  • 加解密

?

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

作者: Peter 譚老師

轉(zhuǎn)發(fā)鏈接:https://mp.weixin.qq.com/s/Vs8CwdGJ6JUyH_Kp0H5l4Q

分享到:
標(biāo)簽:循環(huán)
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定