Как стать автором
Поиск
Написать публикацию
Обновить

Одна маленькая оптимизация

Время на прочтение2 мин
Количество просмотров38K
Совсем недавно со мной поделились историей одной оптимизации (привет stanislaw), которая показалась мне довольно забавной.

Проект игровой и с постоянно растущей базой пользователей, но так как расширятся в ширь не хотелось — возникла задача оптимизировать существующий код в узких местах. После недолгого профайлинга, буквально сразу, удалось найти одно такое узкое место, которое на первый взгляд не вызвало бы ни у кого подозрений:

for (A a : arrayListA) { 
    // do something
    for (B b : arrayListB) {
        // do something
        for (C c : arrayListC) {
            // do something
        }
    }
}

Доступа к коду у меня нету, поэтому я передаю лишь суть повествования. Есть некий метод просчета ситуации на карте, в котором происходит много итераций по разного рода циклам. Причём, граф объектов уже создан и изменяется лишь его состояние. То есть новых объектов фактически не создается… Но тем не менее профайлер показывал приблизительно такую картину (картинка из предыдущего топика):

image

И при частых вызовах метода сборка занимала довольно большую часть времени работы метода.

Проблема оказалась в for циклах. Как известно, компилятор преобразовывает for цикл для коллекций в следующий код:

for (Iterator<A> i = arrayListA.iterator(); i.hasNext();) {
    a = i.next();
}

//смотрим во внутрь метода iterator() ArrayList
public Iterator<E> iterator() {
    return new Itr();
}

//и сам класс итератора. поля которые потребляют память
private class Itr implements Iterator<E> {
	int cursor = 0;
	int lastRet = -1;
	int expectedModCount = modCount;
}

Все бы хорошо, но если у вас несколько вложенных циклов, при чем короткие циклы на самом нижнем уровне вложения, то получается, что просто один лишь проход по циклам создаст тысячи объектов, а если метод постоянно вызывается, то еще и постоянные срабатывания сборщика.
Например, для первого примера — в случае если arrayListA.size() == 1000, arrayListB.size() == 100, arrayListC.size() == 10 за один проход по циклам будет создано около 100 тыс. объектов итераторов…

Решение тут довольно очевидно — получать доступ по индексу:

for (int i = 0; i < arrayListA.size(); i++) {
    A a = arrayListA.get(i);
}


Такое вот маленькое изменение кода в данном конкретном случае позволило увеличить скорость работы горячего метода в 2 раза.
Стоит отметить, что такого рода оптимизация возможна в основном в случае ArrayList.

Будьте внимательны и с прошедшим Вас Новым годом!
Теги:
Хабы:
Всего голосов 92: ↑82 и ↓10+72
Комментарии99

Публикации

Ближайшие события