Views

Biuletyn nr 15

From KDMwiki

Jump to: navigation, search
Biuletyn KDM
1 | 2 | 3 | 4 | 5
6 | 7 | 8 | 9 | 10
11 | 12 | 13 | 14
15 | 16 | 17 | 18
19 | 20 | 21 | 22
23 | 24 | 25 | 26
27 | 28 | 29 | 30
31 | 32
Lista biuletynów

Biuletyn nr 15 (26 września 2006)

Spis treści

TotalView - debugowanie kodów równoległych (część 1)

Autor: Maciek Cytowski

Wstęp

TotalView firmy Etnus to narzędzie, które umożliwia nam debugowanie oraz analizowanie aplikacji. Od innych programów tego typu TotalView różni się możliwością pracy z aplikacjami tworzącymi więcej niż jeden proces lub wątek. Praca z TotalView jest podobna do pracy z innymi popularnymi debugerami:

  • używasz opcji -g do kompilacji swojego programu
  • uruchamiasz program pod kontrolą debugera
  • ustawiasz breakpoint'y
  • analizujesz kod i dane

TotalView został przeportowany przez Cray'a na maszynę CrayX1 (praca zreferowana na konferencji CUG 2003). Dzięki temu w ICM jest on dostępny na komputerze tornado. TotalView ma dwa tryby pracy: okienkowy i linii komend.

Opis narzędzia TotalView zawarty będzie w kilku częściach ze względu na obszerność tematyki. W pierszej części przedstawimy tylko kilka tematów:

  • organizacja procesów i wątków
  • uruchomienie TotalView
  • ustawianie tzw. action point 'ów

Procesy, wątki i grupy

Najprostszymi programami do debugowania są te, które tworzą tylko jeden proces i wątek. Wówczas debuger potrafi z łatwością odpowiedzieć na pytanie 'co w danej chwili robi nasz program?'. Jednak współczesne modele programowania opierają się często na:

  • możliwości tworzenia kilku wątków w obrębie jednego procesu
  • możliwości tworzenia kilku procesów z różną liczbą wątków na komputerach kilku-procesorowych
  • możliwości tworzenia procesów na wielu węzłach komputerowych połączonych siecią

Różne rodzaje wątków

Totalview rozróżnia kilka rodzajów wątków w zależności od funkcji pełnionej w programie:

  • user threads - wszystkie wątki w procesie
  • worker threads - te wątki, które zostały utworzone przez nasz program do wykonania konkretnego zadania
  • manager threads - wątki dodawane do naszego programu przez środowisko lub system operacyjny
  • service threads - wątki, które wykonują usługi dla innych wątków w programie (np. wątek, który wysyła żądanie wydruku do drukarki w odpowiedzi na polecenia z pozostałych dwóch wątków); podzbiór worker threads

W TotalView mamy możliwość wyboru wątku i procesu. Dzięki temu podczas debugowania możemy skupić się na wątkach i procesach, które aktualnie wykonują zaprogramowane przez nas zadanie.

Organizowanie wątków i procesów w grupy

W TotalView mamy kilka możliwości debugowania programów, które uruchamiają duże ilości procesów i wątków na wielu procesorach. Jedną z nich jest przyglądanie się każdemu z tych wątków lub procesów indywidualnie. Jest to jednak mało praktyczne. Rozwiązaniem, które wprowadza TotalView to organizacja procesów i wątków w grupy, których następnie możemy użyć do debugowania programu. Innymi słowy w wieloprocesorowych, wielowątkowych aplikacjach rzadko programujemy każdy proces lub wątek osobno. Częściej wykonujemy wielokrotnie te same operacje na różnych zbiorach danych. TotalView oczywiście nie potrafi odgadnąć architektury naszego programu, może jedynie dokonać inteligentnego wyboru. Potrafi grupować procesy i wątki w następujące rodzaje grup:

  • control group - Wszystkie procesy, które tworzy program (lokalne i nie lokalne). Jeśli program używa procesów, których nie stworzył, TotalView umieszcza je w osobnych control group 'ach. Na przykład program aplikacja typu client/server składa się z dwóch osobnych programów, które uruchamiane są niezależnie od siebie. TotalView umieściłby te programy w osobnych control group. Dla porównania procesy tworzone poleceniem fork należą do tej samej control group.
  • share group - Wszystkie procesy z jednej control group które dzielą ten sam fragment kodu. W większości przypadków programy mają więcej niż jedną share group. Share groups mogą być lokalne lub nie.
  • workers group - Wszystkie wątki będące workers thread i należące do jednej control group. Te wątki mogą należeć do więcej niż jednej share group.
  • lockstep group - Wszystkie wątki które istnieją dla danego stanu licznika programu. Ta grupa jest podzbiorem workers group i istnieje tylko dla zatrzymanych wątków. Z definicji, wszystkie elementy lockstep group są z tej samej workers group.

Dwie pierwsze grupy z powyższych zawierają tylko procesy, a dwie pozostałe tylko wątki. Proszę zwrócić uwagę, że stwierdzenie "ten sam fragment kodu" oznacza, że procesy mają tą samą nazwę i ścieżkę pliku wykonywalnego. TotalView pozwala manipulować procesami oraz wątkami pojedynczo oraz jako grupy. Dodatkowo istnieje możliwość tworzenia własnych grup.

Przygotowanie sesji debugingu - krok po kroku

W tej części opisane jest wszystko to co należy wykonać, aby rozpocząć debuging programu za pomocą TotalView. Wszystkie zawarte tutaj informacje dotyczą użycia TotalView w ICM, czyli komputera tornado. Powinny one być jednak przydatne również w innych środowiskach (poza drobnymi szczegółami, jak np. nazwy kompilatorów).

Krok 1 - program

Aby zaprezentować podstawową funkcjonalność TotalView użyjemy następującego przykładu programu równoległego, który zaprezentowany został w Biuletynie nr 6:

//Przyklad UPC - calkowanie numeryczne
//Obliczanie liczby Pi
#include<upc_relaxed.h>
#include<math.h>
#define N 1000000
#define f(x) (1.0/(1.0+x*x))

upc_lock_t *l;
shared float pi=0.0;
void main(void)
{
       float local_pi=0.0;
       int i;
       l=upc_all_lock_alloc();

       upc_forall(i=0;i<N;i++;i)
               local_pi+=(float) f( (0.5+i)/(N) );
       local_pi*=(float)(4.0/N);

       upc_lock(l);
       pi+=local_pi;
       upc_unlock(l);

       upc_barrier;
       if(MYTHREAD==0) printf("PI=%f\n",pi);
       if(MYTHREAD==0) upc_lock_free(l);
}

Program ten wyznacza przybliżenie liczby Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): \Pi

i napisany jest przy użyciu biblioteki programowania równoległego UPC.

Krok 2 - kompilacja

Zanim rozpoczniemy debugować program, powinniśmy go w odpowiedni sposób skompilować. Jedyną różnicą przy kompilacji jest dodanie opcji -g. Opcja ta mówi kompilatorowi, że ma utworzyć tablicę symboli dla debugera. Na komputerze tornado, w zależności od języka programowania kompilacja z opcją -g wygląda tak:


Kompilator Komenda kompilacji
Cray Fortran ftn -g -o file.out file.ftn
Cray C cc -g -o file.out file.c
Cray C++ CC -g -o file.out file.C

Można również debugować program, który nie został skompilowany z opcją -g lub program, dla którego nie jest dostępny kod źródłowy.

W przypadku naszego programu testowego kompilacja wyglądać będzie następująco:

cc -h upc -h ssp -g -o pi.exe pi.c

Jak widać wyżej, oprócz opcji -g, musieliśmy również dodać opcje -h upc (mówiącą kompilatorowi, że program korzysta z biblioteki UPC) oraz opcję -h ssp (określającą, że program będzie uruchamiany na procesorach SSP).

Pamiętajmy również, że kompilacji na komputerze tornado możemy dokonać tylko i wyłącznie w systamie plików /cpes/.

Ważne jest aby rozszerzenia plików naszego programu odpowiadały konkretnym językom programowania. TotalView czytając pliki, używa ich rozszerzenia do określenia czy jest to fragment programu napisany w Fortran'ie, C czy C++.

Krok 3 - uruchomienie TotalView

Jak już było powiedziane TotalView w ICM dostępny jest w dwóch wersjach: okienkowej (komenda totalview) i terminalowej (komenda totalviewcli). Zaprezentujemy przykład użycia TotalView w wersji okienkowej.

W większości przypadków uruchomienie TotalView wygląda tak:

totalview [executable [corefile]] [opcje]

totalviewcli [executable [corefile]] [opcje]

gdzie executable to plik wykonywalny, a corefile to plik typu core file utworzony przez system operacyjny. Pliki typu core file mogą być tworzone w przypadku błedu lub crashu jakiegoś programu. Wówczas możemy używać ich potem do zrekonstruowania stanu programu sprzed błędu.

Przed uruchomieniem TotalView okienkowego na tornado należy pamiętać o tzw. przekierowniu X-sów. Należy zalogować się do ICMu oraz na maszynę tornado z opcją -Y:

ssh -Y user@atol.icm.edu.pl
..
..
ssh -Y tornado

Aby uruchomić nasz program na 4 procesorach SSP pod kontrolą debugera możemy napisać:

totalview -app "-n4" ./pi.exe

Musimy pamiętać, że powinniśmy znajdować się w katalogu, w którym umieszczony jest plik pi.exe. Najlepiej aby był to podkatalog w systemie plików /tmp* lub podkatalog w naszym katalogu domowym.

Program TotalView standardowo uruchamia zadania poprzez program aprun. Specyfikując opcję -app możemy przekazać programowi aprun argumenty. W tym wypadku określamy liczbę procesorów poprzez "-n4".

Na komputerze tornado istnieje obowiązek korzystania z systemu kolejkowego PBS. Charakter naszego zadania wymaga określenia opcji -I w programie qsub. Opcja ta umożliwi nam pracę interaktywną w shellu po udostępnieniu przez system kolejkowy określonych zasobów. Powiedzmy, że decydujemy się na 15 minutową pracę z debugerem i wykorzystamy 4 procesory SSP. Wówczas do rozpoczęcia pracy możemy użyć następującego skryptu PBS (submit.pbs):

#!/bin/tcsh 
#PBS -S /bin/csh
#PBS -l mppssp=4
#PBS -l cput=01:00:00
#PBS -l pmppt=0:15:00

Następnie wstawiamy takie zadanie do kolejki używając opcji -I:

qsub -I submit.pbs

PBS raportuje przyjęcie zadania:

qsub: waiting for job 29264.sn7710 to start

A po udostępnieniu zasobów powinniśmy ujrzeć komunikat podobny do następującego:

qsub: job 29264.sn7710 ready
 
Currently Loaded Modulefiles:
  1) modules     3) totalview   5) craytools   7) mpt         9) libsci     11) PrgEnv     13) open
  2) X11         4) cal         6) cftn        8) CC         10) craylibs   12) pbs        14) biolib
Available local disk space:
   /tmp1 -  72.25 GB free (26.6 %)
   /tmp2 -  99.06 GB free (36.5 %)
   /tmp3 -  41.62 GB free (27.3 %)
   /tmp4 -  40.47 GB free (67.9 %)
21:sheed@tornado:/home/staff/sheed#

Tutaj może nas spotkać niemiła niespodzianka. Jeśli chcemy skorzystać z TotalView okienkowego musimy ustawić sobie jeszcze zmienną środowiskową $DISPLAY w nowym interakcyjnym shellu, którego otrzymaliśmy od PBSa. Najlepiej tuż przed wstawieniem zadania do kolejki podejrzeć sobie zawartość zmiennej $DISPLAY:

115:sheed@tornado:/home/staff/sheed# echo $DISPLAY
tornado:10.0

A po otrzymaniu shella od systemu kolejkowego najpierw ustawić sobie zmienną $DISPLAY:

setenv DISPLAY tornado:10.0

a następnie uruchomić zadanie pod debugerem:

totalview -app "-n4" ./pi.exe

Po uruchomieniu TotalView ujrzymy nastepujące dwa okna:

Grafika:Tv2.png Grafika:Tv1.png

Jak widać program zatrzymuje się w pobliżu pierwszej linii funkcji main(void). Mniejsze okno to okno główne, w którym widoczne są wszystkie procesy i wątki, które aktualnie są przez nas uruchomione. Okno to ma kilka zakładek. Na załączonym zdjęciu widać zakładkę Attached procesów, które gotowe są do debugingu. Jeśli na komputerze tornado równolegle edytujemy sobie jakiś plik programem vi to proces z nim związany widoczny będzie w zakładce Unattached. Większe okno to okno procesu, a w nim wszystkie narzędzia debugingu dostępne w TotalView. Narzędzi tych jest sporo, tak samo jak czynności, których przy użyciu nich możemy wykonać.

W ten sposób przygotowaliśmy środowisko debugingu i możemy rozpocząć pracę.

Action points - znaczniki

Przy debugowaniu kodu niezmiernie istotna jest możliwość zatrzymywania programu i oglądania jego aktualnego stanu. Zwykle do realizacji takiego zadania, w klasycznym debugingu, używane są breakpoint 'y. Wpierw ustawiamy je, w którymś miejscu w kodzie, a następnie uruchamiamy program. Wykonanie zostaje zastopowane, gdy program dojdzie do zaznaczonej linii. W TotalView występuje kilka rodzajów tego typu znaczników (tu nazywanych action points):

  • Breakpoint - klasyczny breakpoint, gdy dany proces lub wątek dojdzie do breakpoint 'a wszystkie procesy i wątki programu zostają zastopowane
  • Barrier points - podobne do zwykłych breakpoint 'ów, używane aby zsynchronizować grupę procesów lub wątków; wstrzymują wszelkie wątki lub procesy, które do nich dotarły dopóki nie dotrą do nich wszystkie wątki i procesy.
  • Evaluation points - ten typ znacznika jest zwykłym breakpoint 'em, do którego przypisany jest fragment kodu, który zostaje wywołany gdy program dojdzie do tego znacznika; można ich używać w wiele różnych sposobów

Ustawianie action point 'ów

Istnieje kilka sposobów ustawiania action point 'ów:

  • kliknięcie w kodzie na żądanej linii i skorzystanie z górnego paska menu Action Point>
  • ustawienie się na żądanej linii i kliknięcie lewym przyciskiem myszy; wybranie żądanego rodzaju action point 'a
  • pojedyncze kliknięcie na numer linii (standardowo ustawia breakpoint), a następnie zmiana rodzaju action point 'a przy użyciu Properties w górnym pasku menu Action Point> lub w menu podręcznym (po kliknięciu lewym przyciskiem myszy)
  • wybranie Action point>At Location i wskazanie funkcji lub linii w kodzie za pomocą specjalnej składni TotalView

Drugi z tych sposobów prezentują następujące rysunki:

Grafika:Tv3.png Grafika:Tv4.png

Wpierw kursor myszy ustawiony został na linii 16, następnie za pomocą podręcznego menu wybrana została komenda Set Barrier, po czym w widoku kodu pojawiła się ikonka bariery.

Po ustawieniu bariery możemy uruchomić nasz program klikając na Run w pasku narzędzi. Wykonanie programu zatrzyma się w linii 16-tej. Możemy łatwo sprawdzić, że wszystkie procesy zatrzymały się właśnie w tym miejscu. Wystarczy kliknąć na ikonkę P+ lub P- w górnym pasku narzędziowym. Czynności te przedstawiają rysunki:

Grafika:Tv5.png Grafika:Tv7.png

Konfiguracja action point 'ów

Jak można łatwo zauważyć, nie ma możliwości wybrania evaluation point 'a ani z menu podręcznego ani z górnego paska menu. Aby ustawić tego typu znacznik najlepiej jest wpierw ustawić zwykłego breakpoint 'a w dowolny sposób, a następnie wybrać Properties z menu podręcznego (dostępnego po wciśnieciu lewego klawisza myszy nad danym breakpoint 'em). Wówczas widzimy następujące okno:

Grafika:Tv11.png

Dostępnych jest również trochę opcji związanych z breakpoint 'ami oraz barierami. Po wybraniu Properties z menu podręcznego bądź Action Point>Properties z górnego paska menu, możemy konfigurować znaczniki w następujących oknach:

Grafika:Tv10.png Grafika:Tv9.png

Jak widać możemy zarządać, aby po uderzeniu w breakpoint lub barierę zatrzymywana była grupa, proces lub wątek. Ponadto w konfiguracji bariery możemy ustawić co ma być na barierze wstrzymywane: grupa, proces bądź wątek.

Należy być bardzo czujnym w przypadkach ustawiania barier w częściach kodu do których mają dostęp tylko niektóre procesy. Wówczas najlepiej operować na tzw. share group 'ach lub tworzyć własne grupy.

Ćwiczenia i pomoce

Po tej pierwszej lekcji TotalView warto przećwiczyć sobie uruchamianie tego programu oraz ustawianie znaczników w różnych miejscach przykładowego kodu.

W przypadku jakichkolwiek trudności lub pytań warto spojrzeć na pomoce dostępne w sieci:

Dostępny jest adres e-mail: pomoc@icm.edu.pl pod który można kierować ewentualne pytania dotyczące TotalView na tornado.

Macierze w C i Fortranie

Autor: Łukasz Bolikowski

Pisząc programy wykonujące obliczenia naukowe bardzo często zachodzi potrzeba przechowywania tablic dwuwymiarowych, na przykład w celu wykonywania operacji macierzowych. W niniejszym artykule poznamy najpopularniejsze sposoby reprezentacji macierzy (gęstych), oraz zwrócimy uwagę na różnice w pracy z macierzami pomiędzy językami C i Fortran.

W dalszej części będziemy rozważać możliwe reprezentacje macierzy o M wierszach i N kolumnach:

Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,1} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,2} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,3} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,N}
Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,1} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,2} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,3} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,N}
... ... ... ... ...
Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,1} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,2} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,3} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,N}

Reprezentacja przy pomocy tablicy jednowymiarowej

Pierwszy pomysł, to reprezentacja w postaci tablicy jednowymiarowej o rozmiarze M*N. Deklarujemy więc tablicę tak (język C):

 double a[M*N];

lub tak (język Fortran):

 double precision a(M*N)

Mamy pełną swobodę jeśli chodzi o sposób przechowywania macierzy w takiej tablicy. Dwie zdecydowanie najpopularniejsze metody to przechowywanie macierzy "wierszami", oraz przechowywanie "kolumnami".

Reprezentacja "wierszami"

W tej metodzie przechowujemy najpierw cały pierwszy wiersz (N wartości), następnie cały drugi wiersz (N wartości), itd.:

Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,1} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,2} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,3} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,N} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,1} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,2} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,3} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,N} ... ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,1} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,2} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,3} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,N}

Funkcja wypisująca zawartość takiej macierzy może wyglądać następująco (język C):

 C012.  void print_matrix_rows(int m, int n, double a[]) {
 C013.     int i, j;
 C014.
 C015.     for (i = 0; i < m; i++) {
 C016.        for (j = 0; j < n; j++)
 C017.           printf(" %5.1f", a[j+i*n]);
 C018.        printf(" \n");
 C019.     }
 C020.  }

lub tak (język Fortran):

 F013.        subroutine print_matrix_rows(m, n, a)
 F014.           implicit none
 F015.           integer m, n
 F016.           double precision a(m*n)
 F017.           integer i, j
 F018.
 F019.           do i = 1, m
 F020.              do j = 1, n
 F021.                 write(*,'(X,F5.1,$)') a(j+(i-1)*n)
 F022.              end do
 F023.              write (*,'(X)')
 F024.           end do
 F025.        end

Reprezentacja "kolumnami"

W tej metodzie przechowujemy najpierw całą pierwszą kolumnę (M wartości), następnie całą drugą kolumnę (M wartości), itd.:

Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,1} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,1} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,1} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,2} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,2} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,2} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,3} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,3} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,3} ... ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{1,N} Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{2,N} ... Parser nie umiał rozpoznać (Missing texvc executable; please see math/README to configure.): a_{M,N}

Funkcja wypisująca zawartość takiej macierzy może wyglądać następująco (język C):

 C022.  void print_matrix_columns(int m, int n, double a[]) {
 C023.     int i, j;
 C024.
 C025.     for (i = 0; i < m; i++) {
 C026.        for (j = 0; j < n; j++)
 C027.           printf(" %5.1f", a[i+j*m]);
 C028.        printf(" \n");
 C029.     }
 C030.  }

lub tak (język Fortran):

 F027.        subroutine print_matrix_columns(m, n, a)
 F028.           implicit none
 F029.           integer m, n
 F030.           double precision a(m*n)
 F031.           integer i, j
 F032.
 F033.           do i = 1, m
 F034.              do j = 1, n
 F035.                 write(*,'(X,F5.1,$)') a(i+(j-1)*m)
 F036.              end do
 F037.              write (*,'(X)')
 F038.           end do
 F039.        end

Reprezentacja dwuwymiarowa w języku C

Język C pozwala na definiowanie explicite tablic wielowymiarowych. Deklaracja:

 double a[M][N];

zarezerwuje blok pamięci o rozmiarze M*N, w którym macierz przechowywana będzie "wierszami". Podkreślmy: macierze w C przechowywane są "wierszami".

W większości zastosowań nie musimy wiedzieć, jak wewnętrznie przechowywana jest nasza macierz. Przykładowo, funkcja wypisująca zawartość macierzy może wyglądać następująco:

 C032.  void print_matrix_c(int m, int n, double a[m][n]) {
 C033.     int i, j;
 C034.
 C035.     for (i = 0; i < m; i++) {
 C036.        for (j = 0; j < n; j++)
 C037.           printf(" %5.1f", a[i][j]);
 C038.        printf(" \n");
 C039.     }
 C040.  }

Reprezentacja dwuwymiarowa w języku Fortran

Język Fortran także pozwala na definiowanie explicite tablic wielowymiarowych. Deklaracja:

 double precision a(m, n)

zarezerwuje blok pamięci o rozmiarze M*N, w którym macierz przechowywana będzie "kolumnami". Podkreślmy: macierze w Fortranie przechowywane są "kolumnami".

Także w Fortranie, w większości zastosowań, nie musimy wiedzieć, jak wewnętrznie przechowywana jest nasza macierz. Przykładowo, funkcja wypisująca zawartość macierzy może wyglądać następująco:

 F041.        subroutine print_matrix_fortran(m, n, a)
 F042.           implicit none
 F043.           integer m, n
 F044.           double precision a(m, n)
 F045.           integer i, j
 F046.
 F047.           do i = 1, m
 F048.              do j = 1, n
 F049.                 write(*,'(X,F5.1,$)') a(i, j)
 F050.              end do
 F051.              write (*,'(X)')
 F052.           end do
 F053.        end

Przykładowe programy

Dostępne są przykładowe programy:

ilustrujące reprezentację macierzy w językach C i Fortran.

Wszystkie funkcje i procedury pokazane w tym artykule są dostępne w w/w plikach.

Łączenie C i Fortranu

Jak zostało napisane wyżej, do sprawnego posługiwania się macierzami jako dwuwymiarowymi tablicami nie jest wymagana wiedza nt. sposobu ich przechowywania w pamięci.

Reprezentacja macierzy w pamięci okaże się jednak istotna, gdy będziemy chcieli połączyć fragmenty programu napisane w różnych językach (tzn. C i Fortran). Warto wtedy myśleć o macierzach jako o blokach pamięci rozmiaru M*N, którym nadaje się potencjalnie różną interpretację w zależności od użytego języka i deklaracji.

Łączac C z Fortranem należy ustalić a priori reprezentację macierzy - "wierszową" lub "kolumnową" i konsekwentnie ją stosować. Warto wybrać tę reprezentację, która jest naturalna dla dominującego w programie języka (naturalny dla C jest porządek "wierszowy", a dla Fortranu porządek "kolumnowy").

Przykład: projektując program, którego większa część napisana będzie w Fortranie, a pomocnicze funkcje w C, warto założyć porządek "kolumnowy". We fragmentach napisanych w C należy korzystać mniej więcej tak, jak w funkcji print_matrix_columns, natomiast we fragmentach w Fortranie można już użyć naturalnych tablic dwuwymiarowych - tak jak w procedurze print_matrix_fortran.

Warto pamiętać

  • Indeksy tablic w C zaczynają się od 0 (zero), indeksy tablic w Fortranie zaczynają się od 1 (jeden).
  • Korzystanie z dwuwymiarowych tablic jest prostsze - nie trzeba myśleć o reprezentacji w pamięci.
  • Tablice w C przechowywane są "wierszami", tablice w Fortranie przechowywane są "kolumnami".


Einstein spotyka Curie-Skłodowską

Autor: Maciek Cytowski


22 września ICM uczesniczył w projekcie pt. "Einstein spotyka Curie-Skłodowską". Impreza ta została zorganizowana przez Polsko-Niemiecka Współpraca Młodzieży (PNWM) oraz niemieckie Federalne Ministerstwo Edukacji i Nauki (BMBF) (Strona WWW projektu). W ramach imprezy ICM odwiedziła grupa ok. 30 młodych Polaków i Niemców zainteresowanych nauką i techniką. Odbyły się pokazy dotyczące ICM (Prof. Marek Niezgódka), ATVN (Krystyna Rudowska), numerycznej prognozy pogody (Oskar Kapala i Piotr Kmieć) oraz Graduate College (Maciek Cytowski).

Podzieleni w grupy uczestnicy zwiedzali studio Akademickiej Telewizji Naukowej, serwerownię ICM oraz zapoznawali się z ICM-owym serwisem Meteo.

Zdjęcia z imprezy można obejrzeć tu.