Optymalizacja kodu - jak to działa?

0

Nie wiem gdzie to umiescic, bo to temat niezalezny od jezyka wiec niech bedzie tu.
1: majac petle np.

for(i=1; i<5; i++)
    for(j=1; j<1000; j++)
         A[i,j]=i+j;

i

for(j=1; j<1000; j++)
    for(i=0; j<5; i++)
         A[i,j]=i+j;

dlaczego pierwsza bedzie szybsza niz druga? Przeciez w obu przpadkach mamy tyle samo wykonan, chyba ze ja czegos nie widze
2:
albo dlaczego

for(i=0; i<100; i++)
   T[i]=i;

dziala wolniej niz

for(i=0; i<100)
{
  T[i++]=i;
  T[i++]=i;
}

3: albo ze obrazek podzielony wczyta sie szybciej z www niz jako calosc. Przeciez i tak musi pobrac tyle samo danych.
4: albo ze zamiast pojedynczej petli

for

lepiej uzyc do .. while

 bo bedzie szybciej.

Moze mnie ktos oswiecic jak sie to "zauwaza" co jest szybsze?
0
nieznany napisał(a)

Nie wiem gdzie to umiescic, bo to temat niezalezny od jezyka wiec niech bedzie tu.
1: majac petle np.

for(i=1; i<5; i++)
    for(j=1; j<1000; j++)
         A[i,j]=i+j;

i

for(j=1; j<1000; j++)
    for(i=0; j<5; i++)
         A[i,j]=i+j;

dlaczego pierwsza bedzie szybsza niz druga? Przeciez w obu przpadkach mamy tyle samo wykonan, chyba ze ja czegos nie widze

Prawdopodobnie pętla środkowa mieści się w całości w cache L1, w drugim przypadku nie, więc mamy 1000 L1 cache miss. To tylko teoria (niby jak to sprawdzić/definitywnie udowodnić?) ale sądzę że jest prawdziwa.
Oprócz tego w przypadku pierwszej pętli procesor ma ułatwione przewidywanie kolejnych adresów.

2:
albo dlaczego

for(i=0; i<100; i++)
   T[i]=i;

dziala wolniej niz

for(i=0; i<100)
{
  T[i++]=i;
  T[i++]=i;
}

Teoretycznie powinno być tak samo, ale najwyraźniej kompilator jest głupi i w kodzie (wynikowym) mamy tymczasową zmienną, a do tego jest on dluższy, więc znowu może zaistnieć L1 cache miss.
No i znowu zachodzi ułatwione przewidywanie adresów.

3,4 - nie mam pojecia; 4 to jak sadze sprawa konkretnego kompilatora

0
nieznany napisał(a)

Nie wiem gdzie to umiescic, bo to temat niezalezny od jezyka wiec niech bedzie tu.
1: majac petle np.

for(i=1; i<5; i++)
    for(j=1; j<1000; j++)
         A[i,j]=i+j;

i

for(j=1; j<1000; j++)
    for(i=0; j<5; i++)
         A[i,j]=i+j;

dlaczego pierwsza bedzie szybsza niz druga? Przeciez w obu przpadkach mamy tyle samo wykonan, chyba ze ja czegos nie widze

Owszem, nie widzisz - cache'u procesora i mechanizmu stronnicowania pamięci.

  • pierwsza pętla iteruje po kolejnych elemntach kolejnych podtablic - przetwarza całościowo kolejne tablice. Dla procesora jest znacznie łatwiej - ładuje kolejne linie do cache'u i przechodzi przez nie kolejno, pudło trafia się raz na linię - raz na 32 bajty. Od strony stronnicowania zaś jest mniej skakania po stronach, mniej chaotyczne doczytywanie z dysku itd.
  • druga pętla pobiera n-ty element kolejnych tablic, skacze chaotycznie po stronach pamięci, ciągle aktualizuje cache...
    Cały cyrk polega na tym aby pamięci używać sekwencyjnie, aby operacje były przewidywalne dla procesora, cache jest niemal tak szybki jak rejestry procesora. Odpowiednie użycie pamięci pozwala na optymalne użycie cache'u przez procesor. Swego czasu pisząc ultrawydajny algorytm na SSE dorzuciłem ręczne sterowanie ładowaniem pamięci do cache'u - słownie dwie instrukcje i zmieniłem zapisujące wynik na składujące bezpośrednio w RAM z pominięciem pamięci podręcznej. Konwersja obrazu przy większych bitmapach była nawet 10x szybsza. Poczytaj dokumentacje Intela, przede wszystkim tom o optymalizacji.
    Inna sprawa, że skakanie pomiędzy pętlami to dodatkowe utrudnienie dla procesora - in głębsza pętla tym więcej iteracji powinna mieć - mniej nieregularności wykonywania, mniej zmian w dostępie do cache'u... Druga pętle znacznie więcej razy będzie przeskakiwać pomiędzy sprawadzaniem warunków - więcej rozgałęzień, więcej opóźnień.
nieznany napisał(a)

2:
albo dlaczego

for(i=0; i<100; i++)
   T[i]=i;

dziala wolniej niz

for(i=0; i<100)
{
  T[i++]=i;
  T[i++]=i;
}

Hm, masz chyba drobny błąd - aby kody były rónoważne w drugim liczba iteracji powinna być dwukrotnie mniejsza. To tak zwane rozwijanie pętli, sytuacja ma się w sumie podobnie jak poprzednio. Spore znaczenie ma tu praca potokowa - procesor potrafi wykonywać kilka operacji jednocześnie. Skoki powodują wstrzymanie wszystkich strumieni i ich oczyszczenie - strata czasu. Dwukrotnie więcej iteracji to drukrotnie więcej opóźnień. Do tego drugi kod może zostać zoptymalizowany przez kompilator - może on używać np. dwóch rejestrów przez cos równocześnie wpisuje dwie wartości i zwiększa obie o 2. W obecnych czasach rozkazy IA-32 są emulowane - dekodery rozkładają opkody x86 na mikrokod i przekształcają w celu uzyskania lepszej wydajności, tymczasowych rejestrów wewnętrzych zaś jest bodaj 128 /zastępują 8 rejestrów ogólnych przy właściwym wykonywaniu/. Dłuższa iteracja, zawierająca więcej instrukcji daje większe pole manewru w budowaniu 'mikroprogramu' i zarządzaniu cache'm. Idealnym rozwiązaniem jest rozwinięcie pętli do operacji na 2^n bajtów w jednej iteracji, najczęściej mówi się 4 lub 8 operacjach. Duże znaczenie ma też wyrównanie danych - adres danych /w uogólnieniu/ powinien być wielokrotnością rozmiaru danych - dword do 4 itd, ma to związek z ładowaniem danych do cache'u. Najlepsze wyniki daje adres wyrównany do 32 i operacje na 32 bajtach w jednej iteracji /no... jeżeli operujemy na czymś większym - qword czy struktury, np. piksele/.

nieznany napisał(a)

3: albo ze obrazek podzielony wczyta sie szybciej z www niz jako calosc. Przeciez i tak musi pobrac tyle samo danych.

ma to związek z szybkością przesyłania danych w jednym połączeniu, wiele serwerów ma limity itd, różne cuda są. W każdym razie jednoczesne pobieranie na n połączeniach zwykle idzie szybciej /np. omija wspomniane ograniczenia/ niż na pojedynczym.

nieznany napisał(a)

4: albo ze zamiast pojedynczej petli

for

lepiej uzyc do .. while

 bo bedzie szybciej.

Zależy, <b>for</b> równie dobrze może być tak samo szybki jak <b>while</b>. <b>do</b>... <b>while</b> sprawdza warunek pod koniec, wszystkie operacje sterujące są odpowiednio 'poustawiane' przez programistę - kompilator niejednokrotnie może to lepiej zoptymalizować. Chociaż jak znam życie to masz na myli coś takiego:
```cpp
int baiji = 69; // czy tam coś wygenerowanego dynamicznie
do {
    // ...
} while (--baiji);

Na czym owa sztuczka polega? Predekrementacja ustawia flagi procesora - sprawdzenie czy baiji == 0 odbywa się jakby za darmo, skok warunkowy wykonuje się zależnie od dekrementacji, bez dodatkowych testów.
</quote>

@Fr3m3n: nie zauważyłeś kilku rzeczy ale jak sam na ircu przyznałeś zmęczony jesteś :-)</b>

p.s. takiego indeksowania: A[i,j] nie ma w standardzie, i,j to blok, z którego liczy się tylko ostatni symbol, przyjmijmy, że to pseudokod a w 'prawdziwym' powinno być A[i][j]

0

1' - dokladnie tak, popieram, kwestia cache'a i predykcji
2' -

deus napisał(a)

Hm, masz chyba drobny błąd - aby kody były rónoważne w drugim liczba iteracji powinna być dwukrotnie mniejsza.

deus - alez jest! on ma tylko warunek i<100! wykona sie 50 iteracji, czyli o 50 skokow wstecz mniej, stad ta roznica w czasie. z reszta opisu sie w 100% zgadzam

// ups, fakt, drobne niedopatrzenie - podwójna inkrementacja przecież (dop.deus)

nieznany napisał(a)

3: albo ze obrazek podzielony wczyta sie szybciej z www niz jako calosc. Przeciez i tak musi pobrac tyle samo danych.

poza limitami laczy - dodatkowo dochodzi kwestia postrzegania: jak sie pojawi kawalek, kawalek i kawalek i za chwile kawalek, to user sie mniej znudzi niz jakby mial czekac 4x tyle i nagle widac calosc.

deus napisał(a)
int baiji = 69; // czy tam coś wygenerowanego dynamicznie
do {
    // ...
} while (--baiji);

Na czym owa sztuczka polega? Predekrementacja ustawia flagi procesora - sprawdzenie czy baiji == 0 odbywa się jakby za darmo, skok warunkowy wykonuje się zależnie od dekrementacji, bez dodatkowych testów.

hym. moim zdaniem zapis:

for(int i=69; --i; )

jest rownoznaczny Twojemu, wiec w ogole trudno mowic o jakiejkolwiek wyzszosci do/while nad for czy vice versa :)

// napisałem, że równie dobrze for może być tak samo szybki, Twój zapis jest dokładnie równoważny tamtemu do...while :-) (dop. deus)

0
nieznany napisał(a)

dziala wolniej niz

for(i=0; i<100)
{
  T[i++]=i;
  T[i++]=i;
}

A to nie jest przypadkiem Undefined Behavior?

--

Co do tematu, można dyskutować ogólnie, ale konkretne optymalizacje robi się pod konkretnym kompilatorem, z konkretnymi założeniami co do procesora. Na nowszych możesz zakładać różnorakie działanie ficzerów procesora, na starszych nie będzie nic widać.

Czytałem nawet, że for(;;) potrafi być szybsze niż while(1), bo to drugie trzyma trzyma gdzieś zmienną. No ale najprostsza optymalizacja zrobi z tego to samo co for(;;).

// Człowieku, od kiedy 1 to zmienna!? Każdy kompilator rozwija stałe wartości... for szybsze? a zobacz, sam ';' powinien wygenerować 'pustą' instrukcję /nop'a/ - na takiej samej zasadzie podejście... (dop. deus)</i>

0

odnosnie punktu 3 to jesli dobrze pamietam wg protokolu http przegladarka moze wyslac do 8 rzadan do serwera czyli pobierac 8 obrazkow naraz. teoretycznie powinna sie zmniejszyc predkosc pobierania wtedy ale roznie z tym bywa. przypuszcam, ze poprawa szybkosci to tylko efekt psychiczny. webmasterzy dziela grafike na czesci zeby bylo widac ze grafika sie laduje i sprawia to wrazenie ze laduje sie szybciej niz jakby byla duza [ bo obrazek nie zostanie pokazany zanim nie zostanie zaladowany do konca ]

0
Ambroży napisał(a)

A to nie jest przypadkiem Undefined Behavior?

undefined behaiour masz na przyklad wtedy, jak masz modyfikacje tej samej zmiennej dwa razy w jednej_linii np:
T[i++] = i++;
a w tamtym przykladzie to co najwyzej mozna sie przyczepic o brak drugiego srednika w for..
reszte juz mnie deus uprzedzil :)

moriturius napisał(a)

wg protokolu http przegladarka moze wyslac do 8 zadan do serwera

wg http? 8? wskaz mi RFC bo mi sie wierzyc nie chce zeby definiowano tam jakiekolwiek ograniczenia. a reszte to ja juz napisalem pare postow wyzej :)

0

// Człowieku, od kiedy 1 to zmienna!? Każdy kompilator rozwija stałe wartości...
e tam, bzdura, zależy od języka, są języki w których 1 to obiekt, w których można nawet zrobić coś tak strasznego jak 1:=2

T[i++]=i++ , w C jest niezdefiniowane, zresztą ten język jak wiele innych jest w pewnym sensie definiowany przez sam kompilator czyli niuanse pozostawione w specyfikacją są dospecyfikowane w kompilatorze. Zgodnie z moim interpreterem ta insturkcja jest semantycznie równoważna tej: { T[i+1]=i+2; i=i+2 }

W necie jest sporo kompilatorów takich okrojonych wersji języków C i C-podobnych, możesz sobie sprawdzić na jaki kod tłumaczone mogą być instrukcje for, while, itp. i wtedy będzie troche jaśniejsze dlaczego cośtam działa szybciej, a cośtam troche wolniej.</quote>

0

zachowanie nezdeiniowane nie oznacza dodefiniowanie jezyka przez tworcow kompilatora, tylko oznacza tyle, ze standard takiej sytuacji nie przewiduje. nie powinno sie uzywac takich konstrukcji. porownaj z kwestia int*i; cout << *i; zachowanie sie czegos takiego rowniez jest niezdeiniowane przez standard jezyka, poniewaz i nie zostalo zainicjalizowane. to ze takie konstrukcje (czesciej lub rzadziej) jakos-tam-dzialaja jest przypadkiem wynikajacym z konstrukcji kompilatora. to ze 'i' jest niezainicjalizowane oznacza ze moze zawierac wskaznik na sensowny obszar pamieci i sie wyswietli jakas sensowna wartosci. w rzeczywistosci zas, to sa bledy, ktore zazwyczaj sa nie do wykrycia w momencie kompilacji, np. tab[*ptr++]=*ptr2++; jesli ptr==ptr2, to zachowanie jest undefined, na visual studio zadziala w jeden sposob, na g++ zadziala w inny, moze stac sie wszystko, to jest blad programisty ktory za bardzo chcial sobie skrocic robote

0

Czytałem nawet, że for(;;) potrafi być szybsze niż while(1), bo to drugie trzyma trzyma gdzieś zmienną. No ale najprostsza optymalizacja zrobi z tego to samo co for(;;).

// Człowieku, od kiedy 1 to zmienna!? Każdy kompilator rozwija stałe wartości... for szybsze? a zobacz, sam ';' powinien wygenerować 'pustą' instrukcję /nop'a/ - na takiej samej zasadzie podejście... (dop. deus)

Tutaj miałem na myśli, że właśnie takie kwiatki można często spotkać w poradach typu 'optymalizacja dla języka XYZ'. Kompilator C/C++ zrobi z tego bez problemu jakiś zwykły jmp. Zapomniałem po prostu o tym wspomnieć :P

quetzalcoatl napisał(a)

undefined behaiour masz na przyklad wtedy, jak masz modyfikacje tej samej zmiennej dwa razy w jednej_linii np:

#include <iostream>

int main() {
   int a[2] = {0};
   int i = 1;

   a[i++] = i;

   std::cout << a[0] << '\n' << a[1] << std::endl;
   std::cout << i << std::endl;
}

warning: operation on 'i' may be undefined

Wynik jest taki jak można się spodziewać, ale takich konstrukcji raczej bym unikał. Choć rzeczywiście UB byłby w takim przypadku: http://c-faq.com/expr/evalorder1.html

0

Hmm, co do T[i,j]
Możliwość pierwsza - i jest ignorowane - mamy tablice jednowymiarową, więc l2 dużo nie zmienia, ale w takim przypadku drugie byłoby dużo ba szybsze wiec odpada
Dwie podtablice - czyli odpowiednik T[i][j] - to co deus powiedział
Jedna tablica dwuwymiarowa czyli T[i*rozmiar+j] - l2 znowu jest ok, wiec role gra wielkość kodu (tak robi chyba borland c++ ? albo robil)

Byłoby dobrze gdyby autor się sprecyzował.

0

oczywiscie T[i][j].

ad1. tu chyba chodzi o to ze w pierwszym przypadku zmienna wewnetrzna jest inicjowana 100razy, a w drugim tylko 5.
ad4. wedlug pewnej ksiazki jest szybciej bo

do..while

nie analizujemy niepotrzebnie warunku. Dziwne.

0

Wiecie co, trudno tutaj rozpatrywać sprawy optymalizacji w jakiejkolwiek formie. Ja się kiedyś naciąłem na ASM. Co szybsze wg was:
sub o jeden w dół
czy dec?

Zdzwiłem się jak się okazało że to pierwsze :). Sprawa wynikła jak pisałem w C i potem porównałem w C instrukcję i++ z tym samym napisanym w ASM. Okazało się że kompilator przerabiał to na sub i dlatego było szybsze niż moje dec we wstawce.

Poza tym w optymalizacji jest z tego co pamiętam jeszcze motyw z łączeniem instrukcji mimo że normalnie nie są połączone. Gdzieś o tym czytałem, że tak się dzieje w procku, że pewne instrukcje są wczytywane parami (albo coś namieszałem, bo to już wieki temu widziałem).

0

Owszem, sub x, 1 jest szybsze od dec x w procesorach Intela od Pentium 4, u AMD to chyba jeszcze wcześniej było, nie pamiętam dokładnie kiedy. Wniosek? Czytać oficjalne knigi o optymalizacji, sporo można z nich wynieść przydatnych informacji.

Jeżeli zaś o parowanie chodzi - jeżeli instrukcje ze sobą nie kolidują, wynik drugiej nie jest zależny od wyniku pierwszej to mogą być wykonywane równolegle. Niejednokrotnie przeplatałem dwa algorytmy aby uzyskać odpowiednie parowanie - jeżeli instrukcje są od siebie zależne to wytępują tzw. kary /opóźnienia/, procesor musi czekać na wynik poprzedniej operacji. Przy pracy potokowej, gdzie procesory mają po > 20 stopni w potoku każda kara poważnie wpływa na wydajność. Z resztą, dwa potoki to przeszłość, teraz są co najmniej 4, z tego co pamiętam to już P4 miał 4 potoki ALU 'zwykłe' i 2 o podwójnym taktowaniu wiec parowanie faktycznie jest ważne.

A propos sub jeszcze - po prostu sub jest w prostszy sposób dekodowane do mikrokodu, dec wymaga minimalnie więcej pracy od dekoderów /teraz nawet długość instrukcji i wlaściwości w postaci 'argumentów domyślnych' - -1 przy dec - mają wpływ na wydajność/.

0
deus napisał(a)

Owszem, sub x, 1 jest szybsze od dec x w procesorach Intela od Pentium 4, u AMD to chyba jeszcze wcześniej było, nie pamiętam dokładnie kiedy. Wniosek? Czytać oficjalne knigi o optymalizacji, sporo można z nich wynieść przydatnych informacji.

to chyba mało wiesz chłopcze, manuale czytałeś, ale najwyraźniej tylko 1 strone.

deus napisał(a)

Jeżeli zaś o parowanie chodzi - jeżeli instrukcje ze sobą nie kolidują, wynik drugiej nie jest zależny od wyniku pierwszej to mogą być wykonywane równolegle

LOL x 2, parowanie w czasach core duo :-), masz facet fantazje, skad żeście wzięli tego kolesia :-), parowanie to robiło się na procesorach Pentium 1, gdzie faktycznie miało to wpływ na wykonywanie kodu, ale nie w 2007 roku! Człowieku weź się nie wypowiadaj na tematy, o których masz blade pojęcie bo robisz krzywde innym ludziom, rozgłaszając fałszywe informacje. Pokaż fragment manuala intela do najnowszego procka, który opisuje te twoje parowanie na Core Duo...

0
andrzej2 napisał(a)

LOL x 2, parowanie w czasach core duo :-),

Mości lolku co ma parowanie do core duo? Jak chcesz więcej to poczytaj conieco na stronie Intela albo zamów sobie lekturkę do domu. Przynosi ją taki miły pan z firmy kurierskiej nawet do 3-4 dni od wysłania.

Zresztą prócz śmieszności jakie napisałeś nie ma w Twojej wypowiedzi żadnych argumentów. Chcesz przykład nowego procesora, który obsługuje parowanie? Proszę bardzo: Intel Core 2 T7200. Każdy procesor ma kompatybilność wsteczną, a samo parowanie przyspiesza pracę CPU więc dlaczego miałoby się go odpuścić w zamian za core duo? Wiesz w ogóle co to core duo?

deus radzę lać na tego ziomka ciepłym moczem, to zwykły prowokator ;)

0

deus radzę lać na tego ziomka ciepłym moczem, to zwykły prowokator ;)

true, true.. nagadal sie, nagadal, ale sam nic nie powiedzial konkretnego. typowe

0
CyberKid napisał(a)

Intela albo zamów sobie lekturkę do domu. Przynosi ją taki miły pan z firmy kurierskiej nawet do 3-4 dni od wysłania.

ale trzeba tą przesyłke jeszcze otworzyć

CyberKid napisał(a)

Zresztą prócz śmieszności jakie napisałeś nie ma w Twojej wypowiedzi żadnych argumentów. Chcesz przykład nowego procesora, który obsługuje parowanie? Proszę bardzo: Intel Core 2 T7200. Każdy procesor ma kompatybilność wsteczną, a samo parowanie przyspiesza pracę CPU więc dlaczego miałoby się go odpuścić w zamian za core duo? Wiesz w ogóle co to core duo?

LOL x 3, ma kompatybilność wsteczną, gdzie was tego uczą ludzie? To czy tak samo się pisze kod na najnowsze procesory jak na 386 (chyba, że ty tak piszesz cyber-dzieciaku) żeby uzyskać najlepsze rezultaty? NIE! Parowanie, które opisał nasz niedouczony kolega deus funkcjonowało poprawnie na serii Pentium.

CyberKid napisał(a)

deus radzę lać na tego ziomka ciepłym moczem, to zwykły prowokator ;)

a wam radze poczytać manuale, zamiast poklepywać się po ramieniu i utwierdzać w błędnych opiniach

// Tak, LOL 3x, brak konkretów... aż szkoda, że kolega posiada neostradę (dop. deus)
// W tym poście adres jest inny... - Q

0
andrzej2 napisał(a)

a wam radze poczytać manuale, zamiast poklepywać się po ramieniu i utwierdzać w błędnych opiniach

caly czas nic nie powiedziales poza objezdzaniem pozostalych pisaczy. w ten sam sposob moge powiedziec: facet! jak ty tego posta zes napisal! tak to sie pisalo jak Coyote bylo w powijakach! wez Ty sie doucz zanim klawiatury dotkniesz bo to co odwalasz to moja babcia by tak nie napisala. I co, wiadomo o co chodzi, prawda? wyjasnij chocby, czemu Twoim zdaniem tego nie ma lub tez zle dziala w nowych procesorach? bez inwektyw, same konkrety, uzasadnienia, linki do dokumnetacji

0

Dec jest szybsze od sub na każdym procku oprócz pentium 4 (nawet dłuższa instrukcja na 64 bitowym).
Jak można zobaczyć w manualu, jest to jeden mikroopcod.
Core (2) duo ciągle jedzie na bardzo mocno zmodyfikowanym jądrze pochodzącym z pentium 1 (netburst), podczas gdy p4 ma swoje własne (zaprojektowane dla prędkości rzędu 10ghz, ale okazało się że za bardzo się grzeje).

andrzej2 napisał(a)
deus napisał(a)

Jeżeli zaś o parowanie chodzi - jeżeli instrukcje ze sobą nie kolidują, wynik drugiej nie jest zależny od wyniku pierwszej to mogą być wykonywane równolegle

LOL x 2, parowanie w czasach core duo :-), masz facet fantazje, skad żeście wzięli tego kolesia :-), parowanie to robiło się na procesorach Pentium 1, gdzie faktycznie miało to wpływ na wykonywanie kodu, ale nie w 2007 roku! Człowieku weź się nie wypowiadaj na tematy, o których masz blade pojęcie bo robisz krzywde innym ludziom, rozgłaszając fałszywe informacje. Pokaż fragment manuala intela do najnowszego procka, który opisuje te twoje parowanie na Core Duo...

Lol. Czepiasz samej nazwy - 'parowanie'?
Pentium 1 miało tylko dwa potoki, stąd ta nazwa, ale ciągle jest używana jako określanie takiego rozmieszczenia instrukcji, tak żeby te zależne od siebie były jak najdalej od siebie, dzięki czemu będą mogły zostać wykonane równocześnie.

Jeśli chodzi ci oto że core duo nie ma potoków, to się mylisz... owszem, parowanie instrukcji ma obecnie dużo mniejsze znaczenie niż kiedyś (teraz najbardziej liczy się dobre użycie cache, jako że procki są nawet 12x wolniejsze od pamięci ram).
Od tego są właśnie kompilatory, bo ręczne parowanie (całego kodu) dziś wielkiego sensu nie ma :) (poza rzadkimi numerycznymi częściami kodu - dobre sparowanie może dać nawet 8x szybszy kod...).

P.S Na pentium 4 parowanie ma największe znaczenie, co wynika z budowy tego procka i jego 'dziwactw'.

0
fr3 napisał(a)

Od tego są właśnie kompilatory, bo ręczne parowanie (całego kodu) dziś wielkiego sensu nie ma :) (poza rzadkimi numerycznymi częściami kodu - dobre sparowanie może dać nawet 8x szybszy kod...).

Daj spokój.
Już wielu oparło się na kompilatorze, i dlatego programy działają coraz
wolniej pomimo ciągłego wzrostu wydajności sprzętu.

Wlezie taki mistrz i manipuluje w tych VCL, np. tak:

if( Bitmap7->Canvas->Pixels[x,y] = Bitmap7->Canvas->Pixels[x,y+1] )
 Bitmap7->Canvas->Pixels[x,y] = Bitmap7->Canvas->Pixels[x,y+1] ^ 7;

lub, już tradycyjna, metoda sprawdzania parzystości dzieleniem:
if( i % 2 == 1 )
nie wiem czy kompilator zamieni to na if( i & 1 ),
zwłaszcza że kompilatory robią pewnie tacy sami spece.

albo obliczenia na float:

for(float x = 1; x < 10; x++) suma = suma + x;

suma jest float to x też przecież musi być, no bo jak inaczej...

a dla zwiększenia precyzji wali tak:

for(long double x = 1; x < 10; x++) suma = suma + x;

:-D

0
andrzej2 napisał(a)

ale trzeba tą przesyłke jeszcze otworzyć

Ojojoj, no ja wiedziałem, że o czymś zapomniałem. Ach ten Niemiec. Jak mu tam byłó? Coś na A. No tak, Ajnsztajn. Poza tym "tę" przesyłkę, a nie "tą" przesyłkę.

andrzej2 napisał(a)

LOL x 3

To mi zalatuje Tibią. Nie wiem czemu ale każdy tzw. Tibijczyk jakiego spotykam ma słabość do tego lolowania i na dodatek lubi je mnożyć. Jakaś nowa moda?

andrzej2 napisał(a)

chyba, że ty tak piszesz cyber-dzieciaku

Raczysz mnie prowokować? Buhehehe, próbuj, próbuj. Może dwa lata temu by to przeszło. Nie teraz ;) Taki sobie jestem cyber-dzieciaczek. Bywa ;)

andrzej2 napisał(a)

a wam radze poczytać manuale, zamiast poklepywać się po ramieniu i utwierdzać w błędnych opiniach

Mości mentorze, przewspaniały i przebłyskotliwy. Profesorze nad profesorami, nadhabilitowany powiedz że temu ludowi jakie to manuale przewspaniałe czytujesz i gdzie można je znaleźć? Może jakieś tytuły? Przykłady?

Znowu skończyliśmy na niczym nie popartych wywodach tego śmiesznego kolegi. Co by nie być gołosłownym "Intel 64 and IA-31 Architectures Optimization Reference Manual" (2-43): "In general, each core in multicore processor resembles a single-core processor implementation of the underlying microarchitecture". Zakładam, że mości profesor zna tyle języków, że ten marny angielski rozumuje i potrafi sobie przetłumaczyć na nasz rodzimy język.

aż szkoda, że kolega posiada neostradę (dop. deus)

Widać teraz w pakiecie neostrady dają manuale ... jak zostać dzieckiem neostrady [rotfl]

0
qu napisał(a)

Daj spokój.
Już wielu oparło się na kompilatorze, i dlatego programy działają coraz
wolniej pomimo ciągłego wzrostu wydajności sprzętu.

O wiele większe znaczenie mają algorytmy, pisanie w assemblerze dawno przestało mieć sens.
Jedyne sensowne przypadki to:
a.) system operacyjny (pisanie podstaw osu);
b.) wykorzystanie jakichś instrukcji których kompilator nie rozumie np. SSE3 (czy tam SSE4 teraz...). I tyle :)
No, jakiś numeryczny brute force dla 0.001% większej wydajności ^^

Wlezie taki mistrz i manipuluje w tych VCL, np. tak:

if( Bitmap7->Canvas->Pixels[x,y] = Bitmap7->Canvas->Pixels[x,y+1] )
 Bitmap7->Canvas->Pixels[x,y] = Bitmap7->Canvas->Pixels[x,y+1] ^ 7;

Yyy chodzi o pojedyczny '=' w ifie? Co to za przecinki w []... Nie do końca rozumiem na co to przykład... na kogoś kto uczy się języka:?

lub, już tradycyjna, metoda sprawdzania parzystości dzieleniem:
if( i % 2 == 1 )
nie wiem czy kompilator zamieni to na if( i & 1 ),
zwłaszcza że kompilatory robią pewnie tacy sami spece.

Sprawdzanie parzystości NIE używając modulo byłoby naprawdę bardzo głupie :) Każdy sensowny kompilator zoptymalizuje to, a jeśli nie zoptymalizuje to znaczy że trzeba go zmienić :)
Pisanie & zamiast % to niepotrzebne zaciemnianie kodu (and sugeruje, że zmienna/coś ma jakieś flagi bitowe - a nie operację arytmetyczną!)...

albo obliczenia na float:

for(float x = 1; x < 10; x++) suma = suma + x;

suma jest float to x też przecież musi być, no bo jak inaczej...

a dla zwiększenia precyzji wali tak:

for(long double x = 1; x < 10; x++) suma = suma + x;

:-D

Te kody są równoważne i o wiele szybsze od wersji z int x = 1, co zapewne miałeś na myśli.
Dlaczego? Błędy precyzji. Kompilator NIE MOŻE zmienić inta na float gdyż może zmienić to wynik; w rezultacie dodawanie będzie całkowite (add) co razem daje nam dwie niepotrzebne operacje:

  1. dodawanie całkowite
  2. konwersja z inta do floata
  3. dodanie skonwertowanej liczby do 'suma'
    zamiast
  4. dodaj liczbę do suma.

Dodatkowo wykonanie równoległe tego kodu staje się praktycznie niemożliwe, jako że każda instrukcja zależy od poprzedniej...

0
fr3 napisał(a)

Jedyne sensowne przypadki to:
a.) system operacyjny (pisanie podstaw osu);
b.) wykorzystanie jakichś instrukcji których kompilator nie rozumie np. SSE3 (czy tam SSE4 teraz...). I tyle :)
c.) mikroprocesory (takie do lutowania :P)

// nie mikroprocesory a mikrokontrolery... (dop. deus)

co do optymalizacji, to dużo ważniejsze jest odpowiednie zaprojektowanie struktur danych i procedur (algorytmów), które te dane przetwarzają, co pozwala na zmniejszenie złożoności (przyspieszenie programu o rzędy wielkości), niż optymalizacje, które przyspieszą program o 1%...

Nawet, jakby optymalizacje dawały 50% przyspieszenia, to co z tego - kowalski będzie musiał czekać na jakąś akcję 2s zamiast 4 i będzie niezadowolony, bo w produkcie konkurencji wynik jest "od razu" po "kliknięciu myszką"

0
fr3 napisał(a)

Te kody są równoważne i o wiele szybsze od wersji z int x = 1, co zapewne miałeś na myśli.
Dlaczego? Błędy precyzji. Kompilator NIE MOŻE zmienić inta na float gdyż może zmienić to wynik; w rezultacie dodawanie będzie całkowite (add) co razem daje nam dwie niepotrzebne operacje:

  1. dodawanie całkowite
  2. konwersja z inta do floata
  3. dodanie skonwertowanej liczby do 'suma'
    zamiast
  4. dodaj liczbę do suma.

Dodatkowo wykonanie równoległe tego kodu staje się praktycznie niemożliwe, jako że każda instrukcja zależy od poprzedniej...

Odwrotnie - int jest zmieniany na float;
instrukcja:
fldi - załadowanie int do koprocesora, i taki wstawi kompilator.

& jest operatorem arytmetycznym.

Kod przeciętnego frajera, wierzącego w kompilatory, można nawet setki razy przyspieszyć.

// ekhm... po pierwsze to fild, po drugie to & jest mimo wszystko logiczny, nie arytymetyczny... widać też, że nie zrozumiałeś postu fr3m3na /pomijając, że jest tam kilka nieścisłości/ (dop. deus)

0
qu napisał(a)

Odwrotnie - int jest zmieniany na float;

No tu mnie ściąłeś:

fr3 napisał(a)
  1. konwersja z inta do floata

Odwrotnie? O_O

& jest operatorem arytmetycznym.

Nie, & jest funkcją boolowską.

Generalnie twój post można przedstawić jako:
(ja) "nieprawda, jest inaczej"
(ty) "nie zgadzam się, jest tak jak mówisz"
...

(poza ostatnim zdaniem)

0

[C/C++] um.. & to jest operator bitowy wykonujacy AND parami po wszystkich bitach argumentow.. && jest operatorem boolowskim, czyli logicznym. nie znam zadnego '&' arytmetycznego, chociaz rzeczywiscie, odbiedy mozna bitowy uznac za arytmeryczny bo operuje na samej wartosci a nie tylko jej znaczeniu logicznym

0
qu napisał(a)

Kod przeciętnego frajera, wierzącego w kompilatory, można nawet setki razy przyspieszyć.

W takim razie (hipotetyczna sytuacja) ja, jako przeciętny frajer w określonym krótkim czasie bym się głowił nad złożonością, a resztę zostawiłbym kompilatorowi (oczywiście muszę wiedzieć, co kompilator może przyspieszyć, a gdzie podniesie złożoność), a Ty byś dawał gdzie się da jakieś kombinacje w operacjach, czy wstawki assemblerowskie. Co wyjdzie? Tobie stukrotne zoptymalizowany brut n^4, a mi całkowicie niezoptymalizowana kwadratówka ;)

dzisiaj nie płaci się za drobne optymalizacje, które gratisowo zaciemniają kod, liczy się złożoność :)

0

ja pisze wszystko w delphi i jestem bardzo zadowolony z optymalnosci kodu. czasem tylko poprawie cos jak widze ze delphi moglo zrobic to lepiej np. zamiana mov eax, 0 na xor eax, eax. Bardzo wydajny jest tez PHP (ale php 6)i z tego co slyszalem (nie sprawdzalem) kod jezyka C ale tego juz sie w ogole nie uzywa.

ja pisze wszystko w jezykach wysokich poziomow a takie rzeczy jak na przyklad ort! systemu to juz w asemblerze bo zalezy mi na szybkosci.

// a co ma assembler do wyłączania systemu? Teraz jest to osiągalne wyłącznie poprzez API danego systemu (dop. deus)

podam tez przyklad kodu ktory jest szybszy w php niz asemblerze

for ($i = 0; $i < 100; $i++) chodzi o to ze asembler musi sprawdzac czy dana liczba jest liczba a php to wie i nie musi.

// chłopcze, w assemblerze wszystko jest liczbą, coś takiego jak stringi czy wskaźniki nie istnieją (dop. deus)

Kolego qu jak juz slusznie zauwazono nie piszesz prawdy kompilatory (przynajmniej delphi) generuja 100 razy szybszy kod niz ty bys napisal (poza drobnymi wyjatkami o ktorych juz wspomnialem). co do prasowania w nowych procesorach to ono tam jest ale korzysta sie z niego tak malo i szybko ze prawie nie widac

Kolego RR sub o jeden bedzie tak szamo szybkie ze wzgledu na wspomniane prasowanie

pozdrawiam odpowiem na wszelkie pytania

0

czasem tylko poprawie cos jak widze ze delphi moglo zrobic to lepiej np. zamiana mov eax, 0 na xor eax, eax.

ouc.. to delphi takie cos zostawia..? btw. a jak to potem poprawiasz..??

bardzo zadowolony z optymalnosci kodu. czasem tylko poprawie cos jak widze ze delphi moglo zrobic to lepiej np. zamiana mov eax, 0 na xor eax, eax.

co ma wywolanie winapi nakazujace wylaczenie systemu do szybkosci..?

podam tez przyklad kodu ktory jest szybszy w php niz asemblerze
for ($i = 0; $i < 100; $i++) chodzi o to ze asembler musi sprawdzac czy dana liczba jest liczba a php to wie i nie musi

OMG.. czy Ty wiesz o czym mowisz w ogole? jesli juz cokolwiek by musialo sprawdzac, to wlasnie PHP poniewaz ono nie ma typizacji i operuje na svalue'ach.. assembler na pewno nie musialby czegos takiego sprawdzac, wystarczy ze $i trzymalby sobie np. w rejestrze

prasowania

zarzuc linka, bo nie wiem co masz na mysli? ..a moze to mialo byc parowanie?

// widzę, że się tutaj burdel robi, ludzie bez pojęcia wymyślają niestworzone rzeczy - php szybsze od asm itd... a andrzej2 zniknął. Jeszcze jeden taki cyrk a wyczyszczę i zamknę temat (dop. deus)

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