Kispad

Kispad: közös blog
4230 cikk, 53959 hozzászólás
Szerzők | Tudnivalók | Feedek


In memoriam William of Occam

stsmork cikke a Torokgeek rovatból, 2006. január 15. vasárnap, 22:25 | 12 hozzászólás

Azon kevesek közé tartozom, akiknek az Occam név hallatán nem a borotválkozás vagy a filozófia jut az eszébe, hanem egy obskurus programozási nyelv. Az INMOS cég által gyártott Transputer nevű RISC processzorán találkoztam vele, ami ma már jócskán az ipartörténeti műemlék kategóriájába tartozik, így most egy cikk erejéig kirándulást teszünk a műszaki archeológia mezejére.

Radikálisan új dolgot több módon is létre lehet hozni; az egyik bevált módszer az, hogy felrúgunk valamilyen alapelvet. Az Occam is ezt teszi: szakít azzal a hagyománnyal, hogy az egymás után leírt programsorokat ugyanilyen sorrendben hajtja végre a számítógép. Nem, a helyzet itt nem ilyen egyszerű: minden egyes blokkról el kell döntenünk, hogy azok sorai szekvenciálisan vagy párhuzamosan fussanak-e. A PAR kulcsszóval jelölt blokk sorai egymással párhuzamosan, a SEQ blokké pedig egymás után hajtódnak végre. Az már az operációs rendszer vagy a futtató környezet dolga hogy eldöntse, a párhuzamosan futó folyamatok időosztással, vagy külön processzoron fognak-e futni.

Mindez egyébként nem az Occam nyelv újítása, lényegében a CSP formális nyelvben leírtakat próbálták meg a gyakorlatba is átültetni.

A több szálon futó programok rendesen globális változókon keresztül kommunikálnak, ami mindjárt felvet egy súlyos problémát: mi van, ha egyszerre két folyamat akar írni ugyanabba a változóba? (A klasszikus „ebédelő filozófusok” feladat is ezt a kérdést feszegeti.) Többféle megoldás létezik; az Occam egyszerű, de brutális megoldáshoz folyamodott: kitiltotta a nyelvből a globális változókat.

Helyette bevezette a csatornákat. Ha két folyamat adatokat szeretne cserélni egymással, azt ezeken keresztül teheti meg. A csatornák egyirányúak, blokkolják mint a fogadót mind a küldőt, és nincs rajtuk határidő: a program villanyoltásig fog várni, ha nem érkezik adat, illetve ha a túloldalon nem veszik fel a telefont. A csatornák rendesen a párhuzamosan futó folyamatok közti kommunikáció eszközei, de hardverporthoz és a processzor belső órájához is lehet csatornát kötni. A ? operátorral beolvasunk, a ! operátorral pedig adatokat küldünk.

to.host ! status
from.host ? cmd

Nézzünk most egy egyszerű példát! Egy egyenáramú motor fordulatszámát szabályozzuk úgy, hogy a motor egy impulzusjeladót forgat, amelynek a jeleit egy számlálóval számláljuk. A WHILE ciklus két párhuzamos ágra bomlik: az egyik törli a számlálót, vár egy kicsit, aztán leolvassa az aktuális értéket - az így kapott jel a sebességgel arányos. A másik ág lekérdezi a (példában nem látható) szabályozó algoritmusfolyamattól a mintavételi időt és az alapjelet. Ez utóbbit mindjárt tovább is küldi a D/A átalakítóra, amely a motort hajtja.

INT time:
TIMER clock:
WHILE TRUE
  SEQ
    output ! (INT16 data) -- Send counter value to host
    PAR
      SEQ -- Measure motor speed
        CLEAR ! RESET
        CLEAR ! READY
        SEQ i=0 FOR (INT wait)
          SEQ
            clock ? time
            clock ? AFTER time PLUS ten.miliseconds
          COUNTER ? data          -- read counter
      SEQ  -- Receive and execute control signal
        input ? speed        -- control signal
        input ? wait         -- sampling rate
        SPEED ! (INT speed)  -- set DC motor speed

Mi van, ha egyszerre több csatornáról is jöhetnek adatok, de nem tudjuk pontosan előre, hogy melyikről? Az ilyen helyzetek kezelésére született az ALT utasítás, amely addig vár, amíg az alternatívaként felsorolt csatornák valamelyikén adat nem érkezik.

ALT
  foo ? foo_data
    SKIP
  bar ? bar_data
    SKIP
  ...

A csatornaműveletet követő sorban bármilyen utasítás állhat, de tipikusan csak egy SKIP van itt, ami az ALT után következő sorra adja át a vezérlést. Az ALT-ok egymásba ágyazhatók, és ciklussal is elő lehet állítani az egyes ágakat. A következő példa n+1 darab csatornát figyel, az első n-ről adatokat vár, az n+1.-ről pedig a programot leállító utasítást.

ALT
  ALT i = 0 FOR number.of.channels
    req[i] ? Ch.ID;write;PA;PB;PC
      SKIP
  stop.bus.handler ? going
    SKIP

A másik rettentő újítás a kötelező indentáció: a blokkok sorait muszáj két szóközzel beljebb kezdenünk. Nem viccelek! A fordító hibát jelez, ha egy sor nincs szabályosan indentálva. Emiatt egy többszörösen egymásba ágyazott IF szerkezet úgy néz ki, mint egy egérrágta könyv széle. Amúgy az INMOS által szállított könyvtári függvények forráskódja teli van kommentbe tett nyitó és záró kapcsos zárójelekkel, amelyekkel a blokkok kezdetét és végét jelzik.

A kommenteket, amennyiben külön sorba írjuk őket, szintén indentálnunk kell.

Megkülönböztetett figyelmet érdemel az Occam nyelv IF utasítása. Logikáját tekintve inkább a szokásos case-switch utasításpárra hajaz, az IF után szépen felsoroljuk a feltételeket, és hogy teljesülésük esetén mi történjen. A kiértékelés sorban egymás után történik, az első teljesült feltétel után végrehajtódik a hozzá tartozó kód, majd a vezérlés az IF szerkezet után következő utasításra kerül.

IF
  cmd = PARAMETERS
    INT ch:
    SEQ
      input ? cmd
      ch := (INT cmd)
      Get.parameters(input,D0[ch],D1[ch],D2[ch],
                     bias[ch],Umin[ch],Umax[ch],
                     Shift[ch],sampling.time)
  cmd = SAMPLING
    SEQ
      input ? cmd -- change sampling time
      sampling.time := (INT cmd)
  cmd = EXIT
    SEQ
      Clear.DA.channels()
      STOP

Mi történik, ha történetesen egyik felsorolt feltétel sem teljesül? Ez a kérdés a nyelv tervezőinek is szöget ütött a fejébe. Alighanem hosszas tanácskozás után úgy döntöttek, az lesz a legjobb, ha ebben az esetben szép csendben leáll a program végrehajtása. Az IF tehát ilyenkor végrehajt egy STOP utasítást, a programozó pedig ül a sötét képernyő előtt és a fejét vakarja. Ha létezne a processzornak olyan utasítása is, amely nyakonönti a programozót egy vödör hideg vízzel, az Occam IF utasítása bizonyosan ezt hívná meg a STOP helyett.

Csak egy dolgot tehetünk: tételesen számbavesszük az összes lehetséges kombinációt és írunk egy-egy IF ágat nekik. Illetve, van egy ügyes trükk: a sor végére beírunk egy olyan feltételt, amely mindig teljesül, és a hozzá tartozó utasítással kiugrunk az IF szerkezetből:

IF
  (status BITAND PCSW) <> 0
    pelican.cross := TRUE
  TRUE
    SKIP

Bármilyen hihetetlenül is hangzik, az Occam nyelvben (legalábbis az INMOS Transputereken futó változatában) nincs operátorprecedencia. A fordító visszadob minden olyan kifejezést, amelyben egynél több műveleti jel van. Szorgalmasan rakhatjuk tehát kifele a zárójeleket, így lesz az A + B + C + D-ből (((A + B) + C) + D). A korábban már szóba került PID-szabályozó kimenő jelét pedig ez a képlethegy állítja elő:

Pout := K * (Uc - v)
Iout := (((((Uc - v)*K)*dt)/Ti) + ((dt/Ti) * (R - Vout))) + Iout
Dout := ((Dout*Td)/(Td + (n*dt))) +
        ((((((-1.0(REAL32))*Td)*n)/(Td + (n*dt)))*K)*(v - vold))
Vout := ((Pout + Iout) + Dout) + 130.0(REAL32)

Ez a zárójel-megmaradás elve: amit megtakarítunk azzal, hogy a blokkok határait indentációval jelöljük, azt utána el is használjuk az aritmetikai kifejezéseknél.

Az alapértelmezett adattípus az INT. Az összes többit gondolom utólag taknyolták bele a nyelvbe, mert más típusú változókat még összeadni is alig lehet. Két BYTE típusú változón például csak úgy lehet bitenkénti műveleteket végezni, ha előbb külön-külön INT-re alakítjuk őket, az eredményt meg vissza BYTE-ra. Mindezt zárójelek garmadájával:

a := (BYTE ((INT x) BITAND (INT y)))

Tanácsok kezdőknek

1. Globális változók pedig nincsenek!

2. A "SEQ i=0 FOR N" ciklus, tévedések elkerülése végett, N-1 ig tart.

3. Ugyancsak az előző FOR ciklushoz: i-t NEM kell deklarálni.
Minden más esetben minden más valtozót igen.

4. Az az If utasítás, amivel csak egyetlen feltételt vizsgálunk...

    IF a >= 1
      b := b + 2

...a deadlock előállításának legegyszerűbb módja. A fordító természetesen nem jelez hibát.

5. A T400-as transzputerre lefordított kód gond nélkül fut a T800-as transzputeren is. Egészen az első lebegőpontos utasításig, aholis hibajelzés nélkül elakad. A sötét képernyő mint hibajelzés fejleszti az intuíciós érzéket.

6. Egy lebegőpontos számot csak úgy lehet BYTE-ra konvertálni, hogy előbb INT-et csinálunk belőle, és ezt az INT-et konvertáljuk aztán BYTE-ra. Az INT segédváltozót nem lehet megspórolni.

7. A host computer minden erőforrása (képernyő, billentyűzet, merevlemez, I/O portok) osztott erőforrás, tehát ha két, párhuzamosan futó eljárásból is szeretnénk a képernyőre írni, akkor a fordító a teljesen jogos "Parallel Input on the channel to.server" üzenenetet küldi. A dolog ennek ellenére lehetséges, csatornamultiplexeléssel, de azt saját magunknak kell megírnunk.

* * *

Ahogy a bevezetőben írtam, a nyelv már jó ideje létezik, miért van, hogy ma mégsem Occamban programoznak a számítógépbuzi tinédzserek? A döntő ok szerintem az, hogy a párhuzamos programozás viszonylag szűk részterület: a feladatok döntő többségét szekvenciálisan is meg lehet oldani. Egy olyan nyelv tehát, amelyet párhuzamos programozásra hegyeztek ki, nem tud általánosan elterjedni.

Hozzátesz még ehhez, hogy az Occam nyelv első ránézésre frappáns filozófiájának ellenére van pár húzása, amivel képes megkeseríteni a programozó életét. Mintha a tervezők szándékosan ötvözték volnla a Pascal és az Assembly nyelv hátrányos tulajdonságait. Ehhez jön még a fejlesztőrendszer primitívsége (egy olyan korszakban, amikor már létezett a legendás Turbo C 2.0), igy aztán nem is lepődünk meg, hogy a www.inmos.com webeszájton a legfrissebb Transzputer-fejlesztések részletezése helyett ma már csak egy szomorkás hangvételű közlemény olvasható.

» Ugorj a hozzászóló ablakhoz

Megosztások Facebookon

Eddigi hozzászólások (12)

1

sbzx, 2006. január 15. vasárnap, 22:26 (#)

Ez a cikk egy csemege. Köszönöm szépen, a geek-ek nevében is. Az inmos link rossz, kéretik korrigálni.

2

stsmork, 2006. január 15. vasárnap, 22:31 (#)

Ez igen! Még arra se volt időm, hogy a cikk élesítése után reloadoljam a sesblog címlapot és máris belekommentezik valaki...A linket kijavítottam, kösz hogy szóltál.

3

ses, 2006. január 15. vasárnap, 22:39 (#)

Nagyon kiéhezett geekről lehet szó :)

4

pblue, 2006. január 15. vasárnap, 23:59 (#)

ez egy zseniális nyelv lehet, tetszik benne az a sok fasiszta megoldás. nem ironizálok, tényleg!

5

ferenc, 2006. január 16. hétfő, 03:35 (#)

regi szep idok:)

A CDL, (turbo)-prolog, MP/03 makroprocesszor kihalasarol is vannak elmeletek?

6

Ali, 2006. január 16. hétfő, 08:32 (#)

Régi szép idők, igen. Tényleg volt valami megkapó abban, ahogy a teljes hosszúságú PC kártyába rakosgatta az ember a kis transputer-modulokat. :)

Ma meg bármilyen egyszerű Java programozó is képes írni többszálon futó programot. Öregszünk. :)

7

FT, 2006. január 16. hétfő, 10:10 (#)

Jaj, az ám, valaha én is programoztam transputer-t... Kösz, hogy feltépted a sebeket :-)

8

sbzx, 2006. január 16. hétfő, 17:53 (#)

2: Hazugság az egész, legalább negyedórán át olvastam a cikket és bőven volt időm kommentezni is. Lassú az internetkapcsolatod! ;)

9

vinczej, 2006. január 16. hétfő, 19:39 (#)

Életem legvidámabb programozási konzultációján vettem részt. Köszi! :D

10

Tyb, 2007. május 11. péntek, 05:15 (#)

Négy nyelv közül választhattam: Smalltalk, Haskell, Prolog, Occam. Mind a négy jó szar, de tán még az Occam egész normális...

11

malaj, 2009. január 6. kedd, 10:34 (#)

Nem tudom milyen kihalt nyelvről beszéltek. Mi Occamot élesben tanuljuk az SZTE-n. Most van a második kurzusom ami ezzel a nyelvvel (is) foglalkozik. Kötelező programot is kellett írni Occamban. Mondjuk dokumentációt, tutorialt, meg minta progikat alig találtam hozzá a neten...

12

Author Profile Page ervin, 2009. január 6. kedd, 12:08 (#)

malaj, nem értek hozzá, de valószínűleg szanszkritot is lehet tanulni az egyetemen.


Hozzászólsz?

Igen

Hozzászólást csak névvel együtt fogadunk el. Ha linket írsz be, akkor előtte és utána hagyj egy szóközt, főleg akkor, ha zárójelbe teszed.


Az oldal tetejére | Szerzők, tudnivalók, feedek | sesblog és Kispad © 2003-2010 ervin, eszpee, stsmork