Spróbuję swojej teorii na podstawie kodu z headerów VS2010. Podczas linijki m[i] = ...
następuje odwołanie do funkcji _Insert
z pliku xtree
:
iterator _Insert(const_iterator _Where,
_Nodeptr _Node)
{ // try to insert node at _Node using _Where as a hint
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Where._Getcont() != this)
_DEBUG_ERROR("map/set insert iterator outside range");
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */
const value_type& _Val = this->_Myval(_Node);
const_iterator _Next;
bool _Leftish = false; // assume nearest point is end of sequence
if (size() == 0)
return (_Insert(true, this->_Myhead, _Node)); // empty tree
else if (this->_Multi)
{ // insert even if duplicate
if (_Where == begin()) /*Pierwsze sprawdzenie: czy wstawić element na początku*/
{ // insert at beginning if before first element
if (!_DEBUG_LT_PRED(this->comp,
this->_Key(_Where._Mynode()), this->_Kfn(_Val)))
return (_Insert(true, _Where._Mynode(), _Node));
_Leftish = true; // nearest point is beginning of sequence
}
else if (_Where == end()) /*Drugie sprawdzenie: czy wstawić element na końcu*/
{ // insert at end if after last element
if (!_DEBUG_LT_PRED(this->comp,
this->_Kfn(_Val), this->_Key(_Rmost())))
return (_Insert(false, _Rmost(), _Node));
}
/* ..... */
}
else
{ // insert only if unique
if (_Where == begin()) /* Tak samo - pierwsze sprawdzenie, czy na początek */
{ // insert at beginning if before first element
if (_DEBUG_LT_PRED(this->comp,
this->_Kfn(_Val), this->_Key(_Where._Mynode())))
return (_Insert(true, _Where._Mynode(), _Node));
}
else if (_Where == end()) /* Drugie sprawdzenie, czy na koniec */
{ // insert at end if after last element
if (_DEBUG_LT_PRED(this->comp,
this->_Key(_Rmost()), this->_Kfn(_Val)))
return (_Insert(false, _Rmost(), _Node));
}
/* ... */
}
/* ... */
}
Rozpatruję dwa przypadki: kiedy nasza wartość i
wzrasta, kolejne elementy trafiają na koniec. Wtedy na pewno zostaną wywołane funkcje begin()
oraz end()
, a także dwa porównania iteratorów (_Where == begin() albo end()
). Weźmy program:
#include <map>
#include <cstdio>
#include <windows.h>
#define DECREMENT
int main() {
std::map<int, int> m;
for(int i = 50000; i >= 0; --i) m[i] = std::rand(); /* Wypełnianie */
int u = GetTickCount();
/*Iterator nocomp tworzę po to, by zasymulować porównanie*/
std::map<int, int>::const_iterator nocomp = m.begin(); nocomp++;
std::map<int, int>::iterator it;
for(int i = 0; i < 3000000; ++i){ /*Symulacja 3000000 wstawień*/
it = m.begin();
if(it == nocomp) printf("1");
#ifndef DECREMENT
it = m.end();
if(it == nocomp) printf("2");
#endif
}
printf("Czas: %ims\n", GetTickCount()-u);
}
Ponieważ zdefiniowaliśmy etykietę DECREMENT, linijki pomiędzy #ifndef
i #endif
nie wykonają się. Średni czas wykonania programu (3 powtórzenia) wynosi u mnie 3588ms. Zakomentowujemy linijkę #define DECREMENT
i teraz dwie wspomniane linijki wykonają się. Średni czas: 7030ms (taaaak! mniej więcej dwukrotnie więcej). Czas wzrósł o ok. 3,5s. Pozostało jeszcze kilka sekund różnicy. Być może właściwe wstawianie na koniec jest powolne, być może to błąd statystyczny :/
Nie wiem, czy się nie okaże, że to wszystko jest "do kitu", ale myślę, że jest OK.