Zbyt długie wykonywanie skryptu

0

Witam

Napisałem prościutki programik, gdzie w pętli w kółko była dodawana wartość podana w zmiennej a.
Po przekroczeniu wartości 1 powinien wyświetlić się wynik.
Kłopot jest taki ze bardzo długo się to wykonuje.
Co trzeba zrobić aby tak prostą czynność przyspieszyć?

#include <iostream>


using namespace std;

double wynik1, wynik2;

main()
{


//zmiana pracyzji
  cout.precision(15);



wynik1 = 0.000000000004345;

wynik2 = 0;
  


  while(wynik2<1)
{
  
wynik2 = wynik2 + wynik1;

}

cout << wynik2 << endl;
  
  system("pause");
}

6

Potrzebujesz swoje dodawanie wykonać 230149597238 razy, a to jest prawie 2^38 operacji czyli bardzo bardzo dużo. Nie da się tu jakoś magicznie przyspieszyć jeśli chcesz wykonać tyle operacji dodawania. Pytanie brzmi po co tak robisz? Wystarczy podzielić i będzie wiadomo ile mniej więcej dodawań trzeba wykonać...

0

sprobuj skompilowac z optymalizacja -O3:
https://www.linuxtopia.org/online_books/an_introduction_to_gcc/gccintro_50.html

@MarekR22: tak, widziałem pytanie dlaczego?

        movsd   xmm1, QWORD PTR .LC2[rip]
.L2:
        addsd   xmm0, xmm2
        comisd  xmm1, xmm0
        ja      .L2

Za dużo iteracji?

Przy wartości wynik1 = 0.2345; jeszcze obliczenia wykonywane są w czasie kompilacji, po podzieleniu przez 10 już nie.

3
km1606 napisał(a):

Co trzeba zrobić aby tak prostą czynność przyspieszyć?

Czym się motywowałeś, aby takie coś zrobić?
Wolałbym wierzyć, ze chciałeś sprawdzić mozliwości "brutal force" w naiwnej pętli. Sprawdziłeś, uzyskałeś wiedzę że cholernie kosztowne - sukces tego eksperymentu naukowego.

"co trzeba" ?
Oczywiście nie jechać najbardziej naiwnymi schematami. Napisać kod z przemyśleniem o co kaman.
Tu (jak mówi @Shalom) zastąpić przez proste dzielenie.

Gdyby była jakaś zmienność funkcji, i nie dało się uzyskać wyniku przez proste dzielenie, algorytmika zna wiele, wiele sposobów, dlatego jest taka dziedzina.
Na przykład połowienie przedziału, ale i inne.

Ostatnio polski bezład wywrócił taką tradycję liczenia płac od netto.
Zmieniałem naiwny algorytm dziubania co 'x' na wolniejszy (pod względem jednej iteracji - z całym majestatem prawa pracy) ale szybszy, bo z dzieleniem przedziału

2

Zawsze można próbować użyć CUDA na kiju

0

Chyba się nieprecyzyjnie wyraziłem.
Dokładnie chodzi mi oto czy jest jakiś patent aby przyspieszyć samo działanie pętli.
To prostackie dodawanie jest tylko testem szybkości działania.

Moje drugie pytanie jest takie.
Czy w innym języku programowania idzie zwiększyć
szybkość działania pętli?

2
km1606 napisał(a):

Dokładnie chodzi mi oto czy jest jakiś patent aby przyspieszyć samo działanie pętli.

Oczywiście - kupić lepszy komputer!

3

mozesz to przyspieszyc dosyc latwo (o ile masz na to kase) uzywajac OpenMP i AMD Threadrippera. Przyspieszenie do 128 razy:
https://www.x-kom.pl/p/542163-procesor-amd-threadripper-amd-ryzen-threadripper-3990x.html

3

Da się to trochę przyspieszyć:

while(wynik2<1)
{
wynik2 = wynik2 + wynik1;
wynik2 = wynik2 + wynik1;
}
3

@km1606 można to np. spróbować zrównoleglić, więc podzielic pętlę na N kawałków które będą się liczyć na osobnych rdzeniach procesora. Albo możesz sobie zrobić overclocking procka żeby mielił trochę szybciej.

Jeszcze raz powtórzę to co napisałem wyżej: chcesz wykonać 2^38 operacji a to jest bardzo dużo. Założmy że masz 2GHz procesor, czyli może on wykonać w ciagu sekundy 5*10^9 cykli, czyli niecałe 2^31 cykli. Powiedzmy że add jest szybki i zjada tylko 2 cykle, więc mamy 2^30 opercji na sekundę. Czyli twoja pętla zajmie 2^8 = 256 sekund. Jak wykręcisz procek na 4GHz to czas spadnie o połowę. Jak odpalisz na 8 rdzeniach to spadnie 8 razy (przy założeniu że tą pętlę da sie jakoś sensownie podzielić :P). Cudów tutaj nie zdziałasz, nie da się magicznie tej pętli przyspieszyć bo ogranicza się hardware.

5

Ogólnie sposobem na przyspieszenie jest eliminacja skoków, instrukcji warunkowych, pętli.
Musiałbyś podać konkretny przypadek, dla podanego oczywiście pętlę można zastąpić zwykłym dzieleniem. Dla każdego przypadku rozwiązanie będzie inne.
Trick pokazany przez @gk1982 jest faktycznie często stosowany. Skok jest znacznie bardziej kosztowny niż dodawanie. Dodawanie x razy w pętli zamiast jednego powinno przyspieszyć, nawet jeśli po pętli będzie trzeba skorygować wynik wykonując (x-1) odejmowań. Nie wiem jak jest na nowoczesnych procesorach ale operacje na liczbach zmiennoprzecinkowych były swego czasu też wolne. Możesz na początku przesunąć przecinek o 15 pozycji w prawo w pętli i warunku i operować na liczbach całkowitych dopóki się mieszczą w zakresie. Zyskasz być może prędkość, ale na pewno precyzję. Ze zmiennoprzecinkowymi jest jeszcze ten problem że czasami po dodawaniu niektórych wartości (zwłaszcza bardzo małych do dużych) wynik może nie ulegać zmianie - masz wtedy nieskończoną pętlę.

Można też użyć bardziej zaawansowanych instrukcji procesora, liczących na macierzach i wektorach. W tym samym czasie w którym dodajesz do siebie dwie liczby, procesor jest w stanie dodać do siebie 4 lub więcej par liczb na tym samym jednym rdzeniu. Musisz tylko inaczej przygotować dane i użyć innej instrukcji. https://stackoverflow.blog/2020/07/08/improving-performance-with-simd-intrinsics-in-three-use-cases/

Jeśli chodzi o eliminację skoków to gorsze od skoków są źle przewidziane przypadkowe skoki. Procesor zgaduje czy skok nastąpi i dekoduje instrukcje naprzód na podstawie tego, jeśli nie zgadnie to musi się cofnąć. Dlatego ustawienie danych tak żeby warunki skoków były spójne w kolejnych iteracjach (przykładowo przez posortowanie wartości) może znacznie przyspieszyć algorytm: https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array

Pokaż konkretny, a nie wymyślony przypadek to będzie można kombinować.

2
km1606 napisał(a):

Moje drugie pytanie jest takie.
Czy w innym języku programowania idzie zwiększyć
szybkość działania pętli?

C++ (zwłaszcza gdy jak tutaj, nie ma użycia alokowanych obiektów, kontenerów) jest jednym z najszybszych języków. Przyjmij w praktyce, ze najszybszy.

obscurity napisał(a):

Pokaż konkretny, a nie wymyślony przypadek to będzie można kombinować.

+1
Dokładnie, OP jest szalenie niekonkretny.

Kilka uwag:
zrównoleglenie: (w sumie obojętne CPU czy GPU) trzeba wiedzieć, czego się spodziewać. Sprzed wielu lat mam pamiętam liczby: zatrudnienie drugiego rdzenia: zysk 50-60% (mnożnik 1.5-1.6), czterech mnożnik 2.5
Mowa o zrównolegleniu, które ma znaczny wkład profesjonalizmu.
Jeśli zrównoleglimy "prostacko" (co za adekwatne słwo), moze się okazać, że 90% pary pójdzie w gwizdek, na zarządzanie i synchronizacją, czy w skutek konkurowania rdzeni o cache

algorytmika od tego nie ucieknie. Właściwy algorytm dla każdej konkretnej sytuacji, dopasowana struktura danych (model danych)

Na razie @km1606 wyłącznie ukrywasz informacje i tupiesz nogą jak przedszkolak. Dasz rzeczwywistą sytuację, może będą wartościowe odpowiedzi.
ps. pozwoliłem sobie zerknąć w twoją historię - obawiam się, że musisz się troszkę cofnąć w ambicjach i uzupełnić fundamenty

2
obscurity napisał(a):

Ze zmiennoprzecinkowymi jest jeszcze ten problem że czasami po dodawaniu niektórych wartości (zwłaszcza bardzo małych do dużych) wynik może nie ulegać zmianie - masz wtedy nieskończoną pętlę.

... a o wiele wcześniej dokładność leci na pysk. Tzn "coś tam dodaje" ale trudno powiedzieć jaki się przenosi na wynik.
Kolejny powód by głęboko przemyśleć plan, co chcemy

0

Wielkie dzięki za odpowiedzi, będę kolejno testował przedstawione sposoby.

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