Подтвердить что ты не робот

Бывают ли константы?

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

Это действительно правильно? Если да, что действительно происходит внутри, что делает const_iterator быстрее?.

EDIT: я написал небольшой тест, чтобы проверить const_iterator vs iterator и нашел разные результаты:

Для итерации 10 000 объектов const_terator было меньше нескольких миллисекунд (около 16 мс). Но не всегда. Были итерации, в которых оба были равны.

4b9b3361

Ответ 1

Если ничего больше, const_iterator читает лучше, так как он говорит кому-нибудь, читающему код "Я просто перебираю этот контейнер, а не возиться с содержащимися объектами".

Это большая победа, не говоря уже о различиях в производительности.

Ответ 2

Мы используем следующие рекомендации:

Всегда предпочитайте const over non-const

Если вы склонны использовать объект const, вы привыкли использовать только постоянные операции над объектами, которые вы получаете, и это как можно больше использовать const_iterator.

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

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

Ответ 3

Они предназначены для нетривиальных контейнеров/итераторов. Получите ваши привычки прямо, и вы не потеряете производительность, когда это имеет значение.

Кроме того, есть несколько причин, чтобы предпочесть const_iterator, независимо от того, что:

  • Использование const выражает намерение кода (т.е. только чтение, без мутации этих объектов).
  • Использование константы (_iterator) предотвращает случайную модификацию данных. (см. выше)
  • В некоторых библиотеках используется параметр недостаток-const begin(), чтобы помечать данные как грязные (например, OpenSG), и отправляет их в другие потоки/по сети при синхронизации, поэтому они имеют реальные последствия для производительности.
  • Кроме того, для доступа к не-const-членам функции могут иметь побочные эффекты, которые вы не намерены (почти так же, как описано выше), например, отключение контейнеров для копирования на запись из общих данных. Qt для одного, делает именно это.

В качестве примера последнего пункта выше, здесь выдержка из qmap.h в Qt:

inline iterator begin() { detach(); return iterator(e->forward[0]); }
inline const_iterator begin() const { return const_iterator(e->forward[0]); }

Даже если итератор и const_iterator практически эквивалентны (кроме const), detach() создает новую копию данных, если на ней есть два или более объекта. Это совершенно бесполезно, если вы просто собираетесь читать данные, которые вы указываете, используя const_iterator.

Конечно, есть точки данных в другом направлении:

  • Для контейнеров STL и многих контейнеров с простой копией-семантикой для производительности это не имеет значения. Код эквивалентен. Тем не менее, возможность писать чистый код и избегать ошибок выигрывает.
  • Const является вирусным, поэтому, если вы работаете в устаревшей кодовой базе, где const работает плохо (или просто нет), вам может понадобиться работать с неконстантными итераторами.
  • По-видимому, некоторые pre С++ 0x STL имеют ошибку, в которой const_iterators не могут использоваться для удаления() элементов из контейнеров.

Ответ 4

Я не понимаю, почему они были бы - constness - это проверка времени компиляции. Но очевидный ответ - написать тест.

Изменить: Вот мой тест - он дает одинаковые тайминги на моей машине:

#include <vector>
#include <iostream>
#include <ctime>
using namespace std;;


int main() {
    vector <int> v;
    const int BIG = 10000000;
    for ( int i = 0; i < BIG; i++ ) {
        v.push_back( i );
    }
    cout << "begin\n";
    int n = 0;
    time_t now = time(0);
    for ( int a = 0; a < 10; a++ ) {
        for( vector <int>::iterator it = v.begin(); it != v.end(); ++it ) {
            n += *it;
        }
    }
    cout << time(0) - now << "\n";
    now = time(0);
    for ( int a = 0; a < 10; a++ ) {
        for( vector <int>::const_iterator cit = v.begin(); cit != v.end(); ++cit ) {
            n += *cit;
        }
    }
    cout << time(0) - now << "\n";;

    return n != 0;

}

Ответ 5

Наши рекомендации по кодированию говорят, предпочитают const_iterator

Посмотрите на эту статью статьи Скотта Мейерса здесь. Он объясняет, почему следует использовать итератор над const_iterator.

Ответ 6

Это зависит от используемого вами контейнера и реализации.

Да, const_iterator может работать лучше.

Для некоторых контейнеров реализация константных итераторов и изменяемых итераторов может отличаться. Первым примером, о котором я могу думать, является контейнер SGI STL. Уменяемый итератор имеет дополнительный указатель на родительскую веревку, чтобы поддерживать обновления. Это подразумевает дополнительные ресурсы, потраченные впустую для обновлений подсчета ссылок + память для указателя на родительскую веревку. См. примечания по реализации здесь.

Однако, как говорили другие, компилятор не может использовать const самостоятельно для оптимизации. const просто предоставляет доступ только для чтения к указанному объекту, а не говорит, что он неизменен. Для контейнера типа std::vector, итераторы которого обычно реализуются как простые указатели, не будет никакой разницы.

Ответ 7

Они должны быть одинаковыми, поскольку константа - это проверка времени компиляции.

Чтобы доказать себе, не было никаких причуд, я взял код anon, изменил его, чтобы использовать clock_gettime, добавил внешний цикл, чтобы избежать пристрастий к кешированию, и запускал его много раз. Результаты были на удивление несогласованными - вверх и вниз на 20% (без свободных ящиков) - но минимальное время для iterator и const_iterator было практически идентичным.

Затем я получил свой компилятор (GCC 4.5.2-O3) для создания сборки и визуально сравнил две петли: идентичный (за исключением того, что порядок пары регистровые нагрузки были отменены)

iterator loop

    call    clock_gettime
    movl    56(%esp), %esi
    movl    $10, %ecx
    movl    60(%esp), %edx
    .p2align 4,,7
    .p2align 3
.L35:
    cmpl    %esi, %edx
    je  .L33
    movl    %esi, %eax    .p2align 4,,7
    .p2align 3
.L34:
    addl    (%eax), %ebx
    addl    $4, %eax
    cmpl    %eax, %edx
    jne .L34
.L33:
    subl    $1, %ecx
    jne .L35
    leal    68(%esp), %edx
    movl    %edx, 4(%esp)
    leal    56(%esp), %esi
    movl    $1, (%esp)
Цикл

const_iterator:

    movl    60(%esp), %edx
    movl    $10, %ecx
    movl    56(%esp), %esi
    .p2align 4,,7
    .p2align 3
.L38:
    cmpl    %esi, %edx
    je  .L36
    movl    %esi, %eax
    .p2align 4,,7
    .p2align 3
.L37:
    addl    (%eax), %ebx
    addl    $4, %eax
    cmpl    %eax, %edx
    jne .L37
.L36:
    subl    $1, %ecx
    jne .L38
    leal    68(%esp), %edx
    movl    %edx, 4(%esp)
    leal    56(%esp), %esi
    movl    $1, (%esp)

Ответ 8

когда вы сравниваете все это, убедитесь, что используете соответствующий уровень оптимизации - вы получите дико отличающиеся тайминги с использованием "-O0" по сравнению с "-O" и т.д.

Ответ 9

container<T>::const_iterator::operator* возвращает const T& вместо T&, поэтому компилятор может выполнять обычные оптимизации для объектов const.

Ответ 10

"Const-ness", как ограничение доступа (public, protected, private), приносит пользу программисту больше, чем помогает с оптимизацией.
Компиляторы не могут фактически оптимизировать для стольких ситуаций, связанных с const, как можно подумать, по многим причинам (например, const_cast, изменяемые члены данных, сглаживание указателя/ссылки). Однако наиболее важной причиной этого является то, что только потому, что const_iterator не позволяет изменять данные, на которые он ссылается, не означает, что эти данные не могут быть изменены другими способами. И если компилятор не может определить, что данные доступны только для чтения, то он не может действительно оптимизировать намного больше, чем для случая неконстантного итератора.
Более подробную информацию и примеры можно найти по адресу: http://www.gotw.ca/gotw/081.htm a >

Ответ 11

По моему опыту, при использовании константных итераторов компилятор не делает никакой измеримой оптимизации. Хотя утверждение "оно может" истинно, и ссылки не определены как указатели в стандарте.

Однако вы можете иметь две ссылки на один и тот же объект, поэтому можно быть const, один не const. Затем я думаю, что ответы в этот поток на ограничивающих указателях применяются: компилятор не может знать, изменен ли объект другим потоком, например, или некоторым кодом обработки прерываний.