итератор — обход контейнера C ++ — какой самый быстрый путь?

vector<int> my_vector(10);

// A
for (size_t i = 0; i < my_vector.size(); i++) {
// Do stuff with my_vector[i]
}

//B
for (vector<int>::iterator it = my_vector.begin(); it != my_vector.end(); it++) {
// Do stuff with (*it)
}

//C
for (vector<int>::const_iterator it = my_vector.begin(); it != my_vector.end(); it++) {
// Do stuff with (*it)
}

Пожалуйста, ответьте на следующие вопросы:

1) Если то, что я делаю внутри цикла, меняет значение элементов my_vector, то эффективнее ли использовать B над A? Здесь эффективность = более быстрый обход. Если да, то почему?

2) Если элементы my_vector не изменены, какой самый быстрый выход из трех?

-3

Решение

Современные компиляторы достаточно умны, чтобы оптимизировать все три одинаково. Вы всегда можете измерить их или посмотреть на сгенерированную сборку.

Одно из преимуществ универсального подхода итератора состоит в том, что вы можете использовать один и тот же код для наилучшей последовательной итерации вперед независимо от того, какой тип контейнера вы используете (например, вы можете оставить list или set вместо vector). Это может или не может быть полезным для вашей ситуации.

Если у вас есть проблемы с вашим приложением, не отвечающим вашим требованиям к производительности, и вы профилировали и конкретно определили это как узкое место, сначала рассмотрите альтернативные алгоритмы более высокого уровня, а затем сконцентрируйтесь на микрооптимизации этого места.

Более важный вопрос: какой из этих трех производит самый понятный, самый читаемый и самый обслуживаемый код?

2

Другие решения

С точки зрения набора текста, ни один из вышеперечисленных не является ответом:

std::for_each(v.begin(), v.end(), func);

куда func это либо функтор, либо автономная функция, либо лямбда-функция.

С точки зрения кода, сгенерированного компилятором — 95% времени, не будет (большая часть) разницы между любым из 3 вариантов. Небольшое исключение — индексированная версия — в неоптимизированных сборках это может вызвать v.size() каждая итерация. Однако при включенной оптимизации все они должны быть в основном идентичны. С точки зрения определения производительности, профилирование и тестирование являются ключевыми. Вы должен найдите свои настоящие узкие места, прежде чем вы сможете подумать о том, чтобы попытаться что-то оптимизировать. Без профилирования приложения вы, вероятно, будете оптимизировать то, что либо редко вызывается, либо не является вашим узким местом.

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

1

По вопросам рекламы [email protected]