Synchroniczna interpretacja programu i asynchroniczna implementacja programu to są dwie przeciwstawne rzeczy. Jedyne, co je łączy jest warunek, żeby wynik synchronicznego obliczenia był taki sam jak wynik asynchronicznego obliczenia.
Jak interpretować
Antionowe programy trzeba interpretować (wykonywać w naszych myślach) synchronicznie ze względu na operacje tx(), t() i o(). Program interpretujemy instrukcja po instrukcji, tak jak w zwykłym programie. U nas bedzie ogólnie wiecej antionów w globalnym dyskretnym czasie τ. Na poczatku jest jeden antion. Oznaczmy czas wykonania pierwszej instrukcji tx(1) jako τ=0. W czasie τ= 1, po wykonaniu tx(1), bedą w przestrzeni dwa antiony. W czasie τ=2, po wykonaniu tx(2), cztery antiony, itd. Ogólnie, jeśli w interpretacyjnym obliczeniu dojdziemy do którejś z operacji tx(), t(), o(), to wszystkie antiony, muszą dojść do tej operacji, staną „przed progiem” w jednym szeregu, a dopiero kiedy już są zrównane, wykonują tę operacje synchronicznie, są „odpalone” równocześnie.
Dla lokalnych operacji na punktowych i antionowych danych równoczesne odpalanie nie jest wymagane. Czyli w niektórych punktach lokalne obliczenia mogą trwac dłużej niż w innych. Reguła „odpalic równoczesnie” dotyczy tylko tx(), t(), i o(). Tę regułę łatwo przestrzegać, jeśli program jest napisany sposobem „single instruction stream„. Stan obliczenia można wtedy utożsamić z jednym miejscem w programie.
Skad sie bierze „currentpoint”
Pokażemy teraz program, w którym jest wykorzystana pętla „while” do sterowania sekwencji operacji tx(). Jest to program typu „single instruction stream”. Specjalnoscią tego programu jest o, że on w czasie ekspansji „markuje” punkty (albo antiony) tak, że otrzymamy zgodę z wartościami „ukrytej” zmiennej systemowej „currentpoint” (hidden variable).
#include „prob-16-kern.h”
Wynik programu będzie w antionowej zmiennej „MARK”. Najpierw do zmiennej punktowej „pointdata” damy jedynkę, zeby oznaczyć, ze to jest punkt „zero”. Zmienna antionowa GEN będzie sterowac pętlą „while”, wartości GEN będą po kolei 1,2,4. Tak więc sekwencja tx(GEN) bedzie tx(1), tx(2), tx(4). Osiem rozmnozonych antionów wydrukuje następujacy wynik:
currentpoint MARK 0 0
currentpoint MARK 4 4
currentpoint MARK 2 2
currentpoint MARK 6 6
currentpoint MARK 1 1
currentpoint MARK 5 5
currentpoint MARK 3 3
currentpoint MARK 7 7
Czyli rzeczywiscie MARK jest równe currentpoint. Pętla nie korzysta z „currentpoint”. Mając w rezultacie we wszystkich punktach przestrzeni wartosci MARK identyczne jak „currentpoint” mamy wszystko, żeby się móc orientować „gdzie jesteśmy”. Wartości zmiennej systemowej „currentpoint” możemy sobie sami wytworzyć. Ten trik musi byc jednakze wykonany w czasie ekspansji !!
Tym niemniej we wszystkich następnych programach będziemy się swobodnie posługiwac zmienną „currentpoint”, jesli zajdzie taka potrzeba. Będzie tak prościej. Trzeba także pamiętać, że zmienna systemowa „currentpoint” ma być „read only”!
Kilka szczegółów
Szczegół (1). Obliczenie, w sensie synchronicznej interpretacji programu, powinno wyglądać tak, aby w jednym punkcie przestrzeni był najwyżej jeden antion. Gdyby było wiecej antionów w punkcie, mielibysmy niejednoznaczność, niedeterminizm. Trzeba pisać programy tak, aby było jednoznacznie.
Szczegół (2). Z operacją tx() jest związany nastepujacy dylemat. Nowy antion jest na rysunku rozmnozenia oznaczony czerwono.
Czerowny antion jest kopią pierwotnego zielonego antionu. Tak sie umawiamy. Nowy antion będzie miał identyczne wartosci danych pisanych duzymi literami. To, co okreslamy jako dylemat dotyczy danych w punkcie „v ^ r”, nawet samego punktu „v ^ r”. Mamy tx() interpretowac jako rozmnożenie antionu i punktu równoczesnie ? Ma tx() opisywac zarówno ekspansję antionów jak i punktów ? Chodzi o jakiś nowy punkt przestrzeni, czy raczej nowy antion ma skoczyć do wcześniej przygotowanego punktu we wcześniej przygotowanej przestrzeni ? Jak to jest z danymi w punkcie „v ^ r” ? W aktualnej implementacji prob-16-kern.cpp jest tak, że nowy antion wykonując tx() przejdzie do wcześniej przygotowanego punktu we wcześniej przygotowanej przestrzeni. Jednak ten „nowy” punkt będzie miał „nieokreslone” dane punktowe.
Szczegół (3). Kolejnosć punktów 0,4,2,6,1,3,5,7 w wydrukach cout wynika z funkcji kernelu. W rozdziale o implementacji omówimy szczegółowo skad sie to bierze. Dla samej aplikacji ta kolejnosć nie ma znaczenia, bo jakaś kolejność przecież musi być, a własnie taka kolejnosć jest najprostsza z punktu widzenia implementacji.