LINUX.ORG.RU

Ranged for vs Indexed for

 


1

5

Что в должно быть быстрее на arm для контейнера std::vector<T>?

Ranged for:

std::vector<SomeStruct> vec;
for (const auto& v : vec)
{
  // do something with v
}


Indexed for:
std::vector<SomeStruct> vec;
for (size_t i = 0, size = vec.size(); i < size; i++)
{
  const auto& v = vec[i];
  // do something with v
}


Мои синтетические замеры дают приблизительно одинаковые результаты. Профилирование под xcode показывает странные результаты - indexed for получается иногда сильно быстрее, чем range for.
Самостоятельно выводы сделать не получается, надеюсь на коллективный разум ЛОРа.

★★★★★

AFAIK, в некоторых версиях STL различаются реализации итераторов для vector в зависимости от режима сборки. В release-режиме итератор — это просто T*, в debug-сборке итератором будет полноценный класс с дополнительными накладными расходами.

Соответственно, в release разницы в скорости между двумя for-ами не будет. А в debug-е она будет более чем заметна.

Может здесь такой же фокус происходит?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Женя, спасибо. Но речь исключительно о релизной сборке.

У меня такие мысли:
Доступ к элементу вектора происходит по-разному. В одном случае вычисляется адрес, в другом случае адрес уже есть в итераторе. Так же по-разному происходит проверка завершения цикла и инкремент.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

Ну тут еще большой вопрос: как происходит профилирование. В релизную сборку добавляется какая-то информация о символах из программы, какое-то инструментирование кода происходит?

eao197 ★★★★★
()
Ответ на: комментарий от sostupid

for (size_t i = 0; i < vec.size(); i++)

Ну и что произойдет в этом случае, если размер вектора внутри цикла будет изменен (к примеру vec.pop_back();)?

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

Я так понимаю, у тебя один и тот же код внутри range-based и обычного цикла.

Раз так, то никаких push_back там быть не может.

sostupid
()
Ответ на: комментарий от eao197

Эпол заявляет, что оверхед минимальный. Замеры с помощью ::gettimeofday(struct, 0); дают сходный (читать, бесполезный для меня) результат на релизной сборке.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

Ну и что произойдет в этом случае, если размер вектора внутри цикла будет изменен

Лол :-) Вообще-то, по правилам хорошего тона, цикл for применяется там, где число итераций заведомо известно :-) Если это не так, то нужен цикл while :-) Хотя откуда тебе знать про олд-скул :-) Лол :-)

anonymous
()
Ответ на: комментарий от sostupid

Раз так, то никаких push_back там быть не может.

Все верно. Но чем же мешает предварительное получение размера контейнера?

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

Но чем же мешает предварительное получение размера контейнера?

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

sostupid
()
Ответ на: комментарий от anonymous

Хотя откуда тебе знать про олд-скул :-) Лол :-)

Действительно, откуда мне об этом знать? На ЛОРе только вы один специалист. Правда по теме ни разу ничего путного вам сказать не удалось.

Лол :-) Вообще-то, по правилам хорошего тона, цикл for применяется там, где число итераций заведомо известно :-)
Если это не так, то нужен цикл while :-)

Вы не только лалка, но еще и клоун.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от sostupid

Зачем вручную выполнять его работу?

Затем, что бывают случаи, когда размер контейнера меняется внутри цикла:

for (size_t i = 0, size = vec.size(); i < size; i++)
{
  size_t idx = size - i - 1;
  vec[idx] = vec[--size];
  vec.pop_pack();
}

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

Погоди, я что-то не пойму. Если ты меряешь сам, без профайлера, то у тебя скорости обоих циклов одинаковые? А если с профайлером, то сильно разные.

Или у тебя на своих тестах так же получаются результаты, которые похожи на результаты профайлера?

eao197 ★★★★★
()
Ответ на: комментарий от andreyu

На ЛОРе только вы один специалист.

Не один :-) Но у меня нет проблем ни с range for, ни с красотой кода, ни с IDE :-) Лол :-)

Правда по теме ни разу ничего путного вам сказать не удалось.

Зато у меня for работает быстро :-)

Вы не только лалка, но еще и клоун.

А что такое лалка? :-)

anonymous
()

Посмотрел ассемблерный листнинг gcc с -O2. Два варианта различаются только тем, что во втором случае проверка конца цикла приводит к вызову оператора сравнения итераторов (в персом случае тупо сравнение регистров).

anonymous
()
Ответ на: комментарий от anonymous

проверка конца цикла приводит к вызову оператора сравнения итераторов

Хвалёное встраивание цепепе? :-) Zero-cost abstractions говорили они :-)

anonymous
()
Ответ на: комментарий от andreyu

Ну и что произойдет в этом случае, если размер вектора внутри цикла будет изменен (к примеру vec.pop_back();)?

vec.size() вернёт на единицу меньшее значение, очевидно.

sostupid
()
Ответ на: комментарий от eao197

Если ты меряешь сам, без профайлера, то у тебя скорости обоих циклов одинаковые? А если с профайлером, то сильно разные.

Как я сказал в топике - «иногда». По этой причине и не могу сделать однозначных выводов. В синтетике получается в среднем одинаково.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Не один :-) Но у меня нет проблем ни с range for,

У меня с ними проблем тоже нет. Я просто хочу знать, что будет быстрее.

ни с красотой кода, ни с IDE :-) Лол :-)

Держите меня в курсе. Особенно не забывайте сообщать о своих вкусах в топиках о c++.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Зато у меня for работает быстро :-)

Что, все 10 итераций происходят быстро? Неужели быстрее, чем у остальных?

andreyu ★★★★★
() автор топика
Ответ на: комментарий от sostupid

vec.size() вернёт на единицу меньшее значение, очевидно.

Ах да :-) Это больше для тебя, чем для andreyu :-) -- Вообще-то, по правилам хорошего тона, цикл for применяется там, где число итераций заведомо известно :-) Если это не так, то нужен цикл while :-)

anonymous
()
Ответ на: комментарий от anonymous

Два варианта различаются только тем, что во втором случае проверка конца цикла приводит к вызову оператора сравнения итераторов

Да, профайлер указывает на это.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

Я просто хочу знать, что будет быстрее.

Лол :-) А можно полюбопытствовать, зачем это знать? :-)

Держите меня в курсе. Особенно не забывайте сообщать о своих вкусах в топиках о c++.

Не серчай уж так :-) Просто держи в курсе о том, что же быстрее, range for или for по индексам :-)

anonymous
()
Ответ на: комментарий от sostupid

vec.size() вернёт на единицу меньшее значение, очевидно.

Значит вызов std::vector.size() происходит каждую итерацию. Что вносит дополнительные накладные расходы.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от anonymous

А можно полюбопытствовать, зачем это знать? :-)

Да, для этого - https://apps.ugolnik.info/wormaria/

Просто держи в курсе о том, что же быстрее, range for или for по индексам :-)

Как вы думаете, почему я создал этот топик? Или это слишком сложно для вашего понимания?

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

А я и не сказал, какой код медленнее. Но предполагаю, что второй, просто потому что он делает больше действий (инкрементирует счётчик, который ещё и на стек влияет скорее всего). Так то надо мерять. range-for в теории может быть быстрее как раз потому что для компилятора это -1 оптимизация для приближения к идеалу для случая, когда индекс не нужен.

xaizek ★★★★★
()
Последнее исправление: xaizek (всего исправлений: 1)

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

this

Профилирование под xcode показывает странные результаты - indexed for получается иногда сильно быстрее, чем range for.

вопрос к авторам xcode

MyTrooName ★★★★★
()
Ответ на: комментарий от MyTrooName

сишный for - это и есть while.

Нет :-) for - это частный случай while, удобный для применения когда число итераций заведомо известно :-)

anonymous
()
Ответ на: комментарий от anonymous

for - это частный случай while, удобный для применения когда число итераций заведомо известно :-)

в нормальных языках да. в С for - это while

MyTrooName ★★★★★
()
Ответ на: комментарий от xaizek

Так беда в том, что мои измерения не помогли мне сделать правильный вывод. Я всегда считал, что range for, в теории, должен быть быстрее indexed for.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от MyTrooName

в нормальных языках да. в С for - это while

В C for - это for :-) За пруфом читать K&R, глава 3.5 :-) Там же можно обнаружить рекомендации когда следует предпочесть for - когда есть простая инициализация и инкрементирование переменных цикла :-) Т.е. когда число итераций заведомо известно :-)

Ты просто хочешь мне доказать, что в C не нужен for, потому что есть while :-) Я же тебе говорю о хорошем стиле :-)

anonymous
()
Ответ на: комментарий от andreyu

Да, для этого - https://apps.ugolnik.info/wormaria/

Какое отношение к этому имеет скорость for в цепепе? :-)

Как вы думаете, почему я создал этот топик? Или это слишком сложно для вашего понимания?

Я думаю вы создали этот топик, потому что для вас нет ничего более интересного, чем измерение скорости range for и for по индексам в цепепе :-)

anonymous
()
Ответ на: комментарий от MyTrooName

вопрос к авторам xcode

К ним вообще много вопросов. Xcode очень глюкав и падуч. Пару билдов назад инструментарий вообще не работал.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

Я потом отредактировал:

range-for в теории может быть быстрее как раз потому что для компилятора это -1 оптимизация для приближения к идеалу для случая, когда индекс не нужен.

С парой указателей компилятору должно быть проще работать чем с вызовом метода, this, полями объекта, индексированием. В моём примере видно, что компилятор не стал выкидывать индекс, потому что объект который пришёл в функцию f2() может скрытно измениться в функции g() и указатель на начало массива перечитывается на каждой итерации из памяти.

Здесь я бы сказал, что код медленнее.

xaizek ★★★★★
()
Последнее исправление: xaizek (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Какой замечательный выстрел в ногу.

Вы о чем? Там итерация с конца идёт. Элемент получается каждую итерацию.

andreyu ★★★★★
() автор топика
Ответ на: комментарий от andreyu

Вы о чем? Там итерация с конца идёт. Элемент получается каждую итерацию.

О том, что так код писать не надо.

i-rinat ★★★★★
()
Ответ на: комментарий от andreyu

И это говорит персонаж, цель которого пустозвонство.

Сейчас целью было донести до тебя, что ты, скорее всего, напрасно тратишь время :-) Насчёт незнания про олдскул - ну меня давно учили, что for не применяется в случаях, когда массив меняется, поэтому мой олдскул - он такой :-) Так что ты зря начал обзываться :-) Ведь можно нормально вести дискуссию, просто сообщив, что про олдскул ты в курсе :-) И всё, без обзываний :-)

anonymous
()
Ответ на: комментарий от xaizek

КМК, это всё чёртов std::string виноват. У него наверняка внутри char*, а для оптимизатора это красный свет, компилятор начинает параноидально перечитывать всё что может, т.к. это могло алиаситься через char*.

sostupid
()
Ответ на: комментарий от sostupid

std::string виноват. У него наверняка внутри char*, а для оптимизатора это красный свет

Ага :-) А то же ведь оптимизатор пишется отдельно от стандартной библиотеке, и его нельзя научить правильно работать со структурами данных, которые описаны в стандарте :-) Лол :-)

anonymous
()
Ответ на: комментарий от sostupid

std::string конечно усложняет, но с range-for + string нормально же всё соптимизировалось. Т.е. в общем случае range-for может быть выгоднее, если компилятору жизнь упрощает.

xaizek ★★★★★
()
Последнее исправление: xaizek (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.