Optymalizacja obliczeń zmiennoprzecinkowych

Optymalizacja obliczeń zmiennoprzecinkowych
1

Hej, mam dwa pliki z których wczytuje ponad milion liczb zmiennoprzecinkowych, następnie wykonuje na nich obliczenia( parę pętli for itp). Niestety trwa to dość długo. Dane wczytuje do listy i potem wykonuje operacje na liście. Chciałem zapytać czy da się to jakoś przyśpieszyć? czy np numpy Array rzeczywiście szybciej by sobie radził ze wszystkim? Operacje na listach to głownie iteracja, dodawanie elementow, odejmowanie

edytowany 1x, ostatnio: Patryk27
CD
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 2 lata
  • Postów:20
0

Tak, numpy będzie wykonywał te operacje szybciej. Liczby w czystym Pythonie to obiekty, a operacje na obiektach wprowadzają pewien narzut, zarówno pamięciowy jak i obliczeniowy. Podobnie listy, w czystym Pythonie służą do przechowywania wielu rodzajów obiektów, niekoniecznie jednorodnych, przez co Python nie może łatwo optymalizować operacji na nich. numpy.Array jak i reprezentacja float w numpy są zoptymalizowane pod takie właśnie zastosowania.

0

Dzięki wielkie, to zabieram się za ogarnianie numpy

Guaz
numpy to podstawa jak napisał użytkownik powyżej. Ale drugie tyle można uzyskać odpowiednią optymalizacją kodu - np. pracując na wyrywkach list. Kolejny fast improvement to o ile jest taka szansa, przemnożenie wartości zmiennoprzecinkowych do całkowitych i pod koniec dopiero podzielenie ich do wyniku. Z tym że to potrafi przy najmniejszej pomyłce stworzyć wiele błędów i wymaga dobrej znajomości wejścia i metod konwersji. Ale działania na całkowitych są dużo szybsze. Najlepiej byłoby na przykładzie powiedzieć o możliwej optymalizacji tego.
superdurszlak
  • Rejestracja:prawie 7 lat
  • Ostatnio:około 7 godzin
  • Lokalizacja:Kraków
  • Postów:2000
1

Nie korzystałem (jeszcze) z tych dwóch narzędzi, ale zamiast/obok numpy możesz też wypróbować:

  • Dask jeżeli wykonywane przez Ciebie operacje da się zrównoleglić

  • Numba do kompilacji w locie krytycznych fragmentów kodu Pythona

Myślę że dla dużych ilości danych i dużych ilości obliczeń korzyści powinny być większe niż narzut związany ze zrównoleglaniem / kompilacją. Według tego wpisu autorowi udało się uzyskać przyspieszenie ponad 60x dla jego przypadku, przy połączeniu sił obydwu.


edytowany 1x, ostatnio: superdurszlak
0

podepnę się, bo to podobny problem chyba
Mam kod:
``
tol = 5
dane = [dane2.index(d2) for d1, d2 in zip(dane1, dane2) if -tol < d1 < tol]

``
Niestety kiedy dane sa listami zawierającymi okolo kilkuset lub ponad milion liczb to program trwa strasznie wolno. Zauważyłem ze operacja .index() tak zamula. Czy da sie jakos w inny szybszy sposob dostac indexy ?

AF
  • Rejestracja:prawie 7 lat
  • Ostatnio:4 dni
  • Postów:172
0
Waz smutny napisał(a):

podepnę się, bo to podobny problem chyba
Mam kod:
``
tol = 5
dane = [dane2.index(d2) for d1, d2 in zip(dane1, dane2) if -tol < d1 < tol]

``
Niestety kiedy dane sa listami zawierającymi okolo kilkuset lub ponad milion liczb to program trwa strasznie wolno. Zauważyłem ze operacja .index() tak zamula. Czy da sie jakos w inny szybszy sposob dostac indexy ?

Pamiętaj, że index iteruje listę aż znajdzie taki sam item. Czyli masz O(n) operacji. Dodatkowo jeżeli masz taką sytuację:
d2 = [1,2,3,4,5,1]
to Twoja lista dla d2[-1] zwróci index 0.. bo index() wykryje 1 na początku i wyjdzie.

Aby uniknąć iterowania listy ponownie ( w celu szukania indexu) po prostu rób enumerate:

dane = [index for index, (dane1, dane2) in enumerate(zip(dane1, dane2)) if -tol < d1 < tol]

hurgadion
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad 6 lat
  • Lokalizacja:www
  • Postów:259
0

Hej,
jeżeli dobrze zrozumiałem, to może wystarczy odpowiednio posortować i zastosować binary search, powinno działać dużo szybciej.

edytowany 1x, ostatnio: hurgadion
0

Niestety nie mogę posortować danych , ponieważ są to próbki czasu i sygnału z czujnika. W takim razie podczas wykonywania programu nie pozostaje nic innego jak pójść na kawę :)

hurgadion
to dobierz się do danych w trakcie przejmowania sygnału i zapisuj w locie, tak aby dane były posortowane, powodzenia :)
AF
A widziałeś mój komentarz wyżej? Swoją drogą dlaczego próbki czasu trzymasz osobno od sygnału z czujnika? (tak to rozumiem). To wygląda tak, jakbyś gdzieś wypełniał dane1 danymi z czujnika i gdzieś indziej dane2 timestampami (z tym samym okresem co pomiar czujnika).
hurgadion
@AsterFV: coś chyba ściemnia, ale nieistotne :)
AF
@hurgadion: Albo chce mieć powód na przerwę na kawę..a w Pythonie to nie zadziała: ;) https://xkcd.com/303/
hurgadion
to niech zanurkuje w embedded ;)
0

"""
A widziałeś mój komentarz wyżej? Swoją drogą dlaczego próbki czasu trzymasz osobno od sygnału z czujnika? (tak to rozumiem). To wygląda tak, jakbyś gdzieś wypełniał dane1 danymi z czujnika i gdzieś indziej dane2 timestampami (z tym samym okresem co pomiar czujnika). - AsterFV 4 minuty temu
"""

Tak, zastosowałem to z enumeratem ale w dalszym ciągu dla danych około 1mln trwa to około 7 minut. Właśnie tak to wygląda jak napisałeś, mam przetwornik analogowo-cyfrowy który mierzy z odpowiednim krokiem i w jednej tablicy zapisuje wartości sygnału a w drugiej odpowiadająca mu chwile czasu. Ja potem te dane sobie zipuje i przeszukuje i szukam kiedy sygnał przekracza pewien próg , a ze sygnał jest okresowy to co jakiś czas przekracza próg i ja chciałbym mieć wszystkie te przekroczenia odnotowane. np czasem jest ich 2500, a dla sygnału o mniejszej częstotliwości jest ich np 500 ( ja chciałbym notować chwile czasu kiedy nastąpiło przekroczenie)

AF
  • Rejestracja:prawie 7 lat
  • Ostatnio:4 dni
  • Postów:172
0

Bardziej mi chodziło o to, że enumerate to jedyna opcja uzyskania poprawnych indexów (bo index() zwróci pierwszy z brzegu w liście).

Można poprawić architekturę. Nie ma sensu zapisywać timestampu osobno - timestamp ma tylko sens razem z wartością więc mamy przykład dla tuple:
[(value, timestamp), (value, timestamp)...]. Dzięki temu nie musisz nic zipować potem. I unikasz sytuacji, gdzie dane mogą ulec uszkodzeniu (np. jedna lista zostanie przesunięta... i wszystkie próbki z listy drugiej będą przypisane do złych timestampów).Teraz możesz podjąć decyzję:

  1. Zapisujesz wszystko (bo się przyda gdzieś) i potem filtrujesz. Zgodnie z sugestią @hurgadion możesz zapisywać sortując po wartości i póżniej łatwo wybrać przedział zgodnie z Twoim treshold (a nastepnie posortować po timestampie by dane miały sens przy analizie).
  2. Albo filtrujesz przy zapisie.

Jeżeli pomiar ma służyć tylko do zanotowania przekroczeń nie ma sensu notować chwil, dla których przekroczeń nie ma.

edytowany 1x, ostatnio: AsterFV
0
AsterFV napisał(a):

Bardziej mi chodziło o to, że enumerate to jedyna opcja uzyskania poprawnych indexów (bo index() zwróci pierwszy z brzegu w liście).

Można poprawić architekturę. Nie ma sensu zapisywać timestampu osobno - timestamp ma tylko sens razem z wartością więc mamy przykład dla tuple:
[(value, timestamp), (value, timestamp)...]. Dzięki temu nie musisz nic zipować potem. I unikasz sytuacji, gdzie dane mogą ulec uszkodzeniu (np. jedna lista zostanie przesunięta... i wszystkie próbki z listy drugiej będą przypisane do złych timestampów).Teraz możesz podjąć decyzję:

  1. Zapisujesz wszystko (bo się przyda gdzieś) i potem filtrujesz
  2. Albo filtrujesz przy zapisie.

Jeżeli pomiar ma służyć tylko do zanotowania przekroczeń nie ma sensu notować chwil, dla których przekroczeń nie ma.

Niestety nie mogę zapisywać od razu do krotki bo jest nagrywane kilka sygnałów i czas jest przechowywany osobno w słowniku i w tym słowniku tez osobno są tez sygnały i dla każdego jest ten sam czas . Notowany jest cały sygnał bo następnie jest przedstawiany na wykresie oraz te punkty w których nastąpiło przecięcie. Aktualnie ja już sobie poradziłem ze wszystkim , tylko szukam teraz optymalizacji czasowej.

hurgadion
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad 6 lat
  • Lokalizacja:www
  • Postów:259
0

To może inaczej. Jeżeli dane pakujesz do słownika, to przy pakowaniu od razu podziel na jakieś podprzedziały, będziesz miał mniej wyszukiwania... Zrób takie hashowanie (tylko trzeba dobrać odpowiednie parametry), wtedy łatwiej będzie znaleźć i chyba szybciej to co Ci potrzebne, np. dla danych:

Kopiuj
a = [10, 12, 13, 30, 10, 23, 24, 12, 14, 7, 23, 24, 45, 34, 45, 78, 12]

mamy takie hashowanie (V jest słownikiem słowników):

Kopiuj
V[0] = {7: [9]}
V[10] = {10: [0], 12: [1, 7, 16], 13: [2], 10: [4], 14: [8]}
V[20] = {23: [5, 10], 24: [6], 24: [11]}
V[30] = {30:[3], 34: [13]}
V[40] = {45: [12, 14]}
V[50] = {}
V[60] = {}
V[70] = {70: [15]}

Wtedy dla przedziału 5-15 mamy formułkę:

Kopiuj
indexes = []
a = 10 * (5 // 10)
b = 10 * (15 // 10) + 10
for elem in V[a].keys():
    if V[a][elem]>=5:
       indexes += V[a][elem]
for elem in V[b].keys():
    if if V[b][elem]<=15:
        indexes += V[b][elem]

Taka segregacja danych powinna sporo przyspieszyć.

edytowany 1x, ostatnio: hurgadion
hurgadion
a jak masz w uj danych, to możesz zrobić hashowanie hashowania, albo hashowanie hashowania hashowania, itd. :)
CS
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 10 godzin
  • Postów:296
1

Dziwne, dla sygnału rzędu 1 miliona próbek czas obliczeń powinien być w sekundach, 5-20, w zależności od stopnia złożoności algorytmu wyznaczającego przejście przez zero. Dziwne jest też to zapisywanie czasu, kompletnie niepotrzebne przy próbkowaniu równomiernym. Algorytmy pomiarowe operują na czasie dyskretnym, czyli wyłącznie na indeksach. Czas oblicza się mnożąc numer indeksu przejścia przez zero przez okres próbkowania plus ewentualnie czas startu rejestracji sygnału. Dla przykładu program JavaScript operujący naiwnym algorytmem szukania miejsc przejścia przez zero, który skleciłem w 30 minut, potrzebuje prawie 10 razy więcej czasu na generowanie niż na same obliczenia trwające około 5-6 sekund dla 1000k próbek.

edytowany 1x, ostatnio: cs
0

Problem rozwiązałem, dzięki wielkie za pomoc, działa już ładnie i szybko.

Zamiast :

time_idx = [time.index(tim) for sig, tim in zip(signal, time) if -tol < sig < tol]

napisałem:

time_idx= [x for x in range(len(signal)) if -tol < signal[x] < tol]

gdzie signal , tol to parametry funkcji.

hurgadion
skoro wystarczy, to nie ma się co zagłębiać :)
enedil
  • Rejestracja:prawie 12 lat
  • Ostatnio:8 dni
  • Postów:1027
1

Nowa wersja jest nie tylko szybsza - ona jest także przynajmniej poprawna. Jeśli signal = [2, 2, 3, 4, 5, 2], to stara wersja dla tol = 8 by zwróciła [0, 0, 2, 3, 4, 0], a nowa, zgodnie z oczekiwaniami [0, 1, 2, 3, 4, 5].

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)