Skip to content
Ridgun
English

Ridgun

The language itself: a compiler that compiles itself, the stable project tool, the specification and the book

07-goi-mailako-funtzioak-eta-dag-paralelismoa.md259 lines · 14.3 KBRaw
1# 07. kapitulua: Goi-Mailako Funtzioak eta Izerdirik Gabeko Paralelismoa (Ez `Mutex`-ik, ez `Deadlock`-ik)
2
3Ia programazio-lengoaia guztietan une dramatiko bat egoten da proiektu baten bizitzan: bilera batean norbaitek honako hau esaten duen eguna:
4
5> *"Hau motel doa. Hariekin paralelizatu behar da."*
6
7Isiltasuna nagusitzen da aretoan. Mundu guztiak daki zer datorren ondoren: asteak ilargi beteko astearteetan bakarrik gertatzen diren *race condition* mamuak, elkar-blokeatzeak (*deadlock*-ak) eta erreproduzitu ezinezko erroreak ehizatzen.
8
9Ridgunen, norbaitek *"paralelizatu egin behar da"* esaten duenean, nahikoa da zerrenda-eragiketen aurrean `||` jartzea (**`||map`** eta **`||filter`**) edo zure kalkuluak `->` geziak dituen `||` bloke paralelo batean jartzea eta kafe bat hartzera joatea.
10
11Zergatik? **Zure funtzioak puruak direnean eta zure datuak aldaezinak direnean lehenespenez, aldi berean lanean ari diren bi PUZ nukleok ezin diotelako inoiz elkarri mahuka zapaldu**.
12
13Ikus dezagun urratsez urrats.
14
15---
16
17## 7.1. Funtzioak jasotzen dituzten funtzioak: `map`, `||map`, `filter`, `||filter` eta `fold`
18
19Ridgunen funtzio bat beste balio bat gehiago da. Parametro bidez pasa dezakezu `7` zenbakia pasatzen duzun bezalaxe.
20
21Lengoaiak bilduma-konbinatzaileak dakartza seriean, zerrendak eskuzko errekurtsio bakar bat ere idatzi gabe eraldatzeko, bai modu sekuentzialean (`map`, `filter`, `fold`) bai nukleo anitzeko modu paraleloan `||` aurrizkiarekin (**`||map`**, **`||filter`**):
22
23### 1. `map` (sekuentziala) eta `||map` (nukleo anitzeko paraleloa)
24Demagun `garestia Int => Int` funtzio bat eta zenbakiak dituen zerrenda bat dituzula:
25
26```ridgun
27garestia Int => Int
28garestia x = (x * x) + (x * 3)
29
30main => ()
31main =
32 datuak = [1, 2, 3, 4, 5, 6, 7, 8]
33 -- Sekuentzial zorrotza ezkerretik eskuinera:
34 r1 = map garestia datuak
35 -- Nukleo anitzeko paraleloa `||` aurrizkiarekin:
36 r2 = ||map garestia datuak
37 echo r1
38 echo r2
39```
40
41- **`map`**-ekin, Ridgunek funtzioa elementuz elementu aplikatzen du ezkerretik eskuinera uneko harian (zerrenda txikietarako edo ordenatutako sarrera/irteera efektuak dituzten funtzioetarako bikaina).
42- **`||map`**-ekin, Ridgunen runtime-ak `datuak` zerrendako elementuak zure prozesadoreko hari langileen artean banatzen ditu eta azken zerrenda **zehazki ordena berean** berreraikitzen du, `||map f xs == map f xs` bermatuz.
43
44### 2. `filter` (sekuentziala) eta `||filter` (paraleloa): Baldintza bat betetzen dutenekin bakarrik geratzea
45`Bool` itzultzen duen funtzio bat (*predikatu* bat) eta zerrenda bat pasatzen dizkiozu:
46
47```ridgun
48positiboa da Int => Bool
49positiboa da n = n > 0
50
51main => ()
52main =
53 garbiak = filter positiboa da [-5, 10, 0, -2, 42]
54 garbiak_par = ||filter positiboa da [-5, 10, 0, -2, 42]
55 echo garbiak -- [10, 42] inprimatzen du
56 echo garbiak_par -- [10, 42] inprimatzen du
57```
58
59### 3. `fold`: Zerrenda oso bat emaitza bakar batera murriztea
60Bi argumentuko funtzio konbinatzaile bat `(Metatua Elementua => Metatua)`, hasierako balioa eta zerrenda pasatzen dizkiozu:
61
62```ridgun
63metatu biderkadura Int Int => Int
64metatu biderkadura acc x = acc * x
65
66main => ()
67main =
68 echo (fold metatu biderkadura 1 [2, 3, 4, 5]) -- 1 * 2 * 3 * 4 * 5 = 120
69```
70
71### Nola idazten dut nik beste funtzio bat jasotzen duen nire funtzio propioa?
72Oso erraz: mota-sinaduran funtzio gonbidatuaren sinadura parentesi artean jartzen duzu `(Sarrera => Irteera)`:
73
74```ridgun
75exekutatu bi aldiz (A => A) A => A
76exekutatu bi aldiz f x = f (f x)
77```
78
79### 4. Ezkerretik eskuinerako hodi arinak (`->`) — % 100 Ridgun estiloa!
80Hainbat eraldaketa kateatzen dituzunean (`map`, `filter`, `fold` edo zure funtzio eta `op ::` eragile propioak), parentesiak eskuinetik ezkerrera habiaratzeak (`compact l (filter g (map f xs))`) barrutik kanpora irakurtzera behartzen zaitu.
81
82Ridgunen, **gomendatutako estilo idiomatikoa (`2b`)** ezkerretik eskuinera irakurtzen da **`->`** hodi-eragilearekin, non **ezkerretik datorrena beti hurrengo funtzioaren edo `op ::`-aren lehen argumentu gisa sartzen den**:
83
84```ridgun
85-- Ridgunen gomendatutako estilo idiomatikoa (2b):
86xs map (x => x * 2) -> filter (x => x > 10) -> fold (acc x => acc + x) 0 -> emaitza
87
88-- Eta funtzio batek tupla bat itzultzen badu, maskara batekin iragazi eta berez desegituratzen da:
89parseatu paketea "v0.9.9" -> (+ _ _ +) -> konbinatu kodea -> laburpena
90```
91
92Gainera, albo-ondorioak dituen edozein adierazpen (fitxategi bat idatzi, sistemari deitu) bere lerroan bakarrik jar daiteke `echo` edo `_ =` aldagai muturik gabe, edo `-> ()`-n amaitu bere itzulera esplizituki baztertu nahi baduzu.
93
94### 5. Itxitura lexikoak eta funtzio anonimoak (`(x => expr)`, `(acc x => expr)`)
95Unean bertan funtzio labur bat pasa nahi duzunean edo uneko esparruko aldagai lokalak (desplazamendu bat, aurrizki bat edo saio aktibo bat) atzeman nahi dituzunean funtzio global laguntzaile bat deklaratu gabe, **itxitura lexiko** bat erabiltzen duzu parentesi artean **`=>`**-rekin:
96
97```ridgun
98fn :: prozesatu saioarekin => [Int]
99 prozesatu saioarekin =
100 oinarria = 10
101 hobaria = 3
102 batuak = map (x => x + oinarria) [1, 2, 3]
103 iragaziak = filter (x => x >= hobaria) [1, 2, 3, 4]
104 guztira = fold (acc x => acc + x + hobaria) 0 [1, 2, 3]
105 bikoteak = map ((a b) => a + b + oinarria) [(1, 2), (3, 4)]
106 batuak :: iragaziak :: [guztira] :: bikoteak
107```
108
109Bost arauk mantentzen dituzte lambdak garbi eta anbiguotasunik gabe:
110- **Parentesiak derrigorrezkoak**: lambda oro beti doa parentesi artean `(... => ...)`.
111- **Parametrorik gabeko lambda (`(=> expr)`)**: `fn :: main => ()`-k `=>` aurretik `()` ez daraman bezala, parametrorik gabeko lambda `(=> expr)` idazten da (eta bere mota `(=> Mota)` da), inoiz ez `(() => expr)`.
112- **Parametro anitz vs. tupla (`(a b => ...)` vs. `((a b) => ...)`)**: `(acc x => ...)`-k bi parametro bereiz jasotzen ditu; `((a b) => ...)`-k tupla bakar bat jasotzen du eta desegituratu egiten du.
113- **Lerro anitzeko gorputza**: `=>` ondoren lerro-jauzi bat jartzen baduzu, koskatutako urrats anitzeko bloke bat idatz dezakezu ixteko `)` aurretik:
114 ```ridgun
115 bikoitzak = map (x =>
116 urratsa = x * 2
117 urratsa + oinarria
118 ) [1, 2, 3]
119 ```
120- **Maiuskulak vs. minuskulak**: `(Conn => Conn)` funtzio-mota bat da (hasiera maiuskulaz) eta `(conn => conn)` lambda balio bat da (hasiera minuskulaz).
121
122Atzemandako aldagaiak (`oinarria`, `hobaria`) automatikoki paketatzen dira memoria-kudeaketa deterministarekin (`RMM1`/`RMM2`), ihesik gabe eta zabor-biltzailearen etenik gabe.
123
124---
125
126## 7.2. Koroaren bitxia: `||` blokeak `->` geziekin Grafo Paralelo (DAG) gisa
127
128Hemen aldatzen du Ridgunek zure pentsamoldea.
129
130Lengoaia inperatibo arrunt batean, hiru lerro jarraian idazten dituzunean:
1311. Lehenengo 1. lerroa exekutatzen da.
1322. Gero 2. lerroa (nahiz eta 1. lerroarekin inolako zerikusirik ez izan).
1333. Gero 3. lerroa.
134
135Hori ordenagailua betaurreko ilunekin lan egitera behartzea da. Ridgunen, **`||`** barra paraleloak **`->`** geziekin erabiltzen dituzunean (`where` bezalako inongo hitz gakorik idatzi beharrik gabe), ez zara agindu sekuentzialak ematen ari: zein kalkuluk zein aldagai elikatzen duen dioen **Grafo Azikliko Zuzendu (DAG)** bat marrazten ari zara:
136
137```ridgun
138fib Int => Int
139fib 0 = 0
140fib 1 = 1
141fib n = (fib (n - 1)) + (fib (n - 2))
142
143fact Int => Int
144fact 0 = 1
145fact n = n * (fact (n - 1))
146
147super kalkulua => Int
148super kalkulua = ezker_adarra + eskuin_adarra
149 || fib 10 -> ezker_adarra
150 || fact 6 -> eskuin_adarra
151```
152
153Irakurri ozenki:
154- *"`ezker_adarra + eskuin_adarra` kalkulatu nahi dut, bitartean paraleloan (`||`) `fib 10` `ezker_adarra`-ra doalarik (`->`) eta `fact 6` `eskuin_adarra`-ra doalarik (`->`)."*
155
156### Zer egiten duen `ridgunc`-ek oihalaren atzean zure `||` lerroekin:
1571. `->` geziei begiratzen die eta mendekotasun-zuhaitza (DAG) osatzen du.
1582. **Zikloak estatikoki ehizatzen ditu**: hutsez `|| a + 1 -> b` eta `|| b + 1 -> a` idazten badituzu, konpiladoreak berehala esaten dizu *"Geldi hor! Mendekotasun zirkular bat dago"* eta ez dizu blokeo infinito bat konpilatzen uzten.
1593. Ikusten du `fib 10` eta `fact 6` ez daudela elkarren menpe, beraz argi berdea dute paraleloan exekutatzeko `ezker_adarra + eskuin_adarra`-n batu aurretik.
160
161Are gehiago, hainbat solairuko hodi bat munta dezakezu `||` bloke berean —adierazpenaren oinean zein `=` ondoren bertan— eta konpiladoreak bere kabuz ondorioztatuko du zein solairu joan daitezkeen batera eta zeintzuek itxaron behar duten:
162
163```ridgun
164hiruhileko balantzea Int => Int
165hiruhileko balantzea oinarria =
166 || oinarria * 100 -> salmenta_gordinak
167 || oinarria * 40 -> kostu_finkoak
168 || salmenta_gordinak - kostu_finkoak -> irabazi_gordina ||.
169 irabazi_gordina * 80 / 100
170```
171
172Erreparatu azken `||` lerroaren amaierako **`||.`** xehetasunari: `|.` ikurrak habiaratutako zain-zuhaitz bat ixten duen bezalaxe, **`||.`** ikurrak `||` bloke paraleloa ixten du azpian sententzia sekuentzial arruntek jarraitzen dutenean. Hemen `salmenta_gordinak` eta `kostu_finkoak` paraleloan abiarazten dira; biak prest daudenean `irabazi_gordina` kalkulatzen da, eta azkenik `irabazi_gordina * 80 / 100` itzultzen da. Dena `async`, `Future`, `Promise` edo `Mutex` bakar bat ere gabe.
173
174---
175
176## 7.3. Izenik gabeko `||` lasterbidea: Tupla bat itzultzen duten hodi paraleloak
177
178Eta bi edo hiru kalkulu paraleloan abiarazi eta tupla batean elkarrekin jaso nahi badituzu `->` bidez aldagai-izenak asmatu ere egin gabe?
179
180`||` zuzenean erabiltzen duzu:
181
182```ridgun
183bikote paraleloa Int => (Int, Int)
184bikote paraleloa n =
185 || fib n
186 || fact n
187```
188
189Zuzena, bisuala eta `(fib n, fact n)` tupla itzultzen du.
190
191---
192
193## 7.4. Eskuzko bolantea nahi duzunean: Agenteen Eredu Natiboa (`agent`, `send ... to`, `receive from`) eta CSP Kanalak
194
195`||` eta `||map`-en paralelismo automatikoak kasuen % 90a estaltzen du. Baina zerbitzari iraunkor bat, egoera isolatuko erabiltzaile-saioak, lan-ilara bat edo ekoizle-kontsumitzaile hodi bat programatzen ari zarenean, **memoria isolatuko agente konkurrenteak eta motadun mezu-igarotzea** nahi dituzu.
196
197Ridgunen ez dago `actor ::` bezalako sintaxi arrotz bat asmatu beharrik: **edozein funtzio arrunt (`fn ::` edo `op ::`) agente konkurrente bihurtzen da lerro bakar batean `agent` erabiliz**:
198
199### 1. Egoerarik gabeko eta egoera isolatuko agenteak (`agent`, `send ... to`, `receive from`, `stop`)
200- `ag = agent (nire funtzioa)`: `M => R` funtzio bat `Agent M R` motako egoerarik gabeko agente bihurtzen du.
201- `ag = agent (egoeradun funtzioa) hasierako_egoera`: `S M => S` (edo `S M => S R`) funtzio bat egoera isolatuko agente bihurtzen du, mezuz mezu bere egoera gogoratuz `Mutex` bakar bat ere gabe.
202- `send msg to ag` (edo argumentu askorekin: `send a b to ag`): motadun mezu bat bidaltzen du agentearen postontzira.
203- `em = receive from ag` (edo `try receive from ag` blokeatu gabe): agentearen irteerako postontziko hurrengo erantzuna jasotzen du.
204- `stop ag`: agentea gelditzen du eta bere egoera automatikoki askatzen du `RMM1`/`RMM2` bidez.
205
206```ridgun
207fn :: gehitu bisita Int Int => Int
208 gehitu bisita met n = met + n
209
210fn :: prozesatu sorta Int Int => Int
211 prozesatu sorta id t = (id * 100) + t
212
213fn :: main => ()
214 main =
215 kontagailua = agent (gehitu bisita) 10
216 send 5 to kontagailua
217 send 7 to kontagailua
218 b1 = receive from kontagailua
219 b2 = receive from kontagailua
220 stop kontagailua
221
222 langilea = agent (prozesatu sorta)
223 send 3 25 to langilea
224 sorta = receive from langilea
225 stop langilea
226 echo "Bisitak:" b1 "->" b2 "| Sorta:" sorta
227```
228
229### 2. Aukeraketa adimenduna konpilazio-garaian (*Green Threads* vs. Kerneleko Benetako Hariak)
230Ohiko galdera bat hau da: *"`||` blokeek, `||map`-ek eta `agent` agenteek beti sortzen al dituzte sistema eragilearen hariak, ala ataza arin gisa exekutatzen dira?"*
231
232Ridgunen, **konpiladoreak zure ordez hartzen du erabaki hori funtzio bakoitza estatikoki aztertuz**:
233- **Funtzio arinak (`TierTrivial` / `StratInlineSequential`)**: `||`, `||map`, `||filter` edo `agent`-era pasatzen duzun funtzioak sakonera txikia badu (aritmetika, kateen formateatzea, HTTP saio baten egoera-trantsizio azkarra), sistema eragilearen hariak berotzeak kalkuluak berak baino gehiago balioko luke. Konpiladoreak *green-thread* urrats ultraarin gisa exekutatzen du, `SYS_clone` gainkargarik gabe.
234- **Kalkulu astunak edo errekurtsibo sakonak (`TierDynamicRecursive` / `TierHeavyCPU`)**: Funtzioak errekurtsio sakona (`fib` edo `fact` bezala) edo tentsore-aljebra (`einsum`, `hadamard`, `@`) badu, konpiladoreak automatikoki sortzen du nukleo anitzeko bidalketa **kerneleko benetako harien gainean (`SYS_clone`)**, `||` adarrak edo `agent`-aren mezu-ilara zure PUZ nukleo guztietan benetako paraleloan prozesatuz.
235
236### 3. Mezuak kanal mugatuetatik pasatzea (`new bounded channel`)
237Go edo Erlangen bezalaxe, agenteez gain hodi esplizituak ere sor ditzakezu etapak komunikatzeko:
238
239- `new bounded channel edukiera`: `edukiera` mezurentzako lekua duen kanal bat sortzen du.
240- `send through balioa kanala` (edo `chan send balioa kanala`): mezu bat hodian sartzen du.
241- `receive from kanala` (edo `chan recv kanala`): hurrengo mezua hoditik ateratzen du.
242- `try receive from kanala`: mezurik dagoen begiratzen du zain geratu gabe; `Some balioa` edo `None` itzultzen du.
243- `receive from timeout kanala ticks`: denbora-muga bat itxaroten du eta `Ok balioa` edo `Err Timeout` itzultzen du.
244
245```ridgun
246fn :: main => ()
247 main =
248 hodia = new bounded channel 4
249 _ = send through "A ataza osatuta" hodia
250 _ = send through "B ataza osatuta" hodia
251 lehena = receive from hodia
252 bigarrena = receive from hodia
253 echo lehena "|" bigarrena
254```
255
256### 4. Hustubide-agenteak (*fire-and-forget* `agent sink`), postontzi globalak (`agent ::`) eta proba deterministak (`with agent`)
257Ikuskaritza asinkronoa, metrikak edo trazabilitate zeharkakoa behar dituzunean sinadura guztiak zikindu gabe eta irakurri gabeko erantzunak irteera-ilaran pilatu gabe, Ridgunek **hustubide-agenteak (`agent sink`)** eta **goi-mailako postontzi globalak (`agent ::`)** eskaintzen ditu (`agent dropped`, `agent flush`, `agent state` eta `with agent audit_log = espioia do ...`), baita langile anitzen artean parteka daitezkeen `Channel` eta `Agent` deskribatzaileak ere.
258
259Eta orain, lotu gerrikoa **08. kapitulurako**, amaiera handirako: **konpilazio-garaian egiaztatutako dimentsioak dituzten tentsoreak, esparru-aldagaiak (`context`/`shared`), memoria-eskualde deterministak, MMIO hardwarerako sarbide zuzena eta UNIX/POSIX sistemen programazioa**.