Temat: LabVIEW - zrównoleglanie czasochłonnych operacji

Załóżmy, że mamy program, w którym dokonujemy akwizycji danych, ich przetwarzania oraz zapisu wyników do pliku. Każda z tych operacji charakteryzuje się określonym czasem trwania. Dla lepszego zobrazowania symulujemy poszczególne operacje przy pomocy podprogramów przedstawionych poniżej.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8alal71t2a6ke9p8aop3.png
Rysunek 1 Akwizycja danych, czas trwania 20ms

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8al1t9d33n199p466ua34.png
Rysunek 2 Przetwarzanie danych, czas trwania 50ms

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8am14ri13q2umm1p8eii35.png
Rysunek 3 Logowanie wyników, czas trwania 20ms

Jeżeli umieścimy poszczególne operacje w jednej pętli while tak, aby były wykonywane sekwencyjnie, całkowity czas wykonania pojedynczej iteracji pętli będzie określony przez sumę czasów wykonania poszczególnych podprogramów, wyniesie więc 90ms.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8amj9f1pnqkvh18udgm6.png
Rysunek 4 Podprogramy wykonywane sekwencyjnie, pomiar czasu wykonania pojedynczej iteracji pętli

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8ang49ts5cvege11glh7.png
Rysunek 5 Panel frontowy programu, wynik pomiaru czasu wykonania

Taka sytuacja może być dla nas bardzo niepożądana, jeżeli zależy nam na tym, aby akwizycja danych wykonywana była przy najmniejszym możliwym cyklu czasowym. Przy powyższym rozwiązaniu, cykl ten jest ponad 4-krotnie dłuższy.
Rozwiązaniem tego problemu jest zrównoleglenie wykonania poszczególnych operacji poprzez umieszczenie każdego z podprogramów w osobnej, działającej równolegle pętli while.

W tym celu tworzymy na diagramie blokowym dwie dodatkowe pętle while, jako warunek stopu do każdej z nich podpinamy zmienną lokalną dla przycisku Stop Button.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8ang1vn0o1ogn128lae78.png
Rysunek 6 Równoległe pętle while

Poszczególne podprogramy nie mogą działać jednak zupełnie niezależnie od siebie, gdyż są ściśle powiązane przepływem danych. W celu przekazania danych do poszczególnych podprogramów, wykorzystamy mechanizm kolejki FIFO (first in first out). Pętla dokonująca akwizycji danych umieszcza pozyskaną w podprogramie Acquire Data tablicę danych w kolejce, z której będą one pobierane w pętli przetwarzającej. Z kolei przetworzone dane są umieszczane w drugiej kolejce, z której następnie pobiera je pętla logująca do pliku.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8ao1g4g18rm12a4cou188r9.png
Rysunek 7 Pętle z kolejkami

W celu oprogramowania tego mechanizmu, przenosimy poszczególne podprogramy do osobnych pętli, a następnie wstawiamy na diagram blokowy dwa bloczki Obtain Queue, odpowiedzialne za utworzenie kolejek. Pierwszej kolejce nadajemy nazwę Acquired Data, drugiej Processed Data. Jako typ danych dla pierwszej kolejki podłączamy tablicę liczb typu double, natomiast w przypadku drugiej kolejki – pojedynczą liczbę typu double.

W pierwszej pętli (Acquisition) umieszczamy bloczek Enqueue Element, odpowiedzialny za dodanie elementu do kolejki. Na jego wejścia podpinamy kolejkę oraz tablicę wyjściową z podprogramu Acquire Data.

Elementy dodane do pierwszej kolejki, pobierane są w drugiej pętli (Processing). W tym celu umieszczamy w niej bloczek Dequeue Element, realizujący pobranie pierwszego elementu z kolejki. Element ten następnie podajemy na wejście podprogramu przetwarzającego, a wynik przetwarzania dodajemy do drugiej kolejki wykorzystując ponownie bloczek Enqueue Element.

Wyniki przetwarzania są pobierane z kolejki w pętli Logging w analogiczny sposób przy pomocy bloczka Dequeue Element.
Dodatkowo w każdej z pętli umieszczone zostały bloczki Tick Count, służące do pomiaru czasu iteracji pętli.
Po uruchomieniu programu możemy zauważyć, że poszczególne pętle pracują z najkrótszym możliwym cyklem czasowym.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8ao1544eqbpq1ijatpfa.png
Rysunek 8 Czasy cyklu każdej z pętli równoległych

Powyższe rozwiązanie nie jest jednak całkowicie poprawne, ponieważ naciśnięcie przycisku Stop powoduje natychmiastowe przerwania działania wszystkich pętli. Efektem tego jest brak przetworzenia i zapisu do pliku wszystkich wygenerowanych danych.

W celu zobrazowania tego efektu możemy dodać do każdej z pętli liczniki iteracji i porównać  ich wartości po zakończeniu programu.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8aopfkble81m128812ajb.png
Rysunek 9 Pętle równoległe z licznikami iteracji

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8aoi4o93fdei06r21c.png
Rysunek 10 Rezultat działania programu po jego zatrzymaniu

Jeżeli zależy nam na tym, aby wszystkie pozyskane dane zostały przetworzone przed zakończeniem programu, nie możemy zatrzymywać wszystkich pętli w tym samym momencie czasowym.

Aby rozwiązać ten problem, oprócz danych do przetworzenia, do kolejki dodawać będziemy komunikaty, informujące pętle równoległe o czynnościach do wykonania. W sytuacji, gdy tą operacją jest przetworzenie lub zapis danych, do kolejki trafią dane wraz z komunikatem odpowiednio Process lub Log, natomiast w przypadku zakończenia, w kolejce umieszczone zostaną puste dane oraz komunikat Stop. Takie rozwiązanie zapewni nam działanie pętli równoległych do momentu przetworzenia wszystkich danych znajdujących się w kolejkach.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8aofou113c1rhq12atg3gd.png
Rysunek 11 Kolejki z komunikatami

W celu oprogramowania tego rozwiązania, zmieniamy typ danych kolejki na klaster złożony ze stałej Enum oraz odpowiednio tablicy i liczby typu double. Do przechowywania wszystkich możliwych komunikatów wykorzystujemy kontrolkę Enum, którą zapisujemy jako definicję typu, a po wykorzystaniu (utworzeniu poszczególnych stałych) usuwamy z diagramu blokowego.

W pętli Acqisition po pozyskaniu danych, pakujemy je w klaster (kontrolka Bundle) razem z komunikatem Process, a następnie tak przygotowany klaster wstawiamy do kolejki. W przypadku naciśnięcia przez użytkownika przycisku Stop Button, do kolejki wstawiamy klaster składający się z komunikatu Stop oraz pustej tablicy oraz zatrzymujemy działanie pętli.

W pętli Processing, pobieramy element z kolejki, a następnie rozpakowujemy klaster (kontrolka Unbundle). W zależności od wartości komunikatu, dokonujemy przetworzenia danych i wstawienia ich do kolejki z komunikatem Log lub zatrzymania pętli i wstawienia do kolejki pojedynczej liczby z komunikatem Stop dla pętli Logging.

W pętli Logging pobieramy w analogiczny sposób dane z kolejki, a w zależności od otrzymanego komunikatu zapisujemy je do pliku lub przerywamy działanie pętli.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8aof8f11s6k401u10s0te.png
Rysunek 12 Reakcja na komunikat Stop

Po uruchomieniu programu i naciśnięciu przycisku Stop możemy zauważyć, że licznik pierwszej pętli zostaje zatrzymany, natomiast pozostałe pętle działają dalej do momentu przetworzenia wszystkich danych z kolejki.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8ap1d89196lp0e1mjo5nf.png
Rysunek 13 Efekt działania programu z komunikatami

Opracowany w ten sposób program możemy dalej ulepszyć, stosując zamiast typu danych specyficznego dla każdej kolejki, typ Variant. Dzięki temu unikniemy konieczności definiowania typu dla każdej kolejki, zastępując go jednym uniwersalnym typem, będącym klastrem złożonym z komunikatu oraz elementu Variant.

W tym celu zmieniamy dane w klastrze na pustą stałą typu Variant, a w poszczególnych pętlach w momencie dodawania danych do kolejki korzystamy z bloczka To Variant. Natomiast pobierając dane z kolejki, przekształcamy typ Variant na pożądany typ danych przy pomocy bloczka Variant To Data.

http://www.student.mvlab.pl/wiedza/img/m/2/t/p181c2s8ap1aql17213om1v79j3dg.png
Rysunek 14 Wykorzystanie typu Variant

Propozycje ćwiczeń i modyfikacji
•    Zmodyfikować pętlę Acquisition tak, aby generowanie danych nie odbywało się w sposób ciągły, a zdarzeniowo z wykorzystaniem struktury Event
•    Dodać do programu paski postępu, informujące użytkownika o aktualnym stanie przetwarzania i logowania danych
•    Zapoznać się z dostępnym w LabVIEW szablonem aplikacji Queued Message Handler

Załączniki posta

Parallel VI.zip 56.67 kB, 845 pobrań od 2013-08-07