Проблемы с циклической оптимизацией в node.js и chrome?

Я случайно увидел поговорку о том, что избегание чтения атрибута длины массива на каждой итерации экономит время выполнения.
Но я не думаю, что это имеет значение, и провел эксперимент.
Тогда я встретил вопросы.
Вот код

// Function to get the execution time.
function testFunction (func) {
    console.time(func.name)
    for (let i = 0; i < 100; i++) {
        func()
    }
    console.timeEnd(func.name)
}

// Init the array to iterate.
let arr = []
for (let i = 0; i < 10000000; i++) {
    arr.push(0)
}

function loopWithSavedLength () {
    let len = arr.length
    for (let i = 0; i < len; i++) {
        arr[i] = i
    }
}

function loopWithoutSavedLength() {
    for (let i = 0; i < arr.length; i++) {
        arr[i] = i
    }
}

testFunction(loopWithoutSavedLength)
testFunction(loopWithSavedLength)

И вывод очень странный:

loopWithoutSavedLength: 889.633ms
loopWithSavedLength: 1023.269ms

Я много раз пробовал под Node.js 9.8.0 (с v8 6.2.414.46-node.21), и время выполнения loopWithoutSavedLength всегда короче, чем у loopWithSavedLength.

Я выполняю тот же сценарий в консоли chrome 66.0.3359.181 (с версией 6.6.346.32), и они почти не совпадают.

loopWithoutSavedLength: 1475.060302734375ms
loopWithSavedLength: 1493.14892578125ms  

Тогда я подумал, что это может быть проблема с присваиванием и проверкой пустого цикла.
Вот новый код.

function assignmentLoopWithSavedLength () {
    let len = arr.length
    for (let i = 0; i < len; i++) {
        arr[i] = i
    }
}

function assignmentLoopWithoutSavedLength () {
    for (let i = 0; i < arr.length; i++) {
        arr[i] = i
    }
}

function emptyLoopWithSavedLength () {
    let len = arr.length
    for (let i = 0; i < len; i++) {}
}

function emptyLoopWithoutSavedLength () {
    for (let i = 0; i < arr.length; i++) {}
}


testFunction(emptyLoopWithSavedLength)
testFunction(emptyLoopWithoutSavedLength)
testFunction(assignmentLoopWithSavedLength)
testFunction(assignmentLoopWithoutSavedLength)

Результаты в файле node.js:

emptyLoopWithSavedLength: 580.978ms
emptyLoopWithoutSavedLength: 584.923ms
assignmentLoopWithSavedLength: 1046.899ms
assignmentLoopWithoutSavedLength: 901.542ms

Результаты в консоли Chrome:

emptyLoopWithSavedLength: 584.126953125ms
emptyLoopWithoutSavedLength: 892.776123046875ms
assignmentLoopWithSavedLength: 1455.418212890625ms
assignmentLoopWithoutSavedLength: 1449.7529296875ms

Позже я понял, что значение, хранящееся в arr, может повлиять на последствия, и это влияет.

Новый код здесь:

let arr = []
function initArr () {
    arr = []
    for (let i = 0; i < 10000000; i++) {
        arr.push(0)
    }
}

function testFunction (func) {
    initArr()
    console.time(func.name)
    for (let i = 0; i < 100; i++) {
        func()
    }
    console.timeEnd(func.name)
}

Результат в node.js:

emptyLoopWithSavedLength: 560.739ms
emptyLoopWithoutSavedLength: 1134.274ms
assignmentLoopWithSavedLength: 1841.544ms
assignmentLoopWithoutSavedLength: 1609.649ms

Результат в консоли Chrome:

emptyLoopWithSavedLength: 592.8720703125ms
emptyLoopWithoutSavedLength: 910.886962890625ms
assignmentLoopWithSavedLength: 1457.467041015625ms
assignmentLoopWithoutSavedLength: 1488.855224609375ms

Теперь возникает новый вопрос.

В заключение:
1. Почему в файле node.js время loopWithoutSavedLength всегда меньше, чем loopWithSavedLength, независимо от того, установлены ли элементы массива в 0 или нет?
2. Почему создание нового массива и инициализация его элементов в 0 имеют другие последствия?

4 голоса | спросил Cipher 18 Maypm18 2018, 18:50:12

1 ответ


0
Разработчик V8 здесь.Вопрос 1: loopWithoutSavedLength быстрее, потому что для доступа к элементу необходимо выполнить проверку границ, которая в любом случае должна загружать длину.Его можно устранить, если условие цикла уже содержит такую ​​же проверку.Компилятору гораздо сложнее исключить дополнительную проверку, если вы сохраните длину.Таким образом, сохранение длины вручную означает, что работа дублируется.Тем не менее, разница обычно слишком мала, чтобы иметь значение (или даже быть измеримой).Смотрите отличную рецензию на https://mrale.ph/blog/2014/12/24/array-length-caching.html для получения более подробной информации.Вопрос 2: я не уверен.Значения элементов не должны иметь значения;а в Chrome нет.V8 в Node.js действует так же и должен работать так же, как в Chrome;но, возможно, есть разница между двумя версиями V8, которые вы тестировали.Меня смущают результаты, которые вы получили за ---- +: = 0 =: + ---- в Node.js, который, по-видимому, изменился с 584 до 1134 безо всякой на то причины - может быть, что-то еще в вашемсистема вызвала временное замедление?Можете ли вы воспроизвести этот результат?Говоря о воспроизведении: при повторном запуске этих тестов я вижу разницу в производительности примерно на 10% между повторными прогонами одного и того же теста (например, результаты, которые я получил за ---- +: = 1 =: + ---- болееход 10 пробежек был между 1075 и 1222).Это не необычно;Современные компьютеры - это сложные машины с множеством слоев и множеством источников изменений производительности.Это просто означает, что когда вы выполняете один тест из двух тестов и видите 1400 и 1450 миллисекунд, это может ничего не значить - в следующий раз они могут поменяться местами.Если вы видите 1455 и 1449, разница почти наверняка шумовая.
ответил jmrk 19 Mayam18 2018, 00:24:31

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132