optymalizacja c++

optymalizacja c++
0

hej

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

Kopiuj
while (true)
{
  int z ;
}
Bucz
  • Rejestracja:prawie 21 lat
  • Ostatnio:ponad 17 lat
0

Dobre pytanie.

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


"Twój głos jest jak kula karabinowa!" Malcolm X
JU
  • Rejestracja:około 20 lat
  • Ostatnio:prawie 11 lat
0

tylko jeden raz.
pzdr


vixen03
  • Rejestracja:ponad 21 lat
  • Ostatnio:prawie 14 lat
  • Postów:475
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.


06
  • Rejestracja:prawie 20 lat
  • Ostatnio:około rok
  • Postów:2440
0
vixen03 napisał(a)

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

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

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

zmienna

Kopiuj
x

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

Kopiuj
 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;
}
JA
  • Rejestracja:ponad 22 lata
  • Ostatnio:ponad 2 lata
  • Postów:308
0
0x666 napisał(a)
vixen03 napisał(a)

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

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

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

zmienna

Kopiuj
x

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

Kopiuj
 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

Kopiuj
		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

vixen03
  • Rejestracja:ponad 21 lat
  • Ostatnio:prawie 14 lat
  • Postów:475
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;


06
  • Rejestracja:prawie 20 lat
  • Ostatnio:około rok
  • Postów:2440
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

Kopiuj
x

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

Kopiuj
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

Kopiuj
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.

GR
  • Rejestracja:około 20 lat
  • Ostatnio:ponad 18 lat
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.

06
  • Rejestracja:prawie 20 lat
  • Ostatnio:około rok
  • Postów:2440
0
vixen03 napisał(a)

ale zrob sobie takie cos:

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

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

A to spróbuj tego :D

Kopiuj
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 (

Kopiuj
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 ;)

marcinEc
  • Rejestracja:ponad 20 lat
  • Ostatnio:prawie 19 lat
  • Postów:403
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.:

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

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


Pozdrawiam, //\aRC!||Ec!
Czy wiesz co Twój KOD robi w nocy? Code::Bollocks ;-P : eof()/feof(), srand()/rand(), fflush(), string, float (0.1), scanf(), getline(stream,string). Lamer captured...
System ready.
_
:-P
Heimdall
  • Rejestracja:ponad 20 lat
  • Ostatnio:ponad 18 lat
0

Sprawdziłem na kompilatorze Watcomu przy agresywnej optymalizacji:

Kopiuj
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:

Kopiuj
mov bp,sp
sub sp, 08

Pętla while wygląda tak:

Kopiuj
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.


Northern wind take my song up high
To the Hall of glory in the sky
So its gates shall greet me open wide
When my time has come to die
JA
  • Rejestracja:ponad 22 lata
  • Ostatnio:ponad 2 lata
  • Postów:308
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

marcinEc
  • Rejestracja:ponad 20 lat
  • Ostatnio:prawie 19 lat
  • Postów:403
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]:

Kopiuj
_main:
	call	___main
L2:
	jmp	L2 

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

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

Kopiuj
#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:

Kopiuj
#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:

Kopiuj
#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...]


Pozdrawiam, //\aRC!||Ec!
Czy wiesz co Twój KOD robi w nocy? Code::Bollocks ;-P : eof()/feof(), srand()/rand(), fflush(), string, float (0.1), scanf(), getline(stream,string). Lamer captured...
System ready.
_
:-P
0

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

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.