klastry obliczeniowe
Rozproszone fraktale
Marek Sawerwain
soby, które zajmują się nym do rysowania dowolnych fraktali
grafiką, a w szczególno- opartych o tzw. funkcję ucieczki. Python,
ści techniką śledzenia jak każdy język skryptowy, nie oferuje
Opromieni, często mogą wysokiej wydajności, ale posiada pewne
powiedzieć, że moc obliczeniowa ich interesujące nas cechy, a jedną z nich
komputerów nie jest wystarczająca. jest możliwość zdefiniowania funkcji
Dotyczy to także rysowania fraktali, w czasie wykonania skryptu. Dzięki tej
szczególnie tych o skomplikowanych własności serwer będzie odbierał od
wzorach. W tym artykule chciałbym klienta kod zródłowy funkcji i instalował
pokazać, jak rozdzielić zadanie ryso- ją we własnym środowisku.
wania tego typu obiektów na kilka Powód, dla którego za system
komputerów za pomocą technologii CORBY po stronie serwera został
rozproszonych obiektów CORBA. wybrany pakiet OmniORB, jest bardzo
Napiszemy nieskomplikowany program prozaiczny. Obecnie oferuje on najlep-
tworzący coś w rodzaju klastra przezna- szą wersję CORBY dla języka Python.
czonego do rysowania fraktali. Można w nim bez zbędnych kłopotów
Wbrew pozorom, proces rysowa- korzystać z usługi nazw, która ma zna-
nia fraktali jest bardzo pracochłonny czenie zasadnicze dla naszego klastra
i tylko najbardziej popularne zbiory, rysującego fraktale.
takie jak Mandelbrot, Julia czy Newton, Zastanówmy się teraz nad progra-
można narysować w czasie rzeczywi- mem klienta. Może on być napisany
stym. Po wprowadzeniu kilku uprosz- w dowolnym języku i dowolnym systemie
czeń, można nawet pokusić się o ich CORBY, zgodnym ze standardem adapte-
animację. W innych przypadkach jest ra POA oraz obsługującym dynamiczne
to proces bardzo czasochłonny. Dość wywołania metod (DII Dynamic
łatwo można go przyspieszyć, gdy Invocation Inteface). Takim serwerem
sam proces rysowania podzielimy na jest również OmniORB, ale aby pokazać
Na płycie CD/DVD
regiony i rozdzielimy na kilka kompu- elastyczność technologii rozproszonych
Na płycie CD/DVD znajdują
terów. obiektów, zastosujemy inny system
się pliki zródłowe napisanego
o nazwie MICO. Oprócz zastosowania
programu, jak również wszystkie
Kilka postanowień na innego systemu CORBA, program klienta
listingi.
początek zostanie napisany w innym języku, gdyż
O autorze
Pierwsza decyzja, którą trzeba podjąć, będzie to C++.
Autor zajmuje się tworzeniem
dotyczy języków programowania uży- Ostatni problem to: w jaki sposób
oprogramowania dla WIN32
tych do napisania systemu. Serwer zosta- rozproszyć proces rysowania fraktala na
i Linuksa. Zainteresowania:
nie napisany przy pomocy języka Python kilka komputerów? Zastosowany algo-
teoria języków programowania
i systemu CORBA OmniORB. rytm rysuje fraktal linia po linii. Oznacza
oraz dobra literatura. Kontakt
Dlaczego Python? Nasz system, to, że możemy bez problemów podzielić
z autorem:
pomimo swoich niewielkich rozmiarów, cały proces rysowania na kilkanaście
autorzy@linux.com.pl.
ma być systemem uniwersalnym i zdol- komputerów rozdzielając im poszczegól-
34
maj 2004
corba klastry obliczeniowe
Następnie przy pomocy zwykłej pętli W pierwszej kolejności trzeba
Instalacja pakietów
while wywołujemy metodę render_one_ poddać kompilacji plik z interfejsem,
row. Wynikiem tej metody jest jeden gdyż tego wymaga odwzorowanie
OmniORB oraz MICO
Autorzy obydwu pakietów przygotowali wiersz obrazu. Kolory zostały zakodo- OmniORB dla Pythona. Polecenie jest
skrypty configure, więc kompilacja prze- wane w postaci trójek RGB, więc mamy następujące:
biega według typowej ścieżki postępo-
gotowe dane do bezpośredniego przenie-
wania:
sienia na ekran lub zapisu do pliku: omniidl -bpython dfractal.idl
S
./configure prefix=/katalog/
row=0 Po kompilacji pliku z interfejsem, utwo-
gdzie/ma/zostać/zainstalowany/pakiet
while row<=200: rzone pliki dołączamy słowem import.
make
S
dane=calc_node.render_one_ Dołączenie wszystkich modułów konie-
make install
row(row,0,300) cznych do poprawnej pracy serwera to
for i in dane: tylko trzy linie kodu:
Również pakiet z wersją systemu
print i
OmniORB dla języka Python o nazwie
omniORBpy posiada skrypt configure. row=row+1 from omniORB import CORBA, PortableServer
Oczywiście, warto mieć zainstalowane-
import CosNaming
go Pythona w stosunkowo nowej wersji
Tworzymy serwer import dfractal, dfractal__POA
2.2 albo 2.3.
Aplikacje w CORBIE są dzielone na dwie
części: serwer i klient. Prace nad naszym W pierwszej linijce dołączamy moduły
systemem zaczniemy od napisania serwe- bezpośrednio związane z danym syste-
ne wiersze rysowanego obrazu. Klient ra, gdyż, wbrew pozorom, to mniej skom- mem CORBY, czyli w naszym przypad-
przy pomocy usługi nazw pobierze listę plikowane zadanie. ku OmniORB. Druga linia dołącza inter-
zarejestrowanych węzłów i do każdego
wyśle odpowiednią ilość wierszy do
narysowania. Po otrzymaniu wyników
połączy pojedyncze wiersze w jeden
rysunek. Schematycznie zostało to przed-
stawione na Rysunku 1.
Definicja interfejsu
dfractal
Podczas pisania jakichkolwiek aplikacji
w technologii CORBA należy w pierwszej
kolejności określić interfejs obiektów roz-
proszonych. Nasz interfejs o nazwie calc_
node to tylko cztery metody. Jego defini-
cja znajduje się na Listingu 1. Czynności,
które wykonują poszczególne metody, są
następujące:
" setup_iter_func określa postać
funkcji ucieczki;
" setup_render_size określa wymia-
ry obrazu, na którym będzie rysowa-
ny fraktal (w pikselach);
" render_one_row rysuje wiersz o nu-
merze y od pozycji from_x do to_x;
" quit wywołanie tej funkcji powodu-
je zakończenie pracy węzła.
Posługiwanie się tym interfejsem jest
trywialne, np. w Pythonie, gdy chcemy
narysować cały fraktal, to w pierwszej
kolejności ustalamy wymiary oraz poda-
jemy kod funkcji ucieczki:
calc_node.setup_render_size(300,200)
calc_node.setup_iter_func(funkcja_ucieczki)
Rysunek 1. Schemat klastra rysującego fraktal
35
www.linux.com.pl
klastry obliczeniowe
nazwy, pod którą konkretny serwer jest
Listing 1. Definicja interfejsu dfractal
reprezentowany w usłudze nazw.
module dfractal{ Rysowanie fraktala
typedef sequence
TRow; Rysowanie fraktala to zadanie klasy
interface calc_node{ FractalRender. Znaczenie poszczególnych
void setup_iter_func(in string code); metod jest następujące:
void setup_render_size(in long width, in long height);
TRow render_one_row(in long y, in long from_x, in long to_x); " ResetParam(_maxx, _maxy) ustala
void quit(); wymiary rysunku fraktala w pik-
}; selach, współrzędne początku oraz
}; wymiary przestrzeni zespolonej, na
której powstaje fraktal;
" S e t u pIt e r F u n c ( c o d e _ o f _ f u n c )
fejs odpowiedzialny za usługę nazw. jest pisany w Pythonie, to następujący tworzy funkcję ucieczki (iteracji);
W trzeciej linii dołączamy nasz interfejs kod jest prawie identyczny z tym, który " RenderOneLine(render_y, from_x,
oraz wygenerowany szkielet do imple- omówię przy okazji klienta (napisanego to_x) rysuje pojedynczy wiersz,
mentacji serwera. przy pomocy języka C++): wartością wynikową jest lista z kolo-
Kod całego serwera znajduje się na rami w formacie RGB.
S
płycie CD/DVD, ale Listing 2 zawiera ns = orb.resolve_initial_references
jego najważniejsze fragmenty. Jak widać, ( NameService ) W rzeczywistości w kodzie są jeszcze
S
została usunięta definicja klasy Fractal- root_ctx = ns._narrow dwie dodatkowe metody: RenderFrag-
Render. Powrócimy do niej w następnym (CosNaming.NamingContext) ment, przeznaczona do rysowania frag-
punkcie. mentu fraktala, oraz DrawFullFractal-
Każdy, kto choć raz napisał najmniej- Sam proces rejestracji obiektu poprzedza- MandelbrotStyle, rysującą cały fraktal.
szy program w technologii CORBA, od my jego wstępnym utworzeniem: Obydwie, identycznie jak RenderOneLi-
razu pozna charakterystyczne elementy ne, dają w wyniku listę z kolorami w for-
występujące w programie serwera pro- servant = CalcNodeServant() macie RGB.
gramy w CORBIE są do siebie bardzo objref = servant._this() Zastosowany algorytm należy do
podobne, pomimo różnych serwerów najprostszych technik rysowania frak-
CORBY czy zastosowanego języka pro- Gdy w zmiennej objref mamy już refe- tali, która polega na tym, że rysowanie
gramowania. rencję do obiektu, to wystarczy utwo-
Centralnym elementem naszego sys- rzyć obiekt nazwy bazujący na typie
temu renderingu jest usługa nazw. W ser- CosNaming.NameComponent oraz dokonać Format pliku PNM
Pliki w formacie PNM (obsługiwany
werze oraz w kliencie musimy podać jego rejestracji metodą bind obiektu
bez problemów przez GIMP-a) mają
adres symboliczny bądz numer IP, pod root_ctx:
bardzo prostą strukturę. Do naszych
którym znajduje się ta usługa. Z tego
potrzeb wystarczy plik PNM o typie P3.
S
powodu przed uzyskaniem odniesienia name = [CosNaming.NameComponent
Jest to w pełni tekstowy format. Jego
do obiektu orb, zmiennej zawierającej ( calc_node , node_name)]
nagłówek zaczyna się w następują-
argumenty podane w linii poleceń, wpi- root_ctx.bind(name, objref)
cy sposób:
sujemy adres maszyny, na której urucho-
miono usługę nazw: Ostateczna aktywacja serwera to typowy
P3
kod dla dowolnego serwera CORBY.
# CREATOR: narysowane przez dfrac-
S
sys.argv.extend([ -ORBInitRef , Dokonujemy aktywacji adaptera POA:
tal
NameService=corbaname::127.0.0.1 ]) poa._get_the_POAManager().activate()
100 100
oraz uruchamiamy cały serwer: orb.run().
W pierwszej linii zawarty jest typ pliku.
Jak widać z powyższego przykładu, Jak widać na Listingu 2, pomiędzy tymi
P3 oznacza, że będzie to w plik w for-
odwołujemy się do lokalnej maszyny, poleceniami został jeszcze utworzony
macie wyłącznie tekstowym. Następ-
więc używając tego systemu w rzeczywi- obiekt typu FractalRender.
nie podajemy komentarz można w
stej sieci, trzeba zmienić tę linię kodu. Dzięki zdefiniowaniu oddzielnej klasy
nim umieścić krótką informację, kto
Wcześniej wykonujemy jeszcze do rysowania samego fraktala, poszczegól-
utworzył plik. W trzeciej linii nagłówka
jedną istotną czynność, a mianowicie ne metody obiektu CalcNodeServant są
podajemy dwie liczby są to wymiary
odczytujemy podaną nazwę nasze- bardzo krótkie i nie zawierają zbyt skom-
rysunku: szerokość oraz wysokość. Po
go węzła. Nazwy dla poszczególnych plikowanej treści. Wszystkie odwołują się
tak skonstruowanym nagłówku poda-
węzłów muszą być oczywiście inne do globalnego obiektu fractal. I to on
jemy kolejne wartości pikseli, opisa-
w ten sposób je rozróżniamy. Proces wykonuje całą robotę związaną z obsłu- ne za pomocą trójek RGB. Poszcze-
gólne wartości znajdują się w oddziel-
rejestracji naszego węzła w usłudze nazw gą procesu rysowania. Wyróżnia się tylko
nych liniach.
rozpoczynamy od uzyskania odniesie- metoda quit, kończąca działanie naszego
nia do usługi nazw. Pomimo, że serwer serwera. Dokonuje ona także zwolnienia
36
maj 2004
corba klastry obliczeniowe
lewego górnego rogu vfromY. Ta część
Listing 2. Fragmenty skryptu Pythona implementującego serwer calc_node
funkcji wyznacza też początek rysowa-
nia w danym wierszu (początek zawie-
#! /usr/bin/python
import sys ra zmienna xx, a miejsce zakończenia
from omniORB import CORBA, PortableServer
vtoX). Oznacza to, iż metoda RenderOne-
import CosNaming
Line umożliwia także rysowanie dowol-
import dfractal, dfractal__POA
nego fragmentu podanej linii.
class FractalRender:
Po wyznaczeniu współrzędnych
# usunięta definicja klasy rysującej fraktal
class CalcNodeServant(dfractal__POA.calc_node): danego wiersza w części trzeciej pozostaje
global fractal
nam tylko przy pomocy nieskomplikowa-
print setup_iter_func , code
nej pętli przejść wzdłuż osi x po poszcze-
fractal.SetupIterFunc(code)
gólnych punktach należących do danego
def setup_render_size(self, width, height):
wiersza i dla każdego wywołać funkcję
global fractal
fractal.ResetParam(width, height) iter, czyli funkcję ucieczki. Dopiero
print setup_render_size w ,width, h ,height
w argumencie tej funkcji pojawia się kon-
def render_one_row(self, y, from_x, to_x):
struktor complex, który ze współrzędnych
print begin render ,y,from_x, to_x
xx i yy tworzy liczbę zespoloną.
r=fractal.RenderOneLine(y,from_x, to_x)
Wynikiem funkcji RenderOneLine
print end render ,y,from_x, to_x
return r jest lista zawierająca poszczególne trójki
def quit(self):
RGB, opisujące kolory. Lista tworzona jest
global orb, root_ctx
w bardzo prosty sposób. Na początek two-
root_ctx.unbind(name)
rzymy listę pustą, a następnie po wyzna-
print node stop ...
czeniu wartości kolorów dodajemy te
orb.shutdown(0)
if len(sys.argv)>1: wartości do listy. W tym miejscu podczas
node_name=sys.argv[1]
wyznaczania wartości kolorów ujawnia się
else:
print proszę podać nazwę węzła
sys.exit(0)
sys.argv.extend([ -ORBInitRef , NameService=corbaname::127.0.0.1 ])
orb = CORBA.ORB_init(sys.argv, CORBA.ORB_ID)
poa = orb.resolve_initial_references( RootPOA )
ns = orb.resolve_initial_references( NameService )
root_ctx = ns._narrow(CosNaming.NamingContext)
servant = CalcNodeServant()
objref = servant._this()
name = [CosNaming.NameComponent( calc_node , node_name)]
try:
root_ctx.bind(name, objref);
except CosNaming.NamingContext.AlreadyBound:
root_ctx.rebind(name, objref)
print Name calc_node [ ,node_name, ] already existed -- rebound
poa._get_the_POAManager().activate()
fractal=FractalRender()
orb.run()
print ... end work ...
w przestrzeni zespolonej rozpoczynamy nia metody setup_render_size obiektu
od lewego górnego rogu i linia po linii calc_node. Ostatnie dwa przypisania do
dla każdego punktu na wydzielonym kx i ky określają krok, o jaki będą zwięk-
wcześniej obszarze sprawdzamy, jaką szane współrzędne zespolone. W przy-
wartość ma funkcja ucieczki. padku rysowania wiersza istotna jest
Cały kod funkcji rysującej wskaza- tylko zmienna kx.
ny wiersz zawiera Listing 3. Ma ona trzy W części drugiej obliczamy przesu-
części. nięcia względem osi x oraz y, oczywi-
W pierwszej części ustalamy wymia- ście w przestrzeni zespolonej (mimo,
ry rysunku w pikselach: zmienne vWidth że mówimy o liczbach zespolonych, to
i vHeight. Zmienne vfromX i vfromY wszystkie obliczenia bazują na warto-
zawierają współrzędne lewego górne- ściach rzeczywistych). Obliczenia dla
go rogu. Początkowe wartości zawie- zmiennej yy polegają na tym, że podany
rają zmienne z przedrostkiem new . numer wiersz render_y jest mnożo-
Rysunek 2. Diagram przepływu
Ich wartość zostaje określona metodą ny przez wartość kroku ky i dodawany
sterowania w programie klienta
ResetParam w momencie wywoła- do zmiennej zawierającej współrzędne
37
www.linux.com.pl
klastry obliczeniowe
Istotne jest, aby w drugim argumencie wiersz z kolorami RGB). Musimy utworzyć
Listing 3. Metoda klasy FractalRender
tej funkcji podać jako argument funkcję tzw. TypeCode i wygląda to następująco:
rysująca jeden wiersz należący do
globals(). Spowoduje to, iż nasza funkcja
określonego fraktala
(o nazwie iter, bowiem taka nazwa CORBA::TypeCode_ptr _my_tc_longseq;
S
występuje w metodzie RenderOneLine) _my_tc_longseq=CORBA::TypeCode::
S
def RenderOneLine(self, render_y,
zostanie zdefiniowana w podstawowej create_sequence_tc(0, CORBA::_tc_long);
from_x, to_x):
# część I
(głównej) przestrzeni, a więc będzie
self.vWidth=self.newvWidth;
widziana w każdej klasie. Pozostałe typy danych należą do podsta-
self.vHeight=self.newvHeight;
Funkcja iter, oprócz nazwy, przyj- wowych typów CORBY, więc nie trzeba
self.vfromX=self.newvfromX;
muje tylko jeden parametr, który jest stosować dodatkowych zabiegów, aby
self.vfromY=self.newvfromY;
liczbą zespoloną zbudowaną w oparciu z nimi współpracować. Do pracy nasze-
self.kx=self.vWidth/self.maxx;
self.ky=self.vHeight/self.maxy;
o współrzędne punktu w układzie zespo- go klienta potrzebujemy listę wszystkich
# część II
lonym. Przykład takiej funkcji rysującej węzłów. Najlepiej skorzystać z typu
xx=self.vfromX + (from_x * self.kx)
zbiór Mandelbrota zawiera Listing 4. vector:
S
yy=self.vfromY + (render_y *
self.ky)
S
Program klienta vector nodes;
self.vtoX=self.vfromX +
(to_x * self.kx);
Program klienta jest niewiele większy niż
# część III
kod zródłowy serwera. Mimo, że to tylko Potrzebny jest jeszcze jeden typ, gdyż
row=[]
5 kB tekstu, to również omówimy tylko podczas procesu przydzielania wierszy do
while (xx<=self.vtoX):
jego najciekawsze fragmenty. W analizie rysowania będziemy tworzyć listę aktyw-
tc=iter(complex(xx,yy));
programu klienta pomocny będzie rów- nych wywołań. Definicja jest następująca:
r=(tc * 128) % 256;
g=(tc * 32) % 256;
nież schemat z Rysunku 2.
b=(tc * 64) % 256;
Pierwsze zadanie naszego klien- typedef struct {
row.append(r)
ta to odczytanie z usługi nazw wszyst- CORBA::Request_var req;
row.append(g)
kich węzłów, które są dostępne. Milczą- CORBA::Long y,from_x,to_x;
row.append(b)
co zakładamy, że będą to tylko węzły int m;
xx=xx+self.kx
return row
calc_node uprości to nasz program. } TRequest;
Po przygotowaniu listy węzłów
pewna niedoskonałość. Sposób wyzna- możemy rozdać poszczególne wier- Definicja listy, a dokładniej wektora oraz
czania kolorów, który bazuje na reszcie sze obrazu do narysowania. I tu pojawia iteratora niezbędnego do przeglądania,
z dzielenia, nie daje zbyt efektownych się poważny problem. Wywołania metod jest następująca:
układów kolorystycznych, więc zadaniem CORBY zazwyczaj działają w trybie syn-
dla Czytelnika jest napisanie dodatkowej chronicznym, czyli wywołanie jakiejś vector reqs;
funkcji kolorującej fraktal. metody powoduje wstrzymanie pracy vector::iterator reqsIter;
klienta do czasu, gdy serwer odpowie na
Definiowanie funkcji wywołanie określonej metody. Nie jest to Pierwsza z istotnych czynności to uzy-
ucieczki (iteracji) dobre rozwiązanie dla naszego klienta, bo skanie od usługi nazw listy wszystkich
Zajmijmy się teraz metodą, która pozwa- my chcemy rozdać cały zbiór poszczegól- węzłów. Fragment z kodu klienta przed-
la na ustalenie postaci funkcji ucieczki, nych wierszy do rysowania, a dopiero po stawia Listing 5. Listę węzłów odczytuje-
gdyż to ta funkcja odgrywa najważniej- tej czynności poczekać na gotowe dane, my wywołaniem root_ctx->list(0, bl,
szą rolę podczas rysowania różnych frak- aby ostatecznie połączyć wszystkie wier- bi); (zero oznacza, że otrzymamy wszyst-
tali. Treść metody SetupIterFunc wbrew sze w jeden obraz. CORBA oferuje odpo- kie wpisy w usłudze nazw), a następnie
pozorom jest zaskakująco krótka: wiednie rozwiązanie i jest to technika DII,
o której wspomniałem na początku arty-
Listing 4. Przykład funkcji iteracji
def SetupIterFunc(self, code_of_func): kułu. Dynamiczne wywołania pozwala-
odpowiedzialnej za rysowanie zbioru
S
code_of_iter_func=compile(code_of_ ją wywołać zdalną metodę w tzw. trybie
Mandelbrota
func, , exec ) o opóznionej odpowiedzi. Praca progra-
eval(code_of_iter_func,globals()) mu klienta nie jest w takim przypadku moja_funkcja=
wstrzymywana na czas wykonania zdal- def iter(c):
W pierwszej linii dokonujemy kompi- nej metody, ale to na programie klienta i=0
lacji funkcji, więc warto przed rozpo- spoczywa obowiązek odbioru danych. z=complex(0,0);
S
częciem procesu rysowania dokładnie Klient CORBY, który stosuje DII, nie while(i < 128 and (z.real*z.real
sprawdzić, czy funkcja iteracji została musi korzystać z dodatkowych plików + z.imag*z.imag) < 4):
napisana bezbłędnie. Funkcję kompilu- IDL. Z tego powodu nie dokonuje- z=(z*z)+c
jemy w tzw. trybie wykonawczym (trzeci my wstępnej kompilacji naszego inter- i=i+1
parametr przyjmuje wartość exec). Po fejsu. Trzeba jednak utworzyć specjalny return i
wykonania funkcji compile otrzymujemy identyfikator, który pozwoli nam rozpo- ;
obiekt reprezentujący kod, który można znać dane otrzymane od metody serwe-
wykonać przy pomocy funkcji eval. ra render_one_line (będzie to oczywiście
38
maj 2004
corba klastry obliczeniowe
reqs.push_back(r); nn++;
Listing 5. Uzyskanie listy wszystkich węzłów
if(nn>max_nodes-1) nn=0;
}
root_ctx->list(0, bl, bi);
max_nodes=0; Zmienna r służy nam do zapamiętania
while(bi->next_one(b.out())) { wartości obiektu wywołania (Request)
CORBA::ULong k; oraz numeru wiersza aktualnie ryso-
for (k = 0; k < b->binding_name.length (); k++){ wanego. Zapamiętujemy także wymia-
name[0].id=b->binding_name[k].id; ry poziome, czyli początek i koniec.
name[0].kind=b->binding_name[k].kind; Zawsze są to wartości: zero oraz sze-
nodes.push_back(root_ctx->resolve(name)); rokość wiersza, czyli cały wiersz. Treść
max_nodes++; funkcji begin_render_one_row jest bliz-
} niaczo podobna do setup_render_size
} z Listingu 6. Różni się ostatnią linią:
req->send_deferred(); oraz tym, że
w pętli za pomocą resolve otrzymujemy zapisany na liście nodes. W funkcji sto- w wywołaniu set_return_type jako
odniesienia do obiektów typu CORBA:: sujemy metodę _request z argumentem typ podajemy własnoręcznie zdefinio-
Object_var. Przy okazji w zmiennej max_ w postaci ciągu znaków reprezentujących wany TypeCode na samym początku
nodes zliczamy ilość dostępnych węzłów. nazwę metody, którą chcemy wywołać. funkcji main naszego klienta. Metoda
Wartość ta będzie nam potrzebna pod- Następnie musimy określić parametry send_deferred zapewnia, że klient po
czas dystrybucji poszczególnych wierszy metody. Mamy tylko dwa: szerokość (w) jej wywołaniu nie będzie czekał, aż
do węzłów. oraz wysokość (h). Obiekt Request posia- zostanie ona wykonana, lecz przejdzie
Z poziomu funkcji main wystarczą da szereg udogodnień, więc za pomocą do dalszych czynności.
nam już tylko dwa wywołania następu- metody add_in_arg i przeciążonego Listing 7 zawiera ostatnią już
jących funkcji: operatora <<= dodajemy dwa parametry. pętlę, sprawdzającą, czy żądania
Jak łatwo wywnioskować, metodą set_ zostały poprawnie obsłużone i czy
setup_nodes(moja_funkcja, 320, 200); return_type określamy typ wynikowy można odczytać dane. Pętla prze-
render_fractal( rysunek.pnm , 320, 200); tej metody i jest to wartość void. Ostatnia gląda listę żądań reqs i spraw-
istotna linia z Listingu 6 to req->invoke(), dza, czy zmienna m jest ustawiona
Pierwsza ustali nam niezbędne począt- czyli nakazanie wywołania zdalnej na zero. Jeśli tak, oznacza to, że żąda-
kowe parametry, takie jak wymiary oraz metody. W podobny sposób postąpimy nie zostało już poprawnie obsłużone
postać funkcji iteracji, natomiast druga w przypadku drugiej metody, czyli usta- i nie trzeba się nim zajmować. W prze-
wykona całą czarną robotę, czyli nary- lenia treści funkcji ucieczki. ciwnym przypadku funkcją req_is_end
suje fraktal oraz zapisze go w formacie testujemy, czy żądanie zostało już
PNM (więcej informacji o tym formacie Rysujemy fraktal zakończone i jeśli tak, odbieramy dane
w ramce Format pliku PNM). Teraz omówimy ostatni element naszego wiersza wywołaniem get_data, usta-
systemu funkcję render_fracal, w której wiamy wartość flagi m i zwiększamy
Ustalanie parametrów następuje proces rysowania fraktala. zmienną i (zawiera ona ilość wierszy
początkowych W pierwszej kolejności musimy rozdać już narysowanych). Cała pętla zakoń-
Treść funkcji setup_nodes to tylko pętla poszczególnym węzłom kolejne wiersze czy swoje działanie, gdy liczba wierszy
przebiegająca po wszystkich węzłach do rysowania. Poniższa pętla jest spra- będzie większa od wysokości obrazu
i ustalająca potrzebne parametry. Przed- wiedliwa (choć taka strategia nie zapew- pomniejszonego o jedność (pamiętajmy,
stawia się ona następująco: nia największej wydajności), ponieważ że pierwszy wiersz na numer zero).
stara się poszczególnym węzłom przy- Sam proces testowania, czy zada-
for(int i=0;iS
setup_iter_func(nodes[i], zawiera numer aktualnego węzła): w funkcji req_is_end do polecenia: req-
moja_funkcja); >poll_response(). Sprawdza ono, czy
setup_render_size(nodes[i], w, h); for(rows=0;rows<=h;rows++){ żądanie zostało obsłużone. Jeśli tak się
} r.y=rows; r.from_x=0; r.to_x=w; r.m=0; stało, to jak wcześniej podałem, get_data
Ponieważ ustalanie parametrów to
Listing 6. Funkcja ustalająca wymiary rysowanego fraktala na odległym obiekcie
proces łatwy dla poszczególnych serwe-
rów, więc nie ma potrzeby stosowania void setup_render_size(CORBA::Object_var obj, CORBA::Long w, CORBA::Long h){
wywołania o opóznionej odpowiedzi CORBA::Request_var req = obj->_request( setup_render_size );
wystarczy standardowy tryb synchro- req->add_in_arg() <<= w;
niczny. Treść funkcji setup_render_size req->add_in_arg() <<= h;
to Listing 6. Nawet dla osób nie zna- req->set_return_type(CORBA::_tc_void);
jących CORBY jest ona czytelna. Para- req->invoke();
metr obj reprezentuje zdalny obiekt
39
www.linux.com.pl
klastry obliczeniowe
uruchomić klienta. W jego przypadku
Listing 7. Pętla odbierająca wiersze od poszczególnych węzłów
także podajemy adres usługi nazw, np.:
S
/* cześć I -- odbieranie wyników*/ ff_clc -ORBInitRef NameService=
unsigned char *img=new unsigned char[(w+1)*(h+1)*3]; corbaloc::127.0.0.1:2809/NameService
vector::iterator reqsIter;
i=0; Klient po zakończonej pracy (miejmy
while(i < (h-1)){ nadzieję bez kłopotów) powinien pozo-
reqsIter=reqs.begin(); stawić po sobie plik o nazwie rysu-
while(reqsIter!=reqs.end()){ nek.pnm, w którym znajduje się rysunek
r = *reqsIter; z fraktalem.
if(r.m==0 || req_is_end(r.req)==1){
get_data(r.req, (img + (r.y * w * 3))); Na zakończenie
r.m=1; Aączny kod zródłowy systemu to zale-
i++; dwie 10 kB. Jak widać, bardzo niskim
} nakładem można dość szybko uzyskać
reqsIter++; rozproszony system (a można go nawet
} nazwać klastrem), zdolny do rysowa-
} nia różnego typu fraktali. Stosując tego
/* część II -- zapis danych do pliku*/ rodzaju podejście można bardzo łatwo
for(x=0;x fprintf(f_img, %d\n , *(img + ((y*w*3) + (x*3)))); kiem, że da się on podzielić na mniejsze
fprintf(f_img, %d\n , *(img + ((y*w*3) + (x*3)+1))); podproblemy, czyli w przypadku rysowa-
fprintf(f_img, %d\n , *(img + ((y*w*3) + (x*3)+2))); nia fraktali na poszczególne wiersze.
} Co jeszcze można unowocześnić
delete [] img; w naszym systemie? Oprócz funkcji itera-
cji, warto by było wprowadzić dodatkową
funkcję kolorującą. Można również wpro-
obiera dane. Kod odbierający dane jest należy uruchomić usługę nazw. Zastosu- wadzić znacznie wygodniejszego klienta
bardzo krótki: jemy usługę nazw dostępną w serwerze opartego np. o biblioteki QT bądz GTK+
OmniORB: serwer MICO współpracuje przecież
CORBA::LongSeq row; z tymi bibliotekami bez większych pro-
req->get_response(); omniNames -start -logdir /tmp blemów. Zamiast stosowania oddzielnego
req->return_value() >>= row; serwera CORBY, można także napisać
for(int i=0;i *(_row + i) = row[i]; założy swój log w katalogu /tmp. Po para- fikacji nie będzie zbyt wiele. Będą one
metrze -start opcjonalnie można umie- głownie dotyczyć definicji typu wiersza,
Metoda get_response kończy proces ści numer portu, na którym działa usługa jaki jest zwracany przez zdalną metodę
wywołania zdalnej metody i pozwala nazw domyślnie jest to 2809. Jeśli log render_one_row.
wyciągnąć z metody return_value() jest już założony, to wystarczy podać Zachęcam do modyfikacji przedsta-
dane reprezentujące nasz wiersz. tylko parametr z katalogiem, gdzie ten wionego systemu, szczególnie te osoby,
Następnie przy pomocy prostej pętli log się znajduje: które interesują się technologią CORBA.
przepisujemy dane ze zmiennej row do Na podstawie takiego systemu można
odpowiedniego miejsca w tablicy img. omniNames -logdir /tmp zdobyć wiele cennych doświadczeń.
Kończąc opis klienta, jeszcze słowo
o tablicy przechowującej poszczegól- Następnym krokiem jest uruchomienie
W Internecie:
ne wiersze. Tablica img to jednowymia- serwerów calc_node, oczywiście na
rowy wektor, w którym są umieszcza- jak największej ilości różnych kompu-
Strona domowa języka Python:
ne dane wierszy dzięki odpowiedniemu terów. Poszczególne serwery powin- http://www.python.org/
adresowaniu. Adresowanie może wyda- ny oczywiście posiadać inną nazwę: Strona projektu OmniORB:
http://omniorb.sourceforge.net/
wać się nieco dziwne , ale pamiętajmy, ./fc_server.py wezel_1. W kodzie
Strona projektu MICO:
że pojedynczy piksel jest opisany trzema serwera znajduje się także adres usługi
http://www.mico.org/
bajtami, więc dlatego podczas adresowa- nazw. W plikach znajdujących się na
Strona nieaktualizowana, ale
nia współrzędne musimy mnożyć przez CD/DVD adres ten odnosi się do kompu-
zawierająca wiele cennych infor-
trójkę. tera lokalnego, wiec w rzeczywistej sieci
macji o fraktalach:
trzeba go koniecznie zmienić.
http://spanky.triumf.ca/
Jak rysować fraktale? Gdy serwery zostały juz uruchomio-
Więcej informacji o fraktalach:
Zanim zaczniemy cokolwiek rysować ne (do poprawnego działania aplikacji
http://www.fractalus.com/ifl/
w naszym rozproszonym systemie, wystarczy przynajmniej jeden), można
40
maj 2004
Wyszukiwarka
Podobne podstrony:
05 Normalizacja struktury bazy danych (AC)
2004 05 Sybase SQL Anywhere Studio 9 0 [Bazy Danych]
2004 11 Porównanie serwerów relacyjnych baz danych Open Source [Bazy Danych]
2004 09 Kexi bazy danych [Bazy Danych]
Rozproszone bazy danych
05 Część II Pobieranie danych z bazy danych Instrukcja S
BAZY DANYCH Streszczenie z wykładów
Strona polecenia do bazy danych
MySQL Mechanizmy wewnętrzne bazy danych
Bazy danych w CAD
więcej podobnych podstron