optymalizacja c++

0

hej

czy zmienna z bedzie tworzona za kazdym przejsciem petli od nowa czy kompilatory to optymalizuja i bedzie tworzona tylko raz?

while (true)
{
  int z ;
}
0

Dobre pytanie.

Zadanie: niech jakis assemblerowiec sprawdzi jak to wygląda (ja wypadłem dawno z asmowego tematu)

0

tylko jeden raz.
pzdr

0

kompilator zaklada, ze wiesz co robisz i za kazda iteracja od nowa tworzy ta zmienna. i o ile dla typow podstawowych jako taka optymalizacja jest mozliwa (choc nie wiem jak to traktuja kompilatory) to dla klas juz sie tak nie da.

0
vixen03 napisał(a)

[...] kazda iteracja od nowa tworzy ta zmienna

Tego nie byłbym taki pewny ;) Przykład (BCB/VC6):

for(int i=0;i<100;i++)
{
  int x;
  x+=1;
}

zmienna

x

przy każdej iteracji leży pod tym samym adresem, a jej wartość nie jest "kasowana" - czyli na koniec ostatniej iteracji zmienna x

 będzie powiększona o 100. Pomijając kwestię zasięgu zmiennych można to potraktować jak:

```cpp
int x;

for(int i=0;i<100;i++)
{
  x+=1;
}
0
0x666 napisał(a)
vixen03 napisał(a)

[...] kazda iteracja od nowa tworzy ta zmienna

Tego nie byłbym taki pewny ;) Przykład (BCB/VC6):

for(int i=0;i<100;i++)
{
  int x;
  x+=1;
}

zmienna

x

przy każdej iteracji leży pod tym samym adresem, a jej wartość nie jest "kasowana" - czyli na koniec ostatniej iteracji zmienna x

 będzie powiększona o 100. Pomijając kwestię zasięgu zmiennych można to potraktować jak:
> 
> 
```cpp
int x;

for(int i=0;i<100;i++)
{
  x+=1;
}

wydaje mi sie ze tu zaszla troche inna sytuacja... przy pierwszym przebiegu petli tworzona jest zmienna (zauzmy ze ma adres 'adres') inkrementujemy ja i mamy pod adresem 'adres' wartosc 1, zmienna jest kasowana ale pamiec nie jest czyszczona. Drugi przebieg petli, znowu jest tworzona zmienna x tyle ze pod tym samym adresem pod ktorym znajduje sie juz wartosc 1 itd. Wydaje mi sie ze zachodzi wlasnie taka sytuacja gdyz podczas wykonywania tej petli nie jest rezerwowana zadna inna pamiec...

aha i sprawdzilem w VC6 to, jest dokladnie tak jak mowisz tyle ze zmienna x przy tworzeniu nie zawiera wartosci 0 tylko przypadkowa liczbe znajdujaca sie akurat pod tym adresem w pamieci, tak wiec po stu przebiegach petli nie bedzie tam wartosci 100 tylko o 100 wieksza niz na poczatku

i jeszcze jedna rzecz, taki zapisz juz nie daje takiego samego wyniku

		for(i=0;i<10;i++)
		{
			int * x = new int;
			*x++;
		}

tylko nie wiem czy to ma cos z tym wspolnego ale chyba odbywa sie to podobnie jak w pierwszym przypadku :P

0

nie wiem jak to jest dal intow, nie chce mi sie odpalac cbuildera :P

ale zrob sobie takie cos:
<cpp>
class klasa {
public:
klasa() {
cout << "tworzenia klasy";
}
}

for(int i = 0; i < 10; i++)
klasa k;

0

przy pierwszym przebiegu petli tworzona jest zmienna (zauzmy ze ma adres 'adres') inkrementujemy ja i mamy pod adresem 'adres' wartosc 1, zmienna jest kasowana ale pamiec nie jest czyszczona. Drugi przebieg petli, znowu jest tworzona zmienna x tyle ze pod tym samym adresem pod ktorym znajduje sie juz wartosc 1 itd.

Mylisz się ;) Ta zmienna jest "tworzona" podczas wejścia w funkcję, w której się znajduje - przydzielana jest odpowiednia ilość miejsca na stosie. Od strony assemblera zmienna

x

, nawet przed wejściem w pętle, po prostu już jest. Mimo wszystko poprawny zapis powinien wyglądać tak:

for(int i=0,x=0;i<100;i++)
{
  x+=1;
}

ha i sprawdzilem w VC6 to, jest dokladnie tak jak mowisz tyle ze zmienna x przy tworzeniu nie zawiera wartosci 0 tylko przypadkowa liczbe znajdujaca sie akurat pod tym adresem w pamieci, tak wiec po stu przebiegach petli nie bedzie tam wartosci 100 tylko o 100 wieksza niz na poczatku

W rzeczy samej i tak też napisałem ;)

i jeszcze jedna rzecz, taki zapisz juz nie daje takiego samego wyniku
for(i=0;i<10;i++)
{
int * x = new int;
*x++;
}
tylko nie wiem czy to ma cos z tym wspolnego ale chyba odbywa sie to podobnie jak w pierwszym przypadku

Podobnie tyle, że zmienna

x

jest inicjowana przy każdej iteracji i po wyjściu z pętli 40 bajtów będzie niepotrzebnie zalegać w pamięci :P.

0

Wszystko zalezy od kompilatora. W g++ na 100% zmienna ta jest tworzona za kazdym razem, Przeciez jezeli umiesciles jej deklaracje wewnatrz petli, to tego chciales. I jest to bardzo dobre podejscie, poniewaz mogles umiescic jakies obliczenia w konstruktorze.

0
vixen03 napisał(a)

ale zrob sobie takie cos:

class klasa {
public:
klasa() {cout << "tworzenia klasy";}
}

for(int i = 0; i < 10; i++)
klasa k;

A to spróbuj tego :D

class klasa {
private:
int x;
public:
klasa() {std::cout << "tworzenia klasy x="<<x<<std::endl;}
~klasa() {x++;std::cout << "usuwanie klasy"<<std::endl;}
};


int main(int argc, char* argv[])
{
 for(int i = 0; i < 10; i++)
   klasa k;
}

Zauważ, że mowa jest o typach wbudowanych (

int

).

greco napisał(a)

Wszystko zalezy od kompilatora. W g++ na 100% zmienna ta jest tworzona za kazdym razem.

Otóż to!!! Między innymi dlatego warto trzymać się zasad języka C/C++ i nie wariować tak jak ja to zrobiłem w pętli z pierwszego mojego postu. Tylko pytanie brzmi: co to znaczy, że zmienna jest tworzona i jaki wpływ ma to tworzenie na wydajność??? Chyba w pewien sposób odpowiedziałem na te pytania ;)

0

Jest tak jak napisał vixen03, ale to jest bardziej skomplikowane.
Miejsce na zmienną automatyczną przydziela kompilator na początku funkcji, natomiast przy deklaracji może(!) nastąpić wywołanie konstruktora, a przy końcu bloku - wywołanie destruktora.
Oczywiście int() jest pusty :)
Dodatkowo kompilator potrafi powtórnie użyć miejsca na inny obiekt danego typu, jeśli te dwa obiekty nie istnieją w tym samym czasie.

Natomiast jest jeszcze jedna rzecz: inicjalizacja. W bloku ZAWSZE, w każdej iteracji, jest inicjalizowany obiekt automatyczny [C++ 6.7.2], np.:

for (...)
{
    int i = 5;
}

A static POD(?) tylko raz [6.7.4].

0

Sprawdziłem na kompilatorze Watcomu przy agresywnej optymalizacji:

int main(void)
{
    while(1)
    {
        long int a;
        a = 1;
    }
    long int b = 3;
}

najpierw powinna zostać utworzona zmienna a, a potem b a więc po zakończeniu while zmienna b powinna zajmować miejsce a. Ale jest inaczej:
adres a: -04[bp]
adres b: -08[bp]
Wynika z tego, że wszystkie zmienne w funkcji mają ten sam zakres a miejsce na stosie jest robione przy wejściu do funkcji i rzeczywiście tak jest:

mov bp,sp
sub sp, 08

Pętla while wygląda tak:

mov word ptr -08[bp],01
mov word ptr -06[bp],0
jmp 0318

Zmienna więc nie jest tworzona przy każdym wejściu do pętli. Prawdopodobnie jednak, każdy inny kompilator bedzie stosował odmienną taktykę optymalizacji więc lepiej przyjąć, że zmienna jest fizycznie tworzona i usuwana każdorazowo i nie stosować takich trików ;P.

0

no wlasnie lepiej nie stosowac takich trikow i przybrac rozwiazanie najlepiej do wybranej sytuacji... wlasciwie chyba i tak wiedza o tym do niczego sie nie przyda... bo zawsze mozna utworzyc wskaznik przed petla i go inkrementowac i wykorzystanie pamieci bedzie minimalne

0
Heimdall napisał(a)

Sprawdziłem na kompilatorze Watcomu przy agresywnej optymalizacji:
(...)

To ma być optymalizacja?! :D
To JEST optymalizacja [gcc 3.4.2 -Os -fomit-frame-pointer]:

_main:
	call	___main
L2:
	jmp	L2 

Nieskończona pętla... i nic więcej.

Natomiast jeśli chodzi o kompilację czegoś takiego:

#include <stdio.h>
#include <time.h>
int main(void)
{
    int a,b;
    while(a>1)
    {
        int b1 = a+66;
        printf("%d",b1);
    }
    
    while(b>1)
    {
        int b2 = b+66;
        printf("%d",b2);
    }
}

To b1 i b2 mogą być w tym samym miejscu.

Albo:

#include <stdio.h>
#include <time.h>
int main(void)
{
    int a;
    while(a>1)
    {
        int b1 = a+66;
        printf("%d",b1);
        a++;
    }
    
    int b;
    while(b>1)
    {
        int b1 = b+66;
        printf("%d",b1);
        b++;
    }
// mogę tu chcieć b=a;
}

Mamy: a,b są w tym samym bloku, więc NIE MOGĄ zostać połączone.

I kolejny, a'la Heimdall:

#include <stdio.h>
#include <time.h>
int a;
int main(void)
{
    while(a>1)
    {
        int b1 = a+66; // [ebp-4]
        printf("%d",b1);
        a++;
    }

    int b; // b dostaje miejsce b1 [ebp-4]
    while(b>1)
    {
        int b1 = b+66; // to b1 jest juz w innym miejscu [ebp-8]
        printf("%d",b1);
        b++;
    }
}

A jeszcze jak się dorzuci optymalizacje do tego to w ogóle może kompilator zrobić cuda :)

W C++ jest tak:

  • przy wejściu w blok zmienne automatyczne są tworzone, a raczej uzyskane jest dla nich miejsce + konstrukcja
  • przy wyjściu są niszczone, ich miejsce może zostać wykorzystane lub zwolnione (+destruktor)

Implementacja kompilatora dla x86 używa stosu procesora i dlatego przy wejściu w funkcję mamy już miejsce dla wszystkich zmiennych lokalnych (auto + temporary!), bo oczywiście kompilator potrafi obliczyć zajętość miejsca.

Sam standard mówi:
"The lifetime of an object of type T begins when:
? storage with the proper alignment and size for type T is obtained, and
? if T is a class type with a non-trivial constructor (12.1), the constructor call has completed.
The lifetime of an object of type T ends when:
? if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
? the storage which the object occupies is reused or released."
I nawet bardziej się kompilukuje, bo można w pewnych warunkach uzyskać dostęp do obiektu po jego znieszczeniu, a przed ponownym użyciem, itp. [hmm, np. do zmiennej statycznej w klasie, cały punkt 3.8 Object Lifetime dla zainteresowanych]

Najlepiej nie patrzeć na to, niech kompilator sam się tym zajmuje.

[jeśli gdzieś się walnąłem to proszę mnie poprawić, ale jeśli nie wiesz o czym w ogóle to jest to wolę nie czytać twojego postu...]

0

kompilator tylko raz inicjuje ta zmienna, gdyz nie jest ona pozniej ani razu uzywana

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