Executie asincrona

Categorii: Programare

29-Jan-2020 21:40 - 377 vizionari

Intr-un limbaj de programare a calculatoarelor exista tendinta sa gandesti sincron si sa programezi sincron, adica sa executi instructiunile una dupa alta si sa astepti rezultatul fiecarei instructiuni, apoi sa treci la urmatoarea si, daca te astepti ca o anumita instructiune sa necesite ceva timp de executie, sa o executi intr-un subprogram si in paralel cu programul principal.

Executia sincrona nu este fireasca, adica nu e normala, programul nu functioneaza optim sau la viteza maxima, se pierde mult timp cu asteptarea rezultatului anumitor instructiuni (citire-scriere de fisiere de pe hard disk si din reteaua de calculatoare) si daca organizezi executia programului in cateva subprograme care se executa in paralel (task de comunicatie in retea cu alte calculatoare, task de citire si scriere date pe hard disk, task de monitorizare evenimente si alarme, task de calcule complexe sau de prioritate mica, task de orice altceva nu vrei sa ocupe programul principal) efortul de creare/executare/gestionare/sincronizare secvente de cod paralel (taskuri) reduce mult performantele intregului program.

In Java un thread (task sau subproces) consuma 500k – 1M RAM (cu rezervarea zonei de stiva) si, de aceea, programele Java consuma multa multa memorie, pentru ca nu au un singur fir de executie (thread) sau task ci cateva sute in functie de aplicatie. Pentru fiecare client conectat la program se creaza cel putin un thread, care se termina la inchiderea conexiunii.

Dar exista si executia asincrona: tot programul se executa intr-un singur task (dezavantaj: desi ai mai mullte procesoare, numai unul e folosit) si apelurile de instructiuni care nu intorc rezultat imediat este verificat mai tarziu daca rezultatul e gata sau nu.

Avantajul executiei asincrone: nu se pierde timp cu asteptarea rezultatelor, nu se pierde timp cu crearea contextului de executie paralela, nu se aloca memorie pentru executie paralela pentru ca programul nu prevede secvente de cod cu executie paralela, nu este nevoie sa protejezi variabilele globale sa nu fie modificate necontrolat de alte taskuri paralele, pentru ca nu exista taskuri paralele.

In executia asincrona blocurile de instructiuni care trebuiau sa fie executate in paralel se executa cooperant, adica periodic in program este adaugata o instructiune speciala care schimba executia in alta rutina = se executa corutine = rutinele se executa cooperant = rutinele decid cand sa permita executia altor rutine, iar cooperarea executiei se face cand programul sta degeaba o vreme (mici intarzieri in executie din motive estetice sau special pentru schimbarea contextului de executie) si la apelul de functii care aduc greu rezultatul (acces la hard disk si la reteaua de calculatoare).

Cand eram in facultate, cu 20 si ceva de ani in urma, am creat un sistem in “C” si “asm” de executie in MS DOS a programelor in paralel.

Initial am gandit taskuri cu executie preemtiva, adica periodic sistemul DOS oprea executia programului principal si apela rutina 1C definita de mine ca sa schimbe contextul si sa execute taskul urmator, dar din motive tehnice (instabilitate sistem) am recurs la un multitaking cooperativ, adica programul schimba contextul manual in fiecare task. Daca un task uita sa schimbe contextul, celelalte taskuri nu se mai executau.

Schimband contextul suficient de repede aparea iluzia de executie simultana a taskurilor.

Trecerea de la un task la altul prin schimbarea contextului de executie (registri, stiva si contor program) am facut-o in limbajul asm, pentru ca este o operatie simpla dar critica si implementarea operatiei in limbajul “C” mi s-a parut nepotrivita sau chiar imposibila.

Rutina asm folosita este un exemplu foarte interesant (pentru nostalgici si pentru muzeul limbajelor de programare) de integrare a simbolurilor “C” cu “asm”:


;
; cod asm din 1996
;

STMAX	equ	20		;Nr. maxim de task-uri


init_arg macro v		;ds:bx=adresa cap vector(a elem. 0)
	lds bx,dword ptr v
	endm

.model huge

.code


public	_SwitchTask,_count
public	_vss,_vsp,_ntk,_ctk,_vprio

;Interfata cu "C"
_SwitchTask dw offset rutsw,seg rutsw	;pointer la rut1c
_count	dw offset cnt,seg cnt		;pointer la var interna
_vss	dw offset p_ss,seg p_ss		;pointer la vector de ss
_vsp	dw offset p_sp,seg p_sp		;pointer la vector de sp
_ntk	dw offset n_tk,seg n_tk		;pointer la nr. total de task-uri
_ctk	dw offset c_tk,seg c_tk		;pointer la ix task curent
_vprio	dw offset pr_io,seg pr_io	;pointer la vector de prioritati

;Var interne
cnt	dw ?				;variabila interna = nr apeluri ...
rez	dw ?				;variabila de depanare
p_ss	dw STMAX dup (?)
p_sp	dw STMAX dup (?)
pr_io	dw STMAX dup (?)
n_tk	dw ?
c_tk	dw ?


; Rutina din .asm care executa switch task

rutsw	proc far
	cmp cs:cnt,0
	jz ok
	dec cs:cnt	;Dec *count
	ret
ok:			;Switch to next task
	pushf
	cli
	push ax
	push bx
	push cx
	push dx
	push ds
	push es
	push si
	push di
	push bp

	mov bp,sp
	mov ax,cs:n_tk		;ax=nr. taskuri+1 (ca in "C")
	dec ax			;ax=nr. taskuri
	mov si,cs:c_tk		;si=ctk (curent)
	mov di,si		;di=ctk
	cmp ax,si
	jnz nu_e
	mov di,-1		;if (ctk==ntk) di=0;
nu_e:
	inc di			;else  di=si+1;
;ax=ntk si=ctk di=next task

;Update curent task
	mov cs:c_tk,di
;di=2*next
	add di,di
;si=2*ctk
	add si,si

;Store ss,sp in curent task
	init_arg _vss
	mov ds:[bx+si],ss
	init_arg _vsp
	mov ds:[bx+si],sp
;Load  ss,sp din next task
	init_arg _vss
	mov ax,ds:[bx+di]
	mov ss,ax
	init_arg _vsp
	mov ax,ds:[bx+di]
	mov sp,ax

;Load prioritate din next task
	init_arg _vprio
	mov ax,ds:[bx+di]
;Store prioritate in 
	mov cs:cnt,ax

	pop bp
	pop di
	pop si
	pop es
	pop ds
	pop dx
	pop cx
	pop bx
	pop ax
	popf
	ret
rutsw	endp


end



Observatii:
Variabila vprio in “C” (vizibila ca _vprio in “asm”) este un vector de prioritati in executia taskurilor.
Variabila cnt contine prioritatea de executie a taskului curent, este initializata de valoarea din vprio, scade cu cu o unitate la fiecare apel de schimbare context si cand ajunge la zero se executa schimbarea de context, adica se executa taskul urmator.
p_ss, p_sp si pr_io sunt vectori care contin stiva (ss:sp) si prioritatea de executie a taskului.

Folosind rutina asm de mai sus am rulat cu succes in multitaking cooperant, prin anii 90, programe in mod grafic (rezolutie 640x480 si 800x600 cu driver special SVGA 256 de la Borland) de achizitie de date si de automatizare procese in laboratorul universitatii, niciodata in industrie.

Pentru industrie, in vremea aceea, profesorii mei foloseau sistemul RTKernel, un sistem robust si eficient de multitaking pentru DOS.



Ultimele pagini: RSS

Alte adrese de Internet

Categorii

Istoric


Atentie: Continutul acestui server reprezinta ideile mele si acestea pot fi gresite.