Vektorové počítače mívají obvykle různé funkční jednotky pro skalární a vektorové operace nebo alespoň různou metodu zpracování vektorů a skalárů ( skalárem se - na rozdíl od vektoru - rozumí jedno reálné číslo). Jejich architektura je charakterizována několika specifickými vlastnostmi, z nichž vektorové operace realizované tzv. pipeliningem a organizace přenosu mezi procesorem a pamětí jsou nejdůležitější.
Pouze navenek se zdá, že se všech (řekněme) 64 složek obou vektorů sčítá najednou. Ve skutečnosti to probíhá trochu jinak. Mechanizmus, na němž je paralelita ve vektorovém procesoru založena, se nazývá pipelining (“protlačování potrubím, rourou”) a je založen na těchto zásadách:
- vektorové funkční jednotky jsou segmentovány na části se specifickým určením: provádějí obvykle dílčí operace (elementární kroky), které trvají jeden takt,
- složité instrukce (např. operace v pohyblivé čárce) se rozčlení na elementární kroky, z nichž každý se může realizovat v jednom taktu,
- každý krok se může realizovat jen ve specifickém segmentu funkční jednotky,
- nové operandy se mohou začít zpracovávat v každém dalším taktu a rozpracované operace se posouvají po běžícím páse (“protlačují se rourou”) v jednotlivých segmentech funkční jednotky,
- na konci roury vystupuje v každém taktu jeden hotový výsledek, např. operace v pohyblivé čárce.
Pipelining si nyní vysvětlíme detailně na zpracování operace sčítání dvou vektorů ve vektorovém procesoru. Mějme dva vektory (dostatečné délky)
(x1,x2,...)x =
jejichž součet má být umístěn v paměti jako vektor z.
Operace sčítání dvou čísel (skalárů - nikoli vektorů!) je v počítači obvykle tvořena těmito dílčími úkony:
- Přečti oba sčítance z registrů do funkční jednotky.
- Posuň mantisu s menším exponentem na úroveň druhé mantisy.
- Sečti mantisy.
- Normalizuj a vypočti exponent.
- Ulož součet do registru.
Schéma 1 znázorňuje postup zpracování, který nyní popíšeme. Předpokládejme pro názornost, že funkční jednotka našeho vektorového počítače má alespoň 5 segmentů, z nichž každý realizuje některý z úkonů 1 - 5, a že každý z těchto úkonů je realizován v jednom cyklu procesoru:
- v 1. cyklu zavede 1. segment do funkční jednotky složky x1 a y1,
- ve 2. cyklu zavede 1. segment do funkční jednotky složky x2 a y2 a současně 2. segment posune menší z mantis složek x1, y1 (2. dílčí úkon),
- ve 3. cyklu zavede 1. segment do funkční jednotky složky x3 a y3, současně 2. segment posune menší z mantis složek x2 a y2 a současně 3. segment sečte mantisy složek x1 a y1 (3. dílčí úkon),
- atd.
Ve schématu vidíme, že teprve po pěti cyklech (v 6. cyklu) je v registru k dispozici prvý součet
z1=x1+y1,
od této chvíle však je do registru dodána v každém cyklu jedna další složka vektoru z.
Schéma 1
Je tedy zřejmé, že je třeba n+4 cyklů na sečtení dvou vektorů o n složkách. (To je pravda ovšem jen v tom případě, že všechny složky obou vektorů byly v registrech. Registry však mají určitou délku, která je specifická pro každý vektorový počítač; bývá 64 nebo 128. Jestliže mají sčítané vektory větší délku, je v registrech k dispozici nejprve prvých 64 složek obou vektorů a zbývající složky se zpracovávají až v “dalších etapách”. To může znamenat různý postup podle architektury počítače: další složky mohou být v jiném registru nebo se musí použité registry znovu naplnit atd.).
Vraťme se nyní k našemu časovému diagramu: Po zahájení operace sčítání neprodukuje funkční jednotka po dobu čtyř cyklů žádný výsledek. Tento čas se nazývá startovací (“startup time”) a je také pro architekturu vektorového počítače charakteristický. Je potřeba ho brát v úvahu při všech kalkulacích souvisejících s vektorizací programů. Kompilátory na vektorových počítačích dělají vektorizaci do značné míry automaticky a znají-li délku zpracovávaných vektorů, provádějí i tuto kalkulaci. V některých případech to může vést k tomu, že kompilátor “nepustí” krátké vektory do vektorových funkčních jednotek, ale nechá je postupně zpracovat ve skalární jednotce, jejíž rychlost nesouvisí obecně vůbec s rychlostí vektorových jednotek - i tuto skutečnost musí kompilátor “uvážit”.
V reálném vektorovém počítači je pipelining ještě rafinovanější, než jsme si na našem jednoduchém příkladě ukázali, takže zrychlení (“speedup”) vektorového počítače zvyšuje ještě více. Např. ve schématu 1 si všimněme, že v 7. cyklu už 1. segment “zahálí”. Architektura počítače umožňuje, aby tyto segmenty, kterých již není třeba pro dobíhající (vektorovou) operaci, mohly začít zpracovávat následující operaci. Kompilátor o tom ví a zařídí to, kdykoli to je možné (když za vektorovou operací následují skalární operace nebo skoky apod., musí nechat segmenty vektorové funkční jednotky zahálet; optimalizující kompilátory se však i v těchto případech snaží “pro ně najít práci” např. přeskupením programových instrukcí).
Druhou významnou specifickou vlastností vektorových počítačů je organizace paměti, kterou popíšeme nyní.
Registry jsou velmi rychlá paměťová zařízení, která mohou předat procesoru svůj obsah v jednom taktu. Bohužel jsou tak drahé, že jich v počítači musí být relativně málo - ne více než několik set. Vektorový počítač však musí mít v operační paměti miliony či miliardy čísel a kdyby měla být tato paměť konstruována jako registry, dosáhla by cena počítače astronomické výše. Háček je v tom, jak zorganizovat činnost počítače, aby spolupráce mezi procesorem a obecně “pomalejší” pamětí byla dostatečně rychlá. Nejběžnější způsob je založen na tom, že se paměť rozdělí do několika - např. 64 nebo 128 - bloků (anglicky “bank”) a procesor může současně komunikovat s každým z nich. Tak se kompenzuje to, že každé čtení z paměti nebo zápis do ní trvá několik taktů. Při dobré vyváženosti systému a správně napsaném programu může procesor i z “levnější” a “pomalejší” paměti dostat v každém taktu jednu položku: kompilátor zajistí, aby se složky všech vektorů ukládaly postupně do různých bloků. Teprve když byly všechny bloky využity a vektor není ještě celý umístěn, začne se zase od začátku. Za chvíli si to budeme ilustrovat na příkladě.
Doba, kterou paměť potřebuje k tomu, aby realizovala jednu komunikaci mezi blokem a procesorem, se nazývá cyklus bloku . Je zřejmé, že systém je vyvážený (tj. umožní, aby se do procesoru v každém taktu dostala jedna položka), jestliže je paměť rozdělena alespoň do tolika bloků, kolik taktů trvá jeden cyklus bloku.
Nejnáročnější na spolupráci mezi procesorem a pamětí bývají DO-smyčky , pomocí nichž jsou mj. implementovány vektorové operace; jestliže jsou složky vektorů umístěny postupně v různých blocích, vybírají se operandy operací ve smyčce postupně stále z různých bloků paměti.
Cyklus bloku trvá ve vektorových počítačích obvykle 4 nebo 8 taktů, takže stačí rozdělit paměť do 4 nebo 8 bloků.
Uvažujme nyní pro jednoduchost operaci
y = x + u
v níž je na vstupu jen jeden proměnný vektor
(x1,x2,...,x18)x =
a kde
(1.0, 1.0,...,1.0)u =
Vektor x bude kompilátorem uložen v paměti rozdělené do 8 bloků tak, jak je znázorněno ve schématu 2, a operace přičítání konstanty bude kompilátorem přeložena do DO-smyčky
DO 10 I = 1,18 Y(I) = X(I) + 1.0 10 CONTINUE
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
|||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
x1 |
x2 |
x3 |
x4 |
x5 |
x6 |
x7 |
x8 |
|||||||
x9 |
x10 |
x11 |
x12 |
x13 |
x14 |
x15 |
x16 |
|||||||
x17 |
x18 |
|||||||||||||
Schéma 2
Prvky vektoru x uložené postupně cyklicky v různých blocích podle schématu 2 se do procesoru zpracovávajícího tuto smyčku čtou podle schématu 3.
Schéma 3
Optimalizace překladu smyčky by měla zajistit, aby ukládání prvků vektoru y probíhalo bez kolize s čtením prvků vektoru x (aby se povel neukládal do bloku, z něhož se právě čte).
Jestliže cyklus bloku je 4 takty (tj. přečtení jedné položky trvá 4 takty), bude čtení prvých 9 prvků vektoru x z paměti do registru procesoru probíhat podle časového diagramu uvedeného v tabulce 1: počínaje 4. taktem má v každém taktu procesor k dispozici jeden prvek vektoru. Teprve v 9. taktu je požadováno čtení z bloku, z něhož se již četlo. V té době je však předchozí čtení z tohoto bloku již ukončeno.
Takt |
Instrukce |
V registru |
---|---|---|
1 |
čti x1 |
- |
2 |
čti x2 |
- |
3 |
čti x3 |
- |
4 |
čti x4 |
x1 |
5 |
čti x5 |
x2 |
6 |
čti x6 |
x3 |
7 |
čti x7 |
x4 |
8 |
čti x8 |
x5 |
9 |
čti x9 |
x6 |
10 |
x7 |
|
11 |
x8 |
|
12 |
x9 |
Tabulka 1
© J. Nadrchal (28.4.2000) | ![]() ![]() |