奇怪的JavaScript / Node.js性能问题

目前我正在为一个非常奇怪的性能问题而苦苦挣扎。 SlowHeap所需的平均时间为1448.623ms,而FastHeap只需要550.425ms。 我已经多次查看过,但两者之间唯一的区别是,第一个在_arr的开头使用了“undefined”元素,而第二个不是。 但是,他们都做同样的行动等等。 我已经validation了下面的代码。

有没有人可以解释这个问题?

let slowHeapOperationCount = 0 let fastHeapOperationCount = 0 let slowHeapExchanged = [] let fastHeapExchanged = [] function SlowHeap(cmp = (a, b) => a > b ? 1 : a < b ? -1 : 0) { this.cmp = cmp this._arr = [undefined] this.size = 0 } function FastHeap(cmp = (a, b) => a > b ? 1 : a < b ? -1 : 0) { this.cmp = cmp this._arr = [] this.size = 0 } SlowHeap.prototype.bubbleUp = function (cmp, _arr, val) { let idxNr = this.size, parentIdxNr = idxNr >> 1 while (idxNr > 0 && cmp(_arr[parentIdxNr], val) > 0) { slowHeapExchanged.push([_arr[parentIdxNr], val]) _arr[idxNr] = _arr[parentIdxNr] _arr[parentIdxNr] = val idxNr = parentIdxNr parentIdxNr = idxNr >> 1 slowHeapOperationCount++ } } FastHeap.prototype.bubbleUp = function (cmp, _arr, val) { var idxNr = this.size, parentIdxNr = ((idxNr - 1) / 2) | 0; while (idxNr > 0 && cmp(_arr[parentIdxNr], val) > 0) { fastHeapExchanged.push([_arr[parentIdxNr], val]) _arr[idxNr] = _arr[parentIdxNr]; _arr[parentIdxNr] = val; idxNr = parentIdxNr; parentIdxNr = ((idxNr - 1) / 2) | 0; fastHeapOperationCount++ } } SlowHeap.prototype.push = function (val) { ++this.size this._arr.push(val) this.bubbleUp(this.cmp, this._arr, val) return this.size } FastHeap.prototype.push = function (val) { this._arr.push(val); this.bubbleUp(this.cmp, this._arr, val); return ++this.size; } const itemCount = 4000000 const slowHeap = new SlowHeap() const fastHeap = new FastHeap() // // Append the same Number Collection to each Heap: const numberCollection = [] for (let idxNr = 0; idxNr < itemCount; idxNr++) numberCollection.push(Math.random()) // // Benchmark for the Slow Heap: console.time('SlowHeap') for (let idxNr = 0; idxNr < itemCount; idxNr++) { slowHeap.push(numberCollection[idxNr]) } console.timeEnd('SlowHeap') // // Benchmark for the Fast Heap: console.time('FastHeap') for (let idxNr = 0; idxNr < itemCount; idxNr++) { fastHeap.push(numberCollection[idxNr]) } console.timeEnd('FastHeap') // // Verify the "_arr" from the Slow Heap against the Fast Heap: for (let idxNr = 0; idxNr < itemCount; idxNr++) { if (slowHeap._arr[idxNr + 1] !== fastHeap._arr[idxNr]) console.log('Unequal value found!') } if (slowHeapExchanged.length !== fastHeapExchanged.length) console.log('Help! Comp. is not equal.') for (let idxNr = 0, maxNr = slowHeapExchanged.length; idxNr < maxNr; idxNr++) { if (slowHeapExchanged[idxNr][0] !== fastHeapExchanged[idxNr][0] || slowHeapExchanged[idxNr][1] !== fastHeapExchanged[idxNr][1]) { console.log('Help!') } } console.log(slowHeapOperationCount) console.log(fastHeapOperationCount) 

我没有任何关于细节的知识,但看起来像是启用/禁用V8优化:当你用0.0代替undefined时, SlowHeap不再那么慢了(事实上,它比FastHeap对我来说更快)。

我的猜测是,因为你用浮点数组( Math.random() )填充数组,只要数组包含的项目都是相同的types,就可以执行优化。

一旦引入types不匹配( undefined不是浮点数),V8可能不得不切换到一个更通用的数组types,并放弃优化。