User Tools

Site Tools


No ACL setup yet! Denying access to everyone.
pl:edu:zmitac:n1z:ja:lab4

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
pl:edu:zmitac:n1z:ja:lab4 [2021/04/10 22:48] – pczekalskipl:edu:zmitac:n1z:ja:lab4 [2021/04/24 17:07] (current) – [Ćwiczenie 3: Integracja kodu niezarządzalnego, tworzonego w języku Assembler wraz z aplikacją typu WPF w .NET C#, instrukcje SIMD] pczekalski
Line 1: Line 1:
-===== Ćwiczenie 3: Integracja kodu niezarządzalnego, tworzonego w języku Assembler wraz z aplikacją typu WPF w .NET C#, instrukcje SIMD =====+===== Ćwiczenie 4: Integracja kodu niezarządzalnego, tworzonego w języku Assembler wraz z aplikacją typu WPF w .NET C#, instrukcje SIMD =====
  
  
Line 20: Line 20:
 W ramach niniejszego laboratorium użyjemy projektu analogicznego do projektu w laboratorium nr [[pl:edu:zmitac:n1z:ja:lab3|3]], przy czym zamiast aplikacji konsolowej w języku C# zostanie wykorzystany frontend w modelu WPF (aplikacja okienkowa). W ramach niniejszego laboratorium użyjemy projektu analogicznego do projektu w laboratorium nr [[pl:edu:zmitac:n1z:ja:lab3|3]], przy czym zamiast aplikacji konsolowej w języku C# zostanie wykorzystany frontend w modelu WPF (aplikacja okienkowa).
  
-==== WPF ====+==== WPF i assembler ====
 Windows Presentation Fundation (WPF) to w skrócie sposób tworzenia interfejsów użytkownika dla systemu Windows z wykorzystaniem środowiska .NET (C#, F#, VB.NET). WPF zastępuje poprzednie Windows Forms, związane z kodem niezarządzalnym i językiem C++. WPF cechuje się tym, że widok (layout, okienka i układ kontrolek) jest odseparowany od kodu zarządzającego zawartością i interakcją. Wygląd jest zapisany w postaci osobnych plików z rozszerzeniem ''.xaml'', a kod reprezentujący okienko jest w dużej mierze generowany "w locie" podczas edycji wyglądu w edytorze Visual Studio (tzw "Code Behind"). Odseparowanie wyglądu od kodu pozwala na tworzenie modyfikacji wyglądu przez grafików, bez udziału programistów (tworzenie tzw. skórek). Dla każdego widoku (okienka) są zatem tworzone co najmniej dwa pliki: jeden z kodem, drugi z plikiem XML (''.xaml'') reprezentującym położenie i cechy graficzne. WPF jest skalowalny i posiada możliwość tworzenia inerfejsów użytkownika, które automatycznie dostosowują się do proporcji i rozmiarów ekranu oraz ich rozdzielczości i wymiarów (DPI). Więcej informacji można znaleźć w oficjalnym portalu Microsoft((https://docs.microsoft.com/en-us/dotnet/desktop/wpf/getting-started/walkthrough-my-first-wpf-desktop-application?view=netframeworkdesktop-4.8)) lub na wielu innych stronach. WPF na dzień dzisiejszy jest wiodącym środowiskiem tworzenia aplikacji okienkowych dla Windows w środowisku .NET. Windows Presentation Fundation (WPF) to w skrócie sposób tworzenia interfejsów użytkownika dla systemu Windows z wykorzystaniem środowiska .NET (C#, F#, VB.NET). WPF zastępuje poprzednie Windows Forms, związane z kodem niezarządzalnym i językiem C++. WPF cechuje się tym, że widok (layout, okienka i układ kontrolek) jest odseparowany od kodu zarządzającego zawartością i interakcją. Wygląd jest zapisany w postaci osobnych plików z rozszerzeniem ''.xaml'', a kod reprezentujący okienko jest w dużej mierze generowany "w locie" podczas edycji wyglądu w edytorze Visual Studio (tzw "Code Behind"). Odseparowanie wyglądu od kodu pozwala na tworzenie modyfikacji wyglądu przez grafików, bez udziału programistów (tworzenie tzw. skórek). Dla każdego widoku (okienka) są zatem tworzone co najmniej dwa pliki: jeden z kodem, drugi z plikiem XML (''.xaml'') reprezentującym położenie i cechy graficzne. WPF jest skalowalny i posiada możliwość tworzenia inerfejsów użytkownika, które automatycznie dostosowują się do proporcji i rozmiarów ekranu oraz ich rozdzielczości i wymiarów (DPI). Więcej informacji można znaleźć w oficjalnym portalu Microsoft((https://docs.microsoft.com/en-us/dotnet/desktop/wpf/getting-started/walkthrough-my-first-wpf-desktop-application?view=netframeworkdesktop-4.8)) lub na wielu innych stronach. WPF na dzień dzisiejszy jest wiodącym środowiskiem tworzenia aplikacji okienkowych dla Windows w środowisku .NET.
  
Line 33: Line 33:
 Po stworzeniu projektu pojawi się okienko graficznego projektowania interfejsu użytkownika wraz ze skojarzonym plikiem ''.xaml''. Dane w pliku opisują wygląd okienka, a zatem interakcja po stronie okienka (np. dołożenie kontrolek) powoduje automatyczną modyfikację treści pliku ''.xaml''. Zależność ta jest prawdziwa również w drugim kierunku, czyli ręczne wpisanie tekstu do pliku ''.xmal'' spowoduje zmianę wyglądu interfejsu użytkownika. \\Główne okienko aplikacji automatycznie otrzymuje nazwę ''MainWindow'', a zatem związany z nim plik opisujący będzie miał oznaczenie ''MainWindow.xaml'', a kod w języku C# (tzw. Code Behind) odpowiednio ''MainWindow.xaml.cs''.  Po stworzeniu projektu pojawi się okienko graficznego projektowania interfejsu użytkownika wraz ze skojarzonym plikiem ''.xaml''. Dane w pliku opisują wygląd okienka, a zatem interakcja po stronie okienka (np. dołożenie kontrolek) powoduje automatyczną modyfikację treści pliku ''.xaml''. Zależność ta jest prawdziwa również w drugim kierunku, czyli ręczne wpisanie tekstu do pliku ''.xmal'' spowoduje zmianę wyglądu interfejsu użytkownika. \\Główne okienko aplikacji automatycznie otrzymuje nazwę ''MainWindow'', a zatem związany z nim plik opisujący będzie miał oznaczenie ''MainWindow.xaml'', a kod w języku C# (tzw. Code Behind) odpowiednio ''MainWindow.xaml.cs''. 
  
-Przykładowy interface użytkownika, który będzie odpowiedni na potrzeby niniejszego projektu zaprezentowano na rysunku {{ref>asm4_2}}. Każdej kontrolce, do której będzie bezpośrednio odwoływać się kod (tutaj pola typu ''TextBox'') należy nadać unikalną nazwę. Nadawanie nazw odbywa się w okienku właściwości ([Properties]) obiektu, które na rysunku {{ref>asm4_2}} domyślnie znajduje się w prawym dolnym rogu interfejsu użytkownika Visual Studio.+Podczas projektowania, nowe elementy najłatwiej dodać, przeciągając je z menu [Toolbox] (na rysunku {{ref>asm4_2}} po lewej stronie).\\ Na potrzeby naszej aplikacji wystarczą typy ''TextBox'' (nie pomylić z ''TextBlock''!) oraz ''Button''.\\
  
-<figure asm4_2>+Przykładowy interface użytkownika, który będzie odpowiedni na potrzeby niniejszego projektu zaprezentowano na rysunku {{ref>asm4_2}}. Każdej kontrolce, do której będzie bezpośrednio odwoływać się kod (tutaj pola typu ''TextBox'') należy nadać unikalną nazwę. Nadawanie nazw odbywa się w okienku właściwości ([Properties]) obiektu, które na rysunku {{ref>asm4_2}} znajduje się w prawym dolnym rogu interfejsu użytkownika Visual Studio. Wprowadzenie nazwy ma automatycznie swoje odzwierciedlenie w pliku opisowym ''.xaml''.
  
 +<figure asm4_2>
 +{{:pl:edu:zmitac:n1z:ja:wpf_textboxes.png?600|}}
 <caption>Przykładowy interface użytkownika</caption> <caption>Przykładowy interface użytkownika</caption>
 </figure> </figure>
  
-<note important>Uwaga! Okienko właściwości obiektu graficznego to działa w dwóch trybach, przełączanych za pomocą przycisków znajdujących się na górze, po prawej stronie okienka: klucz oznacza tryb zmiany właściwości (Properties), a błyskawica tryb zmiany akcji (Events). </note>+<note important>Uwaga! Okienko właściwości obiektu graficznego działa w dwóch trybach, przełączanych za pomocą przycisków znajdujących się na górze, po prawej stronie okienka: klucz oznacza tryb zmiany właściwości (Properties), a błyskawica tryb zmiany akcji (Events). Na rysunku {{ref>asm4_2}} miejsce to zaznaczono czerwoną strzałką.</note>
  
-Przełączenie z widoku projektowania interfejsu do kodu C# (Code Behind) najprościej wykonać używając klawisza [F7]. Ewentualnie mozna go wybrać z drzewa projektu (rysunek {{ref>asm4_3}}).+W przypadku przycisków, nie jest konieczne nadawanie im nazwy, natomiast konieczne jest utworzenie akcji związanej z kliknięciem przycisku. Akcja ta (Event) zostanie wywołana po kliknięciu danego przycisku. Funkcje przypisane do kolejnych przycisków muszą mieć unikalne nazwy, ponieważ wszystkie są umieszczone w klasie widoku okienka głównego.\\ 
 +Akcję można stworzyć na kilka sposobów: 
 +  * Jednym z nich jest dwukrotne kliknięcie przycisku w edytorze graficznym. Metoda ta nie pozwala jednak na wybranie nazwy funkcji. 
 +  * Drugim jest przełączenie okienka dialogowego właściwości przycisku w tryb edycji akcji (znaczek błyskawicy, jak na rysunku {{ref>asm4_4}}) i wpisanie nazwy funkcji. 
 +  * Jest też metoda ręcznej edycja pliku ''.xaml'' oraz ''.xaml.cs'' - metoda ta nie jest polecana bo łatwo tutaj popełnić błąd. 
 +<figure asm4_4> 
 +{{:pl:edu:zmitac:n1z:ja:wpf_buttons.png?600|}} 
 +<caption>Przełączenie okienka właściwości w tryb edycji akcji</caption> 
 +</figure>
  
-<figure asm4_3>+Dla akcji wykonywanej po kliknięciu przycisku, należy wypełnić pole "Click" (rysunek {{ref>asm4_5}}): 
 +<figure asm4_5> 
 +{{:pl:edu:zmitac:n1z:ja:wpf_button_on_click.png?600|}} 
 +<caption>Akcja ''btPlusOnClick''</caption> 
 +</figure> 
 + 
 +<note tip>Wskazane jest, aby nazwa akcji (nazwa funkcji wywoływanej po pojawieniu się interakcji) odzwierciedlała naturę tej akcji oraz odnosiła się do obiektu, którego dotyczy, bowiem w kodzie zarządzającym okienkiem (Code Behind) nie ma bezpośredniego kontekstu i odwołania do obiektu graficznego. używanie nazw typu ''funkcja1'' nie niesie ze sobą informacji ani w jakich okolicznościach ta funkcja jest wywoływana, ani dla jakiego obiektu.</note> 
 + 
 +<note tip>Przełączenie z widoku projektowania interfejsu do kodu C# (Code Behind) najprościej wykonać używając klawisza [F7]. Ewentualnie mozna go wybrać z drzewa projektu (rysunek {{ref>asm4_6}}).</note> 
 + 
 +<figure asm4_6>
 {{:pl:edu:zmitac:n1z:ja:wpf_code_behind.png?600|}} {{:pl:edu:zmitac:n1z:ja:wpf_code_behind.png?600|}}
 <caption>Code Behind - kod zarządzający akcjami okienka</caption> <caption>Code Behind - kod zarządzający akcjami okienka</caption>
 </figure> </figure>
  
-Podczas projektowania, nowe elementy najłatwiej dodać, przeciągając je z menu [Toolbox].\\ Na potrzeby naszej aplikacji wystarczą typy ''TextBox'' (nie pomylić z ''TextBlock''!) oraz ''Button'' ({{ref>asm4_4}} i {{ref>asm4_5}}):+Odwołanie z poziomu kodu do obiektów graficznych jest realizowane poprzez nazwę instancji obiektu graficznego. Wszystkie kontrolki typu ''TextBox'' dostarczają zawartość wpisaną w kontrolkę w postaci tekstu (''String''), w polu ''Text''. W naszym przykładie interesują nas jednak dane numeryczne. W związku z tym istnieje konieczność konwersji typów z typu ''String'' to typu numerycznego (tutaj ''Float'', analogicznie dla innych typów). 
 +Służy do tego funkcja ''<typ>>.TryParse(...)''. Nietypowo, funkcja ta nie zwraca wartości bezpośrednio, lecz poprzez parametr typu ''out'' co jest wymagane jawnie podczas wywołania: 
 +<code c> 
 +   ... 
 +   float.TryParse(tbA1.Text, out aArg1[0]); //tbA1 - pole tekstowe, aArg1 - tablica typu float 
 +   ... 
 +</code>
  
 +Jeśli konwersja nie może zostać dokonana, ponieważ nie da się "przetłumaczyć" łańcucha na zmienną określowego typu, zostanie zgłoszony wyjątek i wykonywanie programu zostanie przerwane. Oczywiście istnieją mechanizmy walidacji, które pozwalają na wykrycie tego typu sytuacji oraz ich obsługę, niemniej jednak wykraczają one poza ramy niniejszej instrukcji.
 +
 +Aby zaprezentować wynik, należy wykonać operację odwrotną: zamienić zmienną numeryczną na łańcuch znaków (''String'') i przypisać wartość polu ''Text'' wskazanej kontrolki. W tym celu należy użyć funkcji ''ToString()'' wywoływanej "na" wskazanej zmiennej, np. tak:
 +<code c>
 +...
 +tbC4.Text = aResults[3].ToString();
 +...
 +</code>
 +
 +=== Pozostałe ustawienia solucji ===
 +W projekcie WPF należy włączyć możliwość kompilacji kodu typu ''unsafe'' oraz debbugowanie kodu natywnego, analogicznie jak to opisano w instrukcji laboratoryjnej [[pl:edu:zmitac:n1z:ja:lab3|Ćwiczenie 3: Integracja kodu niezarządzalnego, tworzonego w języku Assembler wraz z kodem zarządzalnym .NET C#]]. 
 +
 +Należy też pamiętać o wybraniu wspólnego folderu do generowania wynikowego kodu z projektu w assemblerze i projektu w języku C#, analogicznie jak to opisano w instrukcji laboratoryjnej [[pl:edu:zmitac:n1z:ja:lab3|Ćwiczenie 3: Integracja kodu niezarządzalnego, tworzonego w języku Assembler wraz z kodem zarządzalnym .NET C#]]. W przypadku projektów .NET, folder, do którego wykonuje się kompilację ustawia się we właściwościach projektu (prawym przyciskiem myszy należy kliknąć nagłówek projektu, a następnie wybrać "Properties" ("Właściwości"), i dalej w sekcji "Build" w grupie "Output path:" należy wskazać docelową ścieżkę (rysunek {{ref>asm4_10}}). Aby móc wybrać folder, musi on istnieć na dysku, zatem wskazane jest przeprowadzenie "pustej" kompilacji wyłącznie projektu DLL, aby folder ten został utworzony.
 +
 +<figure asm4_10>
 +{{:pl:edu:zmitac:n1z:ja:buildtargetwpf.png?600|}}
 +<caption>Ustawienie docelowego folderu na wyniki kompilacji projektu WPF</caption>
 +</figure>
 +
 +W przypadku, gdyby pomimo ustawienia wspólnego folderu wynikowego nadal były zgłaszane błędy braku biblioteki DLL, istnieje możliwość, że włączony jest system tworzenia dodatkowych podfolderów, jak to opisano poniżej w notce z uwagami. Aby usunąć ten problem, konieczne jest ręczna edycja pliku konfiguracyjnego projektu.\\ Aby otworzyć plik konfiguracyjny projektu, należy dwukrotnie klinąć nazwe projektu w drzewie. Otworzy się plik typu XML, z rozszerzeniem ''.csproj''.
 +
 +<note warning>Uwaga! W przypadku projektu WPF w folderze wynikowym może pojawić się podfolder z wersją frameworku .NET, co w praktyce spowoduje, że plik wykonywalny z frontside (GUI) zostanie uruchomiony w innym folderze (w podfolderze) w stosunku do wybranego w ustawieniach, skutkiem czego biblioteka DLL nie zostanie załadowana i zostanie zgłoszony wyjątek. Podfoldery te są dodawane automatycznie. Aby usunąć tę niedogodność, w pliku projektu WPF należy mieścić dodatkową sekcję((https://docs.microsoft.com/en-us/visualstudio/ide/how-to-change-the-build-output-directory?view=vs-2019)):
 +<code xml>
 +
 +<PropertyGroup>
 +  <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
 +  <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
 +</PropertyGroup>
 +
 +</code>
 +</note>
 +
 +
 +
 +=== Assembler ===
 +Do solucji należy dodać projekt w assemblerze. Kod w assemblerze należy skompilować do biblioteki DLL, a następnie dołączyć ją dynamicznie do rozwiązania w C#. Jest to dokładnie ten sam proces, który został opisany w  [[pl:edu:zmitac:n1z:ja:lab3|Ćwiczenie 3: Integracja kodu niezarządzalnego, tworzonego w języku Assembler wraz z kodem zarządzalnym .NET C#]] w zakresie tworzenia i konfiguracji części assemblerowej oraz jej udostępniania po stronie C#, natomiast zamiast aplikacji konsolowej występuje aplikacja WPF.
 ==== Zadanie do wykonania ==== ==== Zadanie do wykonania ====
-Należy utworzyć prosty kalkulator, który przeliczy wektorowo dla 4 zmiennych wyniki dla operacji dodawania, odejmowania, mnożenia i dzielenia (akcje na osobnych przyciskach). Wymagane jest przeliczenie więcej niż jednego zestawu danych w ramach jednej instrukcji dodawania (trzeba skorzystać z SIMD). Każde zadanie należy wykonać w dwóch wariantach: z nasyceniem i bez nasycenia.+Należy utworzyć prosty kalkulator, który przeliczy wektorowo dla 4 zmiennych wyniki dla operacji dodawania, odejmowania, mnożenia i dzielenia (akcje na osobnych przyciskach). Wymagane jest przeliczenie więcej niż jednego zestawu danych w ramach jednej instrukcji dodawania (trzeba skorzystać z SIMD). Zadanie należy wykonać na typach zmiennoprzecinkowych, pojedynczej precyzji (32 bity, po stronie C# typ ''float'').
  
 Należy zadeklarować po jednej funkcji assemblerowej dla każdej operacji. Należy zadeklarować po jednej funkcji assemblerowej dla każdej operacji.
Line 58: Line 120:
 Wartości najwygodniej będzie przekazać do funkcji assemblerowych wpisując je po stronie kodu w C# do dwóch tablic źródłowych, a na wyniki zaalokować tablicę trzecią. Liczba zmiennych jest znana (4) i stała, zatem długości tablic nie trzeba przekazywać do funkcji assemblerowej. Wskaźniki na tablice można przekazać poprzez rejestry lub poprzez wskaźnik na strkturę zawierającą wskaźniki do ww. tablic (do wyboru). Wartości najwygodniej będzie przekazać do funkcji assemblerowych wpisując je po stronie kodu w C# do dwóch tablic źródłowych, a na wyniki zaalokować tablicę trzecią. Liczba zmiennych jest znana (4) i stała, zatem długości tablic nie trzeba przekazywać do funkcji assemblerowej. Wskaźniki na tablice można przekazać poprzez rejestry lub poprzez wskaźnik na strkturę zawierającą wskaźniki do ww. tablic (do wyboru).
  
 +Tablice po stronie C# można zadeklarować np następująco:
 +<code c>
 +...
 +public partial class MainWindow : Window
 +    {
 +        public float[] aArg1 = { 0, 0, 0, 0 }; //argument 1
 +        public float[] aArg2 = { 0, 0, 0, 0 }; //argument 2
 +        public float[] aResults = { 0, 0, 0, 0 }; //wynik
 +...
 +</code>
 +
 +<note tip>Powyżej dołączono kontekst: zmienne zostały umieszczone w klasie okienka. Słowo kluczowe ''partial'' w deklaracji typu klasy MainWindow oznacza, że w pliku zaimplementowana jest jedynie część klasy. Pozostała część kodu znajduje się w pliku zarządzanym automatycznie przez edytor GUI okienka. Dla dewelopera w praktyce ten mechanizm jest przezroczysty, a cały kod należy umieszczać w tym pliku (''.xaml.cs'').</note>
 +
 +<note important>Część instrukcji SIMD (SSE, AVX) wymaga specyficznego ułożenia danych w pamięci, w szczególności wyrównania (umieszczenia zmiennej pod adresem podzielnym przez 16). C# nie daje gwarancji umieszczenia początkowego elementu w pamięci pod adresem zgodnym z wymaganiami kodu niezarządzalnego (pomimo wyrównywania zmiennych do 64 bitów). W związku z tym, przy dostępie do pamięci, nie należy stosować instrukcji assemblerowych, wymagających wyrównania, w szczególności np. nie należy stosować ''MOVAPS'', tylko jej wolniejszą wersję, nie wymagającą specyficznego wyrównania zmiennych w pamięci, czyli ''MOVUPS''. </note>
 +
 +<note warning>Użycie instrukcji wymagającej wyrównania może powodować, że program będzie losowo działał i rzucał wyjątek (nawet w kolejnych uruchomieniach będzie zachowywał się niepowtarzalnie), ponieważ może się tak zdarzyć, że zmienna akurat będzie poprawnie ułożona pod adresem podzielnym przez 16, przez przypadek.</note>
 ==== Metoda weryfikacji wyników ==== ==== Metoda weryfikacji wyników ====
 Sprawdzenie debuggerem wraz z podglądem rejestrów / stanu pamięci oraz obserwacja wyników i sprawdzenie obliczeń. Sprawdzenie debuggerem wraz z podglądem rejestrów / stanu pamięci oraz obserwacja wyników i sprawdzenie obliczeń.
 ==== Pomocne zasoby ==== ==== Pomocne zasoby ====
 Visual Studio 2019: [[https://visualstudio.microsoft.com/vs/]] Visual Studio 2019: [[https://visualstudio.microsoft.com/vs/]]
pl/edu/zmitac/n1z/ja/lab4.1618087715.txt.gz · Last modified: by pczekalski