В предыдущей статье, посвященной оптимизации скриптов (Оптимизация скорости выполнения скриптов JavaScript) я описал 7 методов ускорения работы скриптов. Однако этот список не содержал методов оптимизации циклов, которые могут серьезно влиять на скорость выполнения. Сегодня я восполню этот пробел.
1. Оптимизация логического сравнения при итерациях цикла.
Рассмотрим обычный цикл for:
for (var i = 0; i < aList.length; i++) {
// тело цикла
}
При каждой итерации этого цикла происходит сравнение счетчика i со свойством length массива aList. Но как известно, обращение к константе или обычной переменной гораздо быстрее, чем получение значения через свойства объектов. Поэтому этот цикл можно оптимизировать путем замены этого значения на переменную:
// переменная len будет хранить длину массива
var len = aList.length;
for (var i = 0; i < len; i++) {
// тело цикла
}
// то же, что и предыдущий пример, но получение длины
// массива и инициализация счетчика объединены в одно выражение
for (var i = 0, len = aList.length; i < len; i++) {
// тело цикла
}
В этих примерах, переменной len присваивается длина массива. Второй пример выглядит предпочтительнее первого, так как он более компактен и чуточку быстрее за счет объединения инициализации счетчика и получения длины массива в одно выражение.
Дальнейшая оптимизация такого цикла возможна путем прохода его в обратном порядке (если это конечно допустимо логикой приложения). В этом случае, при каждой итерации мы будем сравнивать счетчик с константой, а не переменной, что еще более ускорит проход цикла:
for (var i = aList.length - 1; i >= 0; i--) {
// тело цикла
}
В этом примере, счетчик инициализируется индексом последнего элемента в массиве (который на 1 меньше длины массива), затем при каждом цикле производится сравнение с нулем (можно также использовать выражение i > -1) и уменьшение счетчика на единицу.
Тот же цикл можно еще более ускорить за счет объединения выражений сравнения и декремента:
for (var i = aList.length; --i >= 0;) {
// тело цикла
}
Это самый быстрый из известных мне циклов. Кроме высокой скорости прохода, он имеет еще и очень компактный код.
2. Использование do ... while вместо while.
Можно заменить циклы while на do ... while чтобы увеличить скорость выполнения. Допустим мы имеем следующий цикл:
var i = 0;
while (i < aList.length) {
// тело цикла
i++;
}
Этот код можно переписать с использованием do ... while без изменения результата выполнения:
var i = 0;
do {
// тело цикла
i++;
} while (i < aList.length);
Этот цикл будет работать быстрее цикла while, но его можно еще более оптимизировать, прогоняя его в обратном порядке:
var i = aList.length - 1;
do {
// тело цикла
i--;
} while (i >= 0);
И еще более ускоряем его путем объединения декремента со сравнением:
var i = aList.length - 1;
do {
// тело цикла
} while (--i >= 0);
В итоге получаем максимально оптимизированный цикл.
3. "Разворачивание" циклов.
Вместо того, чтобы выполнять одно выражение внутри цикла при каждом его прохождении, можно "развернуть" его, выполняя сразу несколько выражений при одной итерации. Рассмотрим пример:
var aList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var iSum = 0;
for (var i = 0, len = aList.length; i < len; i++) {
iSum += aList[i];
}
Этот цикл выполняет 20 итераций, каждый раз добавляя очередное значение массива к переменной iSum. Ту же операцию можно выполнять несколько раз внутри цикла, каждый раз увеличивая значение счетчика на единицу:
var aList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var iSum = 0;
for (var i = 0, len = aList.length; i < len; i++) {
iSum += aList[i];
i++;
iSum += aList[i];
i++;
iSum += aList[i];
i++;
iSum += aList[i];
i++;
}
Здесь добавление значения выполняется четыре раза, а количество итераций цикла сократится до пяти. Таким образом мы получаем прирост производительности. Этот пример можно еще более оптимизировать соединив итерацию с суммированием в одно выражение:
var aList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var iSum = 0;
for (var i = 0, len = aList.length; i < len; i++) {
iSum += aList[i++];
iSum += aList[i++];
iSum += aList[i++];
iSum += aList[i++];
}
Конечно же такой метод оптимизации увеличивает количество кода, и его стоит применять только в том случае если необходимо получить максимальную скорость прохода цикла, и если размер скрипта не играет большой роли.
Три упомянутых в статье метода довольно таки просты, но при этом способны значительно увеличить производительность. Если она конечно же для вас важна...