Проблемы с циклической оптимизацией в node.js и chrome? - PullRequest
0 голосов
/ 18 мая 2018

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

// 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 (с v8 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 имеют другое значение?

1 Ответ

0 голосов
/ 19 мая 2018

Разработчик V8 здесь.

Вопрос 1: loopWithoutSavedLength быстрее, потому что для доступа к элементу необходимо выполнить проверку границ, которая в любом случае должна загрузить длину.Его можно устранить, если условие цикла уже содержит такую ​​же проверку.Компилятору гораздо сложнее исключить дополнительную проверку, если вы сохраните длину.Таким образом, сохранение длины вручную означает, что работа дублируется.Тем не менее, разница обычно слишком мала, чтобы иметь значение (или даже быть измеримой).См. Превосходную рецензию на https://mrale.ph/blog/2014/12/24/array-length-caching.html для более подробной информации.

Вопрос 2: Я не уверен.Значения элементов не должны иметь значения;а в Chrome нет.V8 в Node.js действует так же и должен работать так же, как в Chrome;но, возможно, есть разница между двумя версиями V8, которые вы тестировали.Меня смущают результаты, которые вы получили для emptyLoopWithoutSavedLength в Node.js, который, по-видимому, изменился с 584 до 1134 безо всякой на то причины - может быть, что-то еще в вашей системе вызвало временное замедление?Можете ли вы воспроизвести этот результат?

Говоря о воспроизведении: при повторном запуске этих тестов я вижу различия в производительности примерно на 10% при повторных прогонах одного и того же теста (например, результаты, которые я получил за assignmentLoopWithSavedLength болееход 10 пробежек был между 1075 и 1222).Это не необычно;Современные компьютеры - это сложные машины с множеством слоев и множеством источников изменений производительности.Это просто означает, что когда вы выполняете один тест из двух тестов и видите 1400 и 1450 миллисекунд, это может ничего не значить - в следующий раз они могут поменяться местами.Если вы видите 1455 и 1449, разница почти наверняка шумовая.

...