итератор против reverse_iterator

Я использую std::map для хранения большого количества элементов (пар элементов), и у меня есть «немного» сомнения. Что эффективнее перебирать все элементы по моим std::map, iterator или reverse_iterator?


person Miguel Angel    schedule 20.05.2009    source источник


Ответы (6)


Это действительно имеет значение? это типы микрооптимизаций, которые вы должны стараться избегать ИМХО. Кроме того, даже если время итерации изменяется для очень большого количества элементов карты, тот факт, что вы пытаетесь перебрать все элементы такой большой карты, означает, что, скорее всего, вы выбрали неправильную структуру данных.

person Naveen    schedule 20.05.2009
comment
Что ж, ты прав. Я хотел напечатать все элементы с оператором ‹‹ в свой класс, но если напечатать все std::map, то это займет слишком много времени. лучше не делай этого. Спасибо - person Miguel Angel; 20.05.2009

Для справки: разыменование reverse_iterator в контейнерах std::map и std::set вдвое медленнее, чем при использовании iterator -- как с -O3 gcc 3.4.6, так и с MSVC на процессорах Intel/AMD ( почти в 3 раза медленнее на архитектурах PPC.) То же самое верно для const_reverse_iterator по сравнению с const_iterator. Это связано с тем, что reverse_iterator на самом деле указывает на узел дерева, следующий сразу за узлом дерева, который нужно разыменовать, поэтому требуется дополнительная работа. Итераторы std::vector демонстрируют гораздо более мягкую разницу (reverse_iterator всего на ~30% медленнее на PPC, практически неразличима на Intel/AMD). Между прочим, итератор std::vector примерно в 20 раз быстрее, чем итератор std::map или std::set.

#include <set>
#include <vector>
#include <stdio.h>
#ifdef _WIN32
#include <sys/timeb.h>
#else
#include <sys/time.h>
#endif
#include <time.h>

#define CONTAINER std::set< int >

double
mygettime(void) {
# ifdef _WIN32
  struct _timeb tb;
  _ftime(&tb);
  return (double)tb.time + (0.001 * (double)tb.millitm);
# else
  struct timeval tv;
  if(gettimeofday(&tv, 0) < 0) {
    perror("oops");
  }
  return (double)tv.tv_sec + (0.000001 * (double)tv.tv_usec);
# endif
}


int main() {
  int i, x = 0;
  CONTAINER bla;
  for (i = 0; i < 10000; bla.insert(bla.end(), i++)) ;

  double t1 = mygettime();

  for (i = 0; i < 100; ++i) {
    for (CONTAINER::iterator it = bla.begin(); it != bla.end(); ++it) {
      x ^= *it;
    }
  }

  printf("forward: %f\n", mygettime() - t1);

  double t2 = mygettime();

  for (i = 0; i < 100; ++i) {
    for (CONTAINER::reverse_iterator it = bla.rbegin(); it != bla.rend(); ++it) {
      x ^= *it;
    }
  }

  printf("reverse: %f\n", mygettime() - t2);

  return 0;
}
person vladr    schedule 31.03.2010
comment
Проголосовал за ответ на заданный вопрос. Некоторые микрооптимизации настолько очевидны, что их невыполнение следует считать плохой практикой программирования/ошибкой... Но я работаю в критически важной области производительности; если бы это зависело от меня, C++ был бы переименован в ++C. - person fredbaba; 05.06.2013
comment
Этот ответ лучше, чем приведенный выше. Почему он не был выбран как правильный? Более того, мне интересно, как реализовать обратный итератор в rb-дереве? - person chain ro; 25.11.2014

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

«Преждевременная оптимизация — корень всех зол». - Дональд Кнут

person Paul Sonier    schedule 20.05.2009

Разницы скорее всего не будет. std::reverse_iterator — это просто оболочка шаблона, которая переводит ++ в -- во время компиляции.

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

person Drew Hall    schedule 20.05.2009
comment
Неправда, это не просто шаблонная прокладка. FYI set/map reverse_iterators в 2-3 раза медленнее, чем прямые итераторы. - person vladr; 31.03.2010

Я думаю, что нет смысла использовать reverse_iterator; это противоречит интуиции.

Это затруднит понимание кода, кто-то посмотрит на код и скажет "wtf"; и если вам нужно поставить комментарий, чтобы объяснить, почему, это, вероятно, означает, что это не очень хороший код для начала.

person Max    schedule 20.05.2009

Меня интересует необходимость повторения. Карта содержит пары ключ-значение, и обычно вы используете ее для поиска. Если по каким-то причинам вам по-прежнему необходимо повторить карту (может быть, удаление указателей, содержащихся внутри карты и т. д.), вы можете использовать std::for_each. Забудьте о микрооптимизациях и попробуйте повысить читаемость кода.

person aJ.    schedule 20.05.2009
comment
Во-первых, я вас не минусовал, но std::map спроектирован так, чтобы его можно было повторять, и если вы используете только пары ключ-значение, вы, вероятно, в любом случае использовали бы хэш-карту, такую ​​как std::unordered_map. Совершенно нормально использовать std::map, если вам нужна ключ-значение И упорядоченная итерация. - person smerlin; 25.07.2011