Pętla skanująca std::vector i jej optymalizacje

0

Witam.

Od jakiegoś czasu zastanawiam się nad czymś co może jest szczegółem, ale może mieć duże znaczenie.

typedef unsigned int UINT

std::vector<Object> objects;

I pętla:

for(UINT i = 0; i < objects.size(); i++)
{

}

II pętla:

for(UINT i = 0, iEnd = objects.size(); i < iEnd; i++)
{

}

Jak widać:

  • W I pętli co każdy krok jest wywoływana funkcja objects.size()
  • W II pętli co każdy krok jest odczytywana zmienna iEnd

Co tak naprawdę robi funkcja size() w vector? Dokonuje obliczeń w celu sprawdzenia rozmiaru vector'a czy po prostu pobiera prywatne pole obiektu?

I pętla jest zdecydowanie przejrzystsza, II wydaje się być szybsza.

Czy opłaca się tak kombinować jak w II pętli czy nie zyska się na tyle na szybkości żeby psuć przejrzystość kodu?

Pozdrawiam

0

vector<t>::size() jest inline tak wiec nie ma roznicy, czy uzywasz .size() czy nie. Najbardziej nieefektywnym krokiem w twoim rogramowanie jest skladowanie objektow. Generalnie powinienes kladowac shared_ptr<object>
Pozdrawiam serdecznie

0

Dzięki.

PS. Popieram twoje zdanie nad wyższością C++ przed C#, Java w temacie "Programista C++" : P

0

tak wiec nie ma roznicy, czy uzywasz .size() czy nie.
Pod warunkiem, że kompilujesz z włączoną optymalizacją. W buildach debugowych typowo wszystko jest wywoływane.

0

Swoją drogą dlaczego używanie std::vector miałoby być błędem do przechowywania obiektów?
Wg. mnie nie ma różnicy czy przechowuje się tam wskaźniki do obiektów czy całe obiekty, bo vector sam zaalokuje pamięć na nie

żeby nie było wątpliwości, std::vector<objects> używam jako "singleton", jest w jednym miejscu jako jedna kopia

0
Gokor napisał(a)

Swoją drogą dlaczego używanie std::vector miałoby być błędem do przechowywania obiektów?
Wg. mnie nie ma różnicy czy przechowuje się tam wskaźniki do obiektów czy całe obiekty, bo vector sam zaalokuje pamięć na nie

żeby nie było wątpliwości, std::vector<objects> używam jako "singleton", jest w jednym miejscu jako jedna kopia

Pomysl, jakie masz zyski z optymalizacji, jesli kopiujesz maly obiekt taki jak boost_shared_ptr<t> a T. Pamietaj, ze kazde wywolanie push_back wywojuje copy constructor i zysk jest widoczny nawet przy malych klasach.

Pozdrawiam serdecznie

0

a tak a propos to iterowanie mozna najszybciej tak zrobic:

std::vector<Object> objects;
for (std::vector<Object>::iterator i = objects.begin(); i!=objects.end(); ++i)
{
 // *i <-- aktualny obiekt
}

szybsze iterowanie od tego udalo mi sie osiagnac tylko jak zrobilem sobie prosta implementacje tablicy i zrobilem w zasadzie to co wyzej tylko bezposrednio na wskaznikach

0

@Krwq:

Niestety to twoje "szybsze" iterowanie jest ok. 10 razy wolniejsze od tego co użyłem w pierwszym poście :)

Swoją drogą, jest w ktoś stanie wyjaśnić następującą rzecz?

Pierwszy przykład:


class Position
{
   UINT16 x;
   UINT16 y;
   BYTE z;
};

std::vector<Position> vector;

// Inicjacja vector'a
for(int i = 0; i < 10000000; i++)
{
    vector.push_back(Position(0,0,0));
}

Czas wykonywania: 350 ms

for(int i = 0; i < vector.size(); i++)
{
    Position pos = vector[i];

    if(pos.x == 0 && pos.y == 0 && pos.z == 0)
        pos.x = 1;
}

Czas wykonywania: 3600 ms

for(std::vector<Position>::iterator i = vector.begin(); i != vector.end(); i++)
{
      Position pos = *i;

      if(pos.x == 0 && pos.y == 0 && pos.z == 0)
            pos.x = 0;
}

Wyżej został użyty std::vector<position>, teraz użyję std::vector<Position*>

Drugi przykład:


class Position
{
   UINT16 x;
   UINT16 y;
   BYTE z;
};

std::vector<Position*> vector;

// Inicjacja vector'a
for(int i = 0; i < 10000000; i++)
{
    vector.push_back(new Position(0,0,0));
}

Czas wykonywania: 14000 ms (??!!)

for(int i = 0; i < vector.size(); i++)
{
    Position* pos = vector[i];

    if(pos->x == 0 && pos->y == 0 && pos->z == 0)
        pos->x = 1;
}

Czas wykonywania: 17000 ms (??!!)

for(std::vector<Position*>::iterator i = vector.begin(); i != vector.end(); i++)
{
      Position* pos = *i;

      if(pos->x == 0 && pos->y == 0 && pos->z == 0)
            pos->x = 0;
}

Jak widać w pierwszym przykładzie został użyty czysty obiekt Position (5 bajtowy)
W drugim został użyty wskaźnik na ten obiekt (4 bajtowy)

Na logikę drugi przykład powinien być znacznie szybszy, a jest niesamowicie mniej wydajny.

Mógłby ktoś to wyjaśnić?

0

Czy jechales z tym w release czy debug?
Nie chce mi sie zbytnio w to wierzyc, poza tym to przy inkrementowaniu iteratorow powinienes uzywaz ++i a nie i++.

Pozdrawiam serdecznie

0

Tryb był ustawiony cały czas na Release i optymalizacje na Max speed.

Zmieniłem z it++ na ++it, obiekt dałem zamiast 5 bajtowego na kilka razy większy.

Wyniki są następujące:

2500 ms dla pętli z odwoływaniem się do vector'a za pomocą vector[i]
3600 ms dla pętli z iteratorem [begin(), end()]

I bardzo podobne czasy dla użycia vector'a z wskaźnikiem na obiekt zamiast bezpośrednio obiektu.

Tylko, że teraz obiekt zajmuje z kilkadziesiąt bajtów, a wskaźnik 4.
Więc jakim cudem podobne są czasy?

A co do iteratora to może po prostu jest wolniejszy od odwoływania się za pomocą [] ?

Pozdrawiam, mam nadzieje, że ktoś rozwiąże tę zagadkę.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0