日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

js与c语言效率_JavaScript比c语言的性能差了多少?

發布時間:2024/7/19 javascript 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js与c语言效率_JavaScript比c语言的性能差了多少? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我這里先不說和C之間的性能差距,而是展開說JavaScript遞歸優化問題,有人問我為什么不說性能差距,我:???這個問題就跟問地球為什么是圓的一樣(明擺著)

傳統的遞歸函數,比如:

function a(){

return a()

}

這是一個經典的遞歸,在函數a內部調用自身,調用棧的機制如下:每調用一個函數,解釋器就會把該函數添加進調用棧并開始執行.

正在調用棧中執行的函數還調用了其它函數,那么新函數也將會被添加進調用棧,一旦這個函數被調用,便會立即執行.

當前函數執行完畢后,解釋器將其清出調用棧,繼續執行當前執行環境下的剩余的代碼.

當分配的調用棧空間被占滿時,會引發“堆棧溢出”錯誤.

這里我們做一個測試,嘗試運行這個遞歸函數,找出調用棧深度:

let index = 0

function a(){

index += 1

console.log(index)

return a()

}

執行結果如下:

// 省略多行輸出

> 12559

> 12560

> 12561

> 12562

> 12563

> Uncaught RangeError: Maximum call stack size exceeded

at console.X.t. [as log] (init.js:1)

這里可以看出,當遞歸深度達到12563的時候,調用棧爆掉了,平時使用是沒有問題的,但是對于某些特殊或者極端情況,你又有相似需求的情況下,怎么樣突破這個調用棧限制,做到永不爆棧呢?這里需要一個比較hack的寫法, 我們對上面的遞歸函數進行一下改造:

let index = 0

async function a(){

await undefined

index += 1

console.log(index)

return await a()

}

我們來執行一下看一下結果:

// 省略多行輸出

> 100590

> 100591

> 100592

// 省略多行輸出

事實上,這個遞歸函數永遠不會停止,它會一直執行下去,也沒有爆棧,這是一個神奇的優化,可以讓你寫出非常大深度的遞歸而不會出現問題,這個優化的關鍵就是:

async function() { await undefined }

首先將遞歸函數改為async函數,然后在內部最好第一行 await undefined;

這個操作的原理就是:

1, async創建微任務隊列,然后執行器執行當前隊列.

2,此時遇到await undefined,其實這個寫法等同于await (async () => {})和await Promise.resolve(setTimeout)這幾種寫法效果等同,用unedfined只是為了在實現同樣效果的情況下更簡潔,既然已經等同了,那就從這三個寫法分析起.

3,此時,執行器發現第一個任務完全沒有等待,馬上完成了,但是執行器發現后面的任務是需要等待的,并不會馬上完成.

4,這時候執行器為了microtask(也就是協程)調度的合理優化,不會讓這個微任務隊列始終占有這個execution,而是會把當前微任務隊列轉移到別的execution去執行(您幾位走得慢,請去那邊空閑的地方走).

5,轉移execution帶來的操作就是,因為沒辦法直接轉移調用棧,所以會先將當前調用棧入堆,然后把任務隊列轉移到別的execution.

6,然后隊列里面接下來的任務全部都是使用新創建的execution去執行.

這個操作的本意就是為了讓當前棧入堆,而且這個寫法在C#和Kotlin里面是完全通用的,因為這3個語言的異步方案都是基本類似,而這個寫法來自Rust群一位群友的發現,當時我看到這種寫法的時候也表示了驚奇,然后對于遞歸大面積使用這種寫法,目前沒有發現什么問題.

這兩天發現有人@星風雪月對執行機制有很深的成見,所以我這里使用Node.JS的async hooks做了一下異步執行的調試,這是測試代碼:

const async_hooks = require("async_hooks")

let index = 0

let print_buffer = ""

/*** async hooks會追蹤async調用,* 而console.log使用異步輸出,* 所以這里使用同步方法模擬console*/

function println(log) {

print_buffer += log + "\n"

}

/* 創建鉤子 */

async_hooks.createHook({

init(asyncId, type, triggerAsyncId) {

const eid = async_hooks.executionAsyncId()

println("init: *********************************")

println("init: triggerAsyncId " + triggerAsyncId)

println("init: executionAsyncId " + eid)

println("init: asyncId " + asyncId)

println("init: type " + type)

}

})

.enable()

/*********** 測試區 **********/

async function A() {

/* 為了觀察方便只執行2次 */

println("A: runing")

index += 1

if (index === 2) return undefined

// 有優化遞歸 await undefined

return await A()

}

/*********** 測試區 **********/

A().then(() => {

console.log(print_buffer)

})

執行之后的輸出:

init: *********************************

init: triggerAsyncId 1

init: executionAsyncId 1

init: asyncId 2

init: type PROMISE

A: runing

init: *********************************

init: triggerAsyncId 2

init: executionAsyncId 1

init: asyncId 3

init: type PROMISE

init: *********************************

init: triggerAsyncId 3

init: executionAsyncId 1

init: asyncId 4

init: type PROMISE

init: *********************************

init: triggerAsyncId 2

init: executionAsyncId 1

init: asyncId 5

init: type PROMISE

init: *********************************

init: triggerAsyncId 4

init: executionAsyncId 4

init: asyncId 6

init: type PROMISE

A: runing

init: *********************************

init: triggerAsyncId 6

init: executionAsyncId 4

init: asyncId 7

init: type PROMISE

這里看executionAsyncId標志,這個是當前執行器的ID,這里可以看到當經過await undefined之后, 執行器從1變成了4,說明這里發生了execution轉移,下面我修改一下代碼,變成不優化的寫法:

// 無優化遞歸// await undefinedreturn await A()

我這里將await undefined刪除,再來執行看看會是什么情況:

init: *********************************

init: triggerAsyncId 1

init: executionAsyncId 1

init: asyncId 2

init: type PROMISE

A: runing

init: *********************************

init: triggerAsyncId 1

init: executionAsyncId 1

init: asyncId 3

init: type PROMISE

A: runing

init: *********************************

init: triggerAsyncId 3

init: executionAsyncId 1

init: asyncId 4

init: type PROMISE

init: *********************************

init: triggerAsyncId 2

init: executionAsyncId 1

init: asyncId 5

init: type PROMISE

再看executionAsyncId,這里始終是使用1去執行,所以沒有轉移execution執行,這里就很能說明問題了,await undefined可以轉移執行器執行,讓當前棧入堆,這樣可以使調用棧不會溢出,達到深遞歸優化的目的.

對于評論區朋友說明,這種方式會阻塞macrotask,所以不推薦這種寫法,我這里并不表示完全反對意見,我這里來說一下我自己的看法:

異步任務歸屬microtask,而其他事件回調歸屬macrotask,microtask的優先級本身就比macrotask要高,所以肯定是microtask先執行,然后才輪到macrotask,像setTimeout(0)這種本身就是屬于macrotask,肯定要等到microtask執行完成之后才能執行,不過這確實會帶來一個問題,就是對已經運行的macrotask產生時間分辨率精度影響,比如定時器偏移,定時器不會精準得按時間分片執行任務,所以這種寫法見仁見智,你如果需要精確macrotask執行的場景還是慎用.

總結

以上是生活随笔為你收集整理的js与c语言效率_JavaScript比c语言的性能差了多少?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。