Parallellisatie en optimalisatie voor de Cell BE architectuur

advertisement
Faculteit Ingenieurswetenschappen
Vakgroep Elektronica en informatiesystemen
Voorzitter: prof. dr. ir. J. Van Campenhout
Parallellisatie en optimalisatie voor de
Cell BE architectuur
door Michiel Questier
Promotor: prof. dr. ir. K. De Bosschere
Thesisbegeleider: ir. S. Rul en dr. ir. H. Vandierendonck
Afstudeerwerk ingediend tot het behalen van de graad van burgerlijk
ingenieur in de computerwetenschappen
Academiejaar 2006–2007
Toelating tot bruikleen
De auteur geeft de toelating dit afstudeerwerk voor consultatie beschikbaar te stellen
en delen van het afstudeerwerk te copiëren voor persoonlijk gebruik. Elk ander gebruik
valt onder de beperkingen van het auteursrecht, in het bijzonder met betrekking tot de
verplichting de bron uitdrukkelijk te vermelden bij het aanhalen van resultaten uit dit
afstudeerwerk.
Michiel Questier
4 juni 2007
i
Dankwoord
In de eerste plaats wens ik mijn promotor, prof. dr. ir. K. De Bosschere, te bedanken om
mij de mogelijkheid te geven deze scriptie in alle vrijheid af te werken.
Vervolgens wens ik mijn begeleiders, ir. Sean Rul en dr. ir. Hans Vandierendonck, te
bedanken voor hun steun en goede ideëen. Ook ir. Peter Bertels verdient een woord van
dank voor zijn hulp bij de presentaties en zijn aanstekelijke enthousiasme.
Verder mag ik ook mijn medestudenten niet vergeten, die de laatste jaren aangenamer
en interessanter hebben gemaakt. Bedankt Pieter, Francis, Simon, Michiel, Jeroen, Tom
en alle anderen die ik hier vergeet.
Als laatste wil ik vooral mijn ouders bedanken voor hun onvoorwaardelijke steun gedurende al deze jaren.
ii
Parallellisatie en optimalisatie voor de Cell BE architectuur
door
Michiel Questier
Afstudeerwerk ingediend tot het behalen van de graad van burgerlijk ingenieur in de computerwetenschappen
Academiejaar 2006–2007
Universiteit Gent
Faculteit Toegepaste Wetenschappen
Promotor: prof. dr. ir. K. De Bosschere
Samenvatting
Traditionele superscalaire microarchitecturen met een enkele rekenkern bereiken stilaan
hun limiet wat prestatie en vermogenverbruik betreft. Daarom wordt steeds vaker voor een
CMP-architectuur (Chip multiprocessor ) gekozen, waarbij meerdere (eenvoudige) kernen
op één chip worden geı̈ntegreerd. De Cell Broadband Engine, hierna Cell , is een heterogene
multiprocessor met niet minder dan negen kernen.
In eerste instantie wordt de architectuur van de Cell onderzocht. Deze blijkt complex
maar uiterst krachtig te zijn, waardoor bij het programmeren steeds de onderliggende
architectuur in het achterhoofd moet worden gehouden. Om bestaande programma’s
aan te passen wordt een programmeermodel voorgesteld, dat stapsgewijs het potentieel
van de Cell aanwendt. Dit model wordt toegepast om een eenvoudig algoritme voor
matrixvermenigvuldiging aan te passen. Hieruit blijkt dat met relatief weinig inspanning
de uitvoeringstijd met een grootteorde daalt.
Vervolgens wordt de opgedane kennis gebruikt om een meer complex algoritme te optimaliseren voor de Cell . Het betreft Clustal W , een programma uit de bio-informatica
om meerdere DNA-sequenties te aligneren. Na een toelichting van de onderliggende concepten, profileren we het programma. Aan de hand van een groot aantal inputs wordt
nagegaan wat de invloed van de parameters zijn. Hieruit kan een beeld worden gevormd
over de tijdsverdeling van de verschillende stappen in het algoritme. Na parallellisatie en
optimalisatie van de meest tijdsrovende functies, bekomen we een aangepast programma
dat de mogelijkheden van de Cell zo optimaal mogelijk tracht uit te buiten.
iii
We besluiten dat de Cell een hoge complexiteit en een stijle leercurve heeft, maar potentieel een enorme prestatie met zich meebrengt. Afhankelijk van de input kan Clustal W
met ongeveer een factor 2 worden versneld ten opzichte van de traditionele implementatie.
Trefwoorden: computerarchitectuur, parallellisatie, bioinformatica
iv
Parallelization and optimization for the Cell BE
architecture
Michiel Questier
Supervisor(s): Sean Rul, Hans Vandierendonck, prof. Koen De Bosschere
B. Synergistic Processor Element
Abstract— This article gives a short overview of the Cell architecture.
The differences with more traditional processor architectures are highlighted. This shows an innovative but complex design, so that incremental
modifications are necessary in order the facilitate the optimization of an algorithm for the Cell. This process is illustrated by adapting Clustal W for
execution on the Cell.
The SPEs are the real workhorses of the Cell. Eight in total,
they implement a design optimized for data intensive computations. They consist of the following elements:
• The core is a 128-bit RISC vector processor with a totally new
instruction set. Because of the wide registers, SIMD instructions
are especially suited for the SPE.
• Each SPE has its own local store. This memory is only 256
KiB large, and contains all the necessary instructions and data.
Data can be transferred from and to the main memory or local
stores of other SPEs by issuing explicit Direct Memory Access
(DMA) instructions.
• All SPEs are connected with the EIB through a Memory Flow
Controller (MFC). The MFC works asynchronically with the execution core of the SPE, so that computations and data transfers
can be executed in parallel.
These elements enable high theoretical performance on the
SPEs, but because of the high complexity and responsibility of
the programmer, this is often difficult to achieve.
Keywords—Computer architecture, parallelization, bioinformatics
I. I NTRODUCTION
S
INCE frequency scaling of complex out-of-order architectures has diminished over the years, and power consumption
is becoming increasingly troublesome, more and more microprocessors are implementing multiple cores. Instead of relying
on one overly complex core to do all calculations, computations
are parallelized over multiple simple cores. By keeping the individuals cores simple, problems such as power consumption are
much less of an issue for multi-core processors. However, the
task of parallelizing the workload over multiple cores is often a
difficult task for the programmer.
The Cell BE architecture [2] builds upon the idea of multiprocessors to achieve high performance, but extends this by implementing more and simpler heterogeneous cores in comparison
with many of the other modern multiprocessors. This comes at
a cost however, since the complexity is high and the learning
curve for a programmer steep.
C. Element Interconnect Bus
The EIB sits at the heart of the Cell and connects the processor elements with each other and the main memory. Since the
SPEs rely on the EIB to provide new data by issuing DMA requests, a high bandwidth is necessary to avoid a bottleneck. The
EIB offers a total peak bandwidth of 204,8 GB/s between all elements and 25,6 GB/s to main memory. Because of this high
bandwidth, data can be transferred to and from the local store
of the SPEs at high speed, offsetting the limitation of it’s small
size.
II. T HE C ELL A RCHITECTURE
The Cell consist of two different types of processor elements:
one PowerPC Processor Element (PPE) and a total of eight identical Synergistic Processor Elements (SPE). These are connected
with each other and the main memory through the Element Interconnect Bus (EIB).
III. C LUSTAL W
A. Description
A. PowerPC Processor Element
Clustal W is a program for multiple sequence alignation. The
alignation of DNA-sequences is useful for determining the properties of new sequences by matching them to sequences with
known properties. The algorithm consists of three main phases:
1. Pairwise alignment (PW). Aligning all sequences at once
consumes too much time because the search space grows too
big. The first phase aligns all possible pairs of sequences and
stores the score of the alignment, which indicates how closely
the two sequences match, in a matrix.
2. Guide tree (GT). Based on the scores from the previous
phase, a guide tree is built. This facilitates the execution of the
next phase.
3. Progressive alignment (PA). The last phase uses the guide
tree to choose a new sequence to be aligned with a group of
The PPE is a 64-bit RISC processor based on the existing
PowerPC architecture. Its instruction set is fully compatible
with the POWER instruction set, enabling the PPE to execute
existing binaries for the PowerPC, in particular an operating system.
Since the design is very much alike with most processors, the
targeted functionality of the PPE is extremely broad. In theory it is able to adequately execute most programs. Because
the SPEs are designed for high computational speed, the PPE’s
main responsibility is to offer control for the execution on the
SPEs. The PPE most often serves as a master thread for the
SPEs, communicating with them to ensure synchronisation and
an optimal workload balance.
v
already aligned sequences. The end result is a global alignment
of all sequences.
B. Analysis
The execution times of the phases scales differently with
varying parameters. The two defining parameters are the number of sequences, N , and the average length of the sequences,
L. The complexity of the phases [3] indicates how the execution of the phases will scale. However, the global impact of the
phases on the total execution time can not be determined by the
time complexity alone. By executing Clustal W and timing the
execution time of the phases for a large number of different input sets, an empirical conclusion can be drawn on the most time
consuming phases. A problem arises however, because the test
input sets have very similar parameters. Only the length of the
sequences varies, while the number of sequences remains almost
the same. To overcome this limitation, random input sets are
generated with requested parameters such as length and number. The randomness of the sequences has no perceived impact
on the execution times. The result of a select number of input
sets is illustrated in Figure 1.
The small local stores soon prove to be a limiting factor, and
the execution fails when the input sets grow to a significant size.
This can be avoided by only keeping the relevant data for the
current computation in the local store. As soon as data becomes
obsolete, even if for a short amount of time, it should be replaced with necessary or soon to be necessary data. Normally,
this would severely limit the performance. Since DMA transfers
can be executed in parallel with computations and the EIB offers
very high bandwidth, this is not so much of an issue on the Cell.
By limiting memory consumption throughout the algorithm, a
very wide set of inputs can be executed.
C.2 Progressive alignment
Parallelizing the progressive alignment proves to be more difficult. The only parallelism found is two independent loops in
the frequently called pdiff function. Theoretically, this would
lead to a speedup of 2 for the progressive alignment. Creating
a new thread each time pdiff is called creates too much overhead, which can be solved by statically creating two threads at
the beginning of the last phase. A call to pdiff can then be
replaced by writing a signal message in the mailboxes of the
SPEs.
Even though the reduction of the thread creation overhead significantly speeds up the progressive alignment, the parallelized
version is still slower than the original one. This is due to the
large amounts of communication needed in order to correctly
synchronize the threads. Only a small fraction of the time the
two SPE threads are alive is spent by useful calculations. The
lack of parallelism and the need for significant communication
makes the phase of the progressive alignment poorly suited for
execution on the SPEs.
IV. R ESULTS
Fig. 1. Normalized execution time of different phases in function of length
(above) and number (below) of sequences.
This clearly shows that on average, the pairwise alignment
takes the longest amount of time, followed by the progressive
alignment. As noted, the guide tree phase does not scale with
increasingly large sequences, and therefore is only the most important phase when the number of sequences is very large in
comparison with the length of the sequences. In order to optimize the average case, pairwise alignment is optimized first.
Progressive alignment follows next.
C. Parallelization and Optimization
C.1 Pairwise alignment
Parallelizing the phase of the pairwise alignment is relatively
easy since the alignment of two sequences is not dependent on
other sequences. Since there are no dependencies for the calculation of the matrix elements, the workload can easily be divided among all SPEs. However, it is not that trivial to divide the
workload into equal parts. This can be solved by letting the PPE
dynamically balance the workload, guaranteeing that all SPEs
receive approximately the same amount of work.
vi
Even though only the first phase can be sped up, a global
speedup of 1,6 to 2,2 for input set A and B from the BioPerf
suite [1].
V. C ONCLUSIONS
The Cell offers high performance, but the complexity is likewise very high. Incremental optimization is the key to achieve
high performance while limiting the difficulties for the programmer.
R EFERENCES
[1] Bader, D.A. and Sachdeva, V., An Open Benchmark Suite for Evaluating
Computer Architecture on Bioinformatics and Life Science Applications,
[2] IBM, Cell Broadband Engine programming handbook version 1.0,
[3] Chaichoompu, K. and Kittitornkun, S. and Tongsima, S., MT-ClustalW:
multithreading multiple sequence alignment, 20th International Parallel and
Distributed Processing Symposium
Inhoudsopgave
1 Inleiding
1
1.1
Probleemstelling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
1.2
Doelstelling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
1.3
Onderverdeling van de scriptie . . . . . . . . . . . . . . . . . . . . . . . . .
4
2 De Cell BE architectuur
2.1
2.2
2.3
2.4
2.5
6
Achtergrond . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
2.1.1
Inleiding en motivatie . . . . . . . . . . . . . . . . . . . . . . . . . .
6
2.1.2
De drie prestatielimiterende muren . . . . . . . . . . . . . . . . . . .
7
Overzicht van de Cell Broadband Engine architectuur . . . . . . . . . . . .
9
2.2.1
De processorelementen . . . . . . . . . . . . . . . . . . . . . . . . . .
9
2.2.2
Element Interconnect Bus . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2.3
Memory Interface Controller . . . . . . . . . . . . . . . . . . . . . . 11
PowerPC Processor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3.1
PowerPC Processor Unit . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.2
PowerPC Processor Storage Subsystem . . . . . . . . . . . . . . . . 14
2.3.3
PowerPC instructies . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Synergistic Processor Element . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.4.1
Synergistic Processor Unit . . . . . . . . . . . . . . . . . . . . . . . . 16
2.4.2
Memory Flow Controller . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.4.3
SPU instructieset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Programmeeromgeving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3 Matrixvermenigvuldiging
3.1
3.2
24
Inleiding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.1.1
Keuze algoritme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.1.2
Gelijkaardig werk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Programmeermodel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
vii
3.3
3.4
3.5
3.6
Matrixvermenigvuldiging op de PPU . . . . . . . . . . . . . . . . . . . . . . 28
3.3.1
Aanpassingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.3.2
Resultaten
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Matrixvermenigvuldiging op een enkele SPU . . . . . . . . . . . . . . . . . . 29
3.4.1
Aanpassingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.4.2
DMA-transfers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.4.3
Resultaten
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Parallellisatie over meerdere SPU’s . . . . . . . . . . . . . . . . . . . . . . . 35
3.5.1
Keuze parallellisatieschema . . . . . . . . . . . . . . . . . . . . . . . 35
3.5.2
Resultaten en schaalbaarheid . . . . . . . . . . . . . . . . . . . . . . 35
Optimalisaties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.6.1
Dubbele buffering . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.6.2
Lusontvouwing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4 Clustal W
4.1
4.2
43
Algemene beschrijving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.1.1
Achtergrond . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.1.2
Beschrijving algoritme . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.1.3
Aanverwant werk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Analyse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.2.1
Theoretische analyse . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.2.2
Empirische analyse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.3
Initiële aanpassingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.4
Paarsgewijze alignatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.4.1
Uitvoering op de SPU . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.4.2
Parallellisatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.5
Progressieve alignatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.6
Mogelijke uitbreidingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5 Besluit
70
A Bijkomende resultaten
72
A.1 Gedetailleerde resultaten matrixvermenigvuldiging . . . . . . . . . . . . . . 72
A.2 Gedetailleerde resultaten empirische analyse Clustal W . . . . . . . . . . . . 76
viii
Lijst van figuren
2.1
Overzicht van de opbouw van de Cell
. . . . . . . . . . . . . . . . . . . . .
9
2.2
Fysieke implementatie van de Cell . . . . . . . . . . . . . . . . . . . . . . . 10
2.3
Overzicht van de opbouw van de PPE . . . . . . . . . . . . . . . . . . . . . 12
2.4
Gedetailleerd overzicht van de opbouw van de PPE
2.5
Overzicht van de opbouw van de SPE . . . . . . . . . . . . . . . . . . . . . 16
2.6
Gedetailleerd overzicht van de opbouw van de SPU
2.7
Overzicht van de opbouw van de MFC
2.8
Schematische voorstelling verloop DMA transfer . . . . . . . . . . . . . . . 22
3.1
Uitvoeringstijd matrixvermenigvuldiging op de PPU met en zonder luste-
. . . . . . . . . . . . . 13
. . . . . . . . . . . . . 17
. . . . . . . . . . . . . . . . . . . . 20
geling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.2
Vergelijking uitvoering matrixvermenigvuldiging op de PPU en de SPU. . . 34
3.3
Schaling matrixvermenigvuldiging in functie van aantal SPU’s en dimensie.
3.4
Conceptuele werking dubbele buffering. . . . . . . . . . . . . . . . . . . . . 37
3.5
Verhouding DMA-latentie en berekeningen tussen origineel en gebufferd
36
algoritme. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.6
Uitvoeringstijd voor origineel, gebufferd en ontvouwd algoritme. . . . . . . . 40
3.7
Verhouding berekeningen gebufferd en ontvouwd algoritme op origineel. . . 42
4.1
Uitvoeringstijd van Clustal W bij variërende lengte en aantal sequenties . . 52
4.2
Uitvoeringstijd deelfasen bij vaste lengte en aantal sequenties. . . . . . . . . 53
4.3
Procentueel aandeel van de verschillende deelfasen op de uitvoeringstijd. . . 55
4.4
Verhouding van de uitvoeringstijden bij enkeldradige uitvoering. . . . . . . 60
4.5
Keuze statisch en dynamisch verdeelde werklast. . . . . . . . . . . . . . . . 62
4.6
Genormaliseerde verhouding van de uitvoeringstijden bij meerdradige uitvoering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
ix
Lijst van tabellen
2.1
Overzicht van de belangrijkste PPU instructies. . . . . . . . . . . . . . . . . 15
2.2
Aandeel latentie DMA-fases bij kloksnelheid van 3.2 GHz. . . . . . . . . . . 22
2.3
Overzicht van de belangrijkste SPU instructies. . . . . . . . . . . . . . . . . 23
3.1
Opgemeten verhouding DMA-transfer en berekeningen bij matrixvermenigvuldiging op 1 SPU. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.1
Tijdscomplexiteit van het originele Clustal W-algoritme. . . . . . . . . . . . 48
A.1 Resultaten matrixvermenigvuldiging origineel algoritme. . . . . . . . . . . . 73
A.2 Resultaten matrixvermenigvuldiging gebufferd algoritme.
. . . . . . . . . . 74
A.3 Resultaten matrixvermenigvuldiging algoritme met lusontvouwing. . . . . . 75
A.4 Resultaten empirische analyse Clustal W. . . . . . . . . . . . . . . . . . . . 77
x
Hoofdstuk 1
Inleiding
1.1
Probleemstelling
De in 1965 opgestelde wet van Moore voorspelde dat het aantal transistoren op een
geı̈ntegreerd circuit om de 18 maanden zou verdubbelen [28]. Hoewel dit recent werd
afgezwakt naar een verdubbeling om de twee jaar, blijkt deze wetmatigheid over de laatste 40 jaar stand te houden. In het domein van de computerarchitectuur heeft dit als
gevolg dat de complexiteit van een microarchitectuur potentieel om de twee jaar verdubbelt. Om deze ontwerpsvrijheid om te zetten in een prestatieverhoging, werden technieken
ontwikkeld zoals superscalaire architecturen en out-of-order uitvoering. Kenmerkend aan
deze technieken is dat ze een enkele rekenkern uitbreiden met extra functionaliteit om zo
een prestatieverhoging uit de instructiestroom te extraheren. De traditionele techieken
hebben echter een aantal beperkingen:
• De meeste technieken hebben als doel om het aantal instructies per klokcyclus (IPC)
te verhogen. Een belangrijke observatie hierbij is dat opeenvolgende instructies niet
altijd afhankelijk van elkaar zijn, en dus niet in de aangeboden volgorde moeten
worden uitgevoerd om de correctheid van het programma te bewaren. Hoewel studies [18] uitwijzen dat dit Instruction Level Parallelism theoretisch een IPC van 60
tot 70 kan opbrengen, blijkt in de praktijk slechts 1.5 tot 2 consistent haalbaar te zijn.
De reden hiervoor is dat het detecteren van onafhankelijkheden tussen instructies
niet triviaal is.
• Hoewel de prestatie van nieuwe processors vrij goed de wet van Moore volgt, is de
groei bij de snelheid van het geheugen minder snel. Omdat de groei in snelheid exponentieel is, wordt de kloof steeds groter. Om deze Memory Gap [36] te overbruggen
zijn er over de jaren heen meerdere niveau’s van tussengeheugen ingevoerd. Omdat
1
snelle geheugens duur zijn en veel plaats op de chip innemen, is het idee om dicht
bij de processor kleine maar snelle geheugens te plaatsen die de processor voldoende
snel van data en instructies kunnen voorzien. Naarmate het niveau van het geheugen
zich verder van de processor bevindt, wordt dit groter maar trager. Het probleem
hiermee is dat er steeds meer niveau’s nodig zijn om de kloof te dichten, wat op
lange termijn en op het gebied van schaalbaarheid geen werkbare oplossing is.
• Door de miniaturisatie van de transistoren is het mogelijk om de kloksnelheid van
nieuwe ontwerpen op te voeren. Er lijkt echter een grens te zijn aan deze frequentieschaling. Een van de redenen is dat het steeds moeilijker wordt om transistoren
met een kleinere afmeting te produceren1 . Veel problematischer is echter dat het
vermogenverbruik kubisch afhankelijk is van de frequentie.
P
V
⇒P
1 2
V f Ca
2
∼ f
1 3
∼
f Ca
2
∼
Een verdubbeling in kloksnelheid voor eenzelfde ontwerp heeft dus een verachtvoudiging van het vermogenverbruik tot gevolg. Dit heeft grote consequenties op onder
andere het gebied van betrouwbaarheid en koeling.
• Door de jaren heen is er veel onderzoek gedaan naar technieken die de prestatie
van een enkele microprocessor kan verhogen. De meest veelbelovende zijn echter al
geı̈mplementeerd, en nieuwe technieken zijn meestal ofwel moeilijk realiseerbaar of
bieden weinig meerwaarde ten opzichte van hun hardwarekost. Er is dus sprake van
een verminderde meerwaarde van nieuwe technieken.
Vanwege de voorgaande problemen wordt er steeds meer gekozen voor Chip Multiprocessor architecturen, waar meerdere (eenvoudige) rekenkernen op één enkele chip worden
geı̈ntegreerd. Indien het uitgevoerde programma in twee of meer onafhankelijke delen kan
worden opgesplitst, kan elke kern afzonderlijk een deel van het programma uitvoeren. Op
deze manier is een aanzienlijke versnelling mogelijk ten opzichte van de uitvoer op een
enkele kern, zonder dat een individuele kern heel complex hoeft te zijn. Daardoor worden
problemen zoals schaling en vermogenverbruik beter aangepakt dan bij traditionele microarchitecturen met een enkele kern. Er zijn echter een aantal bedenkingen te maken bij
deze techniek:
1
Huidige processors zijn ontworpen volgens een 65 nm ontwerp, met een verwachte verkleining naar 45
nm op het einde van dit jaar
2
• Omdat de prestatieverhoging wordt bekomen door het opsplitsen van een programma in meerdere afzonderlijke delen, is het detecteren van dit parallellisme van groot
belang. De verantwoordelijkheid hiervan ligt nog bij de programmeur, die dus extra
inspanningen moet leveren om maximale prestatie te halen uit een chip met meerdere
kernen. Hij moet ook de communicatie tussen de verschillende draden op zich nemen, en andere synchronisatieproblemen oplossen. Uit de praktijk blijkt het concept
van parallelle uitvoering niet eenvoudig om als progammeur mee te redeneren. Er
wordt dan ook veel onderzoek gedaan naar automatische detectie van parallellisme
op programma- of functieniveau.
• Vaak blijken algoritmes slechts voor een bepaald deel parallelliseerbaar te zijn. Indien een deel van het programma niet over meerdere kernen valt te verdelen, is er
een bovengrens op de maximaal verkrijgbare versnelling, uitgedrukt in de wet van
Amdahl:
versnelling =
1
f:
(1 − f ) + Sf
versnelbare f ractie programma
S:
versnelling van f ractie programma
Indien bijvoorbeeld slechts de helft van een programma kan versneld worden (f =
0.5) met een versnelling van een factor 2 (S = 2), dan is de totale versnelling van het
programma slechts 4/3. De bovengrens op de versnelling wordt in dit geval bereikt
wanneer S = ∞ en bedraagt slechts 2.
• Het komt vaak voor dat meerdere uitvoeringsdraden op een gemeenschappelijke variabele werken. Om te voorkomen dat het aanpassen van een variabele teniet wordt
gedaan door een andere draad of dat een draad verder rekent met een oude waarde,
moet het geheugen consistent worden gehouden voor alle draden. Er bestaan verschillende coherentieprotocollen om dit probleem op te lossen, die rekening houden
met gedeeld geheugen (waarbij elke draad hetzelfde beeld heeft van het geheugen)
of lokaal geheugen (waarbij elke draad slechts inwerkt op zijn eigen deel van het
geheugen).
Ondanks deze bijkomende moeilijkheden lijken CMP-ontwerpen toch de toekomst te
worden. Dualcore processors, met twee kernen, lijken standaard te worden, en steeds vaker
duiken modellen met vier kernen op. De Sun Niagara [3] heeft zelfs acht kernen, en de
Intel Teraflops Research Chip [16] maar liefst 80. De Cell is een CMP-architectuur die het
3
idee van dualcores nog extremer doortrekt. In totaal zijn er niet minder dan negen rekenkernen aanwezig. Om de grootte van de chip te beperken, zijn acht van deze kernen sterk
vereenvoudigd in vergelijking met superscalaire out-of-order microarchitecturen. Daarnaast is het geheugenmodel radicaal anders: de acht eenvoudige kernen hebben slechts
een beperkt lokaal geheugen. Indien data tussen deze lokale geheugens of het hoofdgeheugen moet worden uitgewisseld, zijn er expliciete Direct Memory Acces (DMA) instructies
nodig. Hoewel de Cell een ongelooflijke theoretische maximumprestatie van 200 GFLOPS
heeft, is de complexiteit en de verantwoordelijkheid voor de programmeur zeer hoog.
1.2
Doelstelling
Het doel van deze scriptie is te onderzoeken wat de pijnpunten zijn van het programmeren
voor de Cell . Daarnaast wordt nagegaan op welke manier efficiënt deze problemen kunnen
worden voorkomen, zowel voor nieuwe programma’s als voor het aanpassen van bestaande
programma’s. Naast een aantal welbekende technieken voor parallellisatie blijken ook een
aantal specifieke optimalisaties noodzakelijk te zijn om maximale prestatie op de Cell te
bekomen.
1.3
Onderverdeling van de scriptie
In hoofdstuk 2 wordt de architectuur van de Cell meer in detail bekeken. Hoewel er
hierover erg veel valt te schrijven, wordt enkel toegespitst op de punten waar de Cell
verschilt van de meeste andere architecturen en die belangrijk zijn voor de rest van dit
werk. De onderliggende redenen van de ontwerpsbeslissingen worden hierbij uiteen gezet.
Hoofdstuk 3 licht de verschillen tussen de Cell en andere microarchitecturen verder toe aan
de hand van een eenvoudig voorbeeld. Er wordt gekozen voor matrixvermenigvuldiging,
een triviaal en welbekend algoritme. De nadruk ligt dan ook niet zozeer op het algoritme
zelf, maar op de nodige aanpassingen die moeten worden gedaan om maximale prestatie te
bekomen. Hieruit blijkt dat de complexiteit in eerste instantie meevalt, maar dat er enkele
extra stappen moeten worden ondernomen om de moeilijkheden van de Cell te omzeilen.
Desondanks blijkt de bekomen prestatieverhoging groot te zijn, waardoor het beloofde
potentieel van de Cell gerechtvaardigd blijkt te zijn. Vervolgens wordt de opgedane kennis
toegepast op een interessanter probleem. In hoofdstuk 4 wordt Clustal W aangepast, een
programma gebruikt in de bioinformatica om meerdere DNA-sequenties te aligneren. Na
een uiteenzetting van de belangrijkste concepten van het algoritme, wordt het programma
aangepast voor uitvoering op de Cell . Na een grondige analyse aan de hand van een groot
4
aantal inputs wordt een beeld bekomen van de belangrijkste parameters die invloed hebben
op de uitvoeringstijd van het programma. Dit laat ons vervolgens toe om onze aandacht
toe te spitsen op de functies die het grootste aandeel in de uitvoeringstijd hebben. Deze
delen van het programma worden geparallelliseerd en geoptimaliseerd, rekening houdend
met de onderliggende architectuur en de opgedane kennis uit de vorige hoofdstukken.
Hoewel het aanpassen van een bestaand en vrij complex algoritme een grote inspanning
vergt, blijkt ook hier de bekomen versnelling, afhankelijk van de input, aanzienlijk te zijn.
Hoofdstuk 5 vat de belangrijkste besluiten samen.
5
Hoofdstuk 2
De Cell BE architectuur
2.1
2.1.1
Achtergrond
Inleiding en motivatie
De Cell Broadband Engine, hierna Cell , is de eerste telg van een nieuwe familie microprocessors, de Cell Broadband Processor Architecture (CBEA). De CBEA is een uitbreiding
op de bestaande 64-bits PowerPC-architectuur, en is ontwikkeld door Sony, Toshiba en
IBM.
Oorspronkelijk was de Cell bedoeld voor gebruik in spelconsoles zoals de Playstation
3, maar de architectuur en implementatie zijn ontworpen om een fundamentele stap voorwaarts te zetten in de prestatie van processors. Daardoor is een veel breder gamma aan
toepassingen mogelijk.
De Cell is een heterogene multiprocessor met negen processors. In dat opzicht wordt
de recente trend in processors gevolgd. Vernieuwend is echter dat de Cell twee soorten
gespecialiseerde processors bevat, namelijk een enkel PowerPC Processor Element (PPE)
en acht Synergistic Processor Elements (SPE).
Het eerste type, de PPE, is gebaseerd op de bestaande 64-bits PowerPC-architectuur,
en is er volledig compatibel mee. Daardoor kan de PPE bestaande 32-bit en 64-bit besturingssystemen en applicaties draaien. Het tweede type, de SPE, is geoptimaliseerd voor
rekenintensieve taken. Het idee, en de verklaring voor het synergistische in de naam van
de SPE’s, is dat de SPE’s en de PPE van elkaar afhankelijk zijn om alle functionaliteit
op een optimale manier te implementeren. De SPE’s hangen af van de PPE om het besturingssysteem te draaien en de controle-intensieve algoritmes uit te voeren, terwijl de
PPE hoofdzakelijk op de SPE’s vertrouwt om de overgrote meerderheid van de prestatie
te verzorgen. De SPE’s kunnen in een standaard hoogniveautaal zoals C en C++ worden
6
geprogrammeerd. Naast een uitgebreide instructieset bieden ze ook ondersteuning voor
SIMD-instructies. De PPE ondersteunt de bestaande PowerPC Architecture Vector/SIMD
Multimedia Extension.
Het grootste verschil tussen de SPE’s en de PPE is echter de manier waarop het geheugen wordt benaderd. De PPE werkt op de traditionele manier in op het hoofdgeheugen,
waarbij loads en stores data transfereren tussen private registers en het hoofdgeheugen,
eventueel met caches als tussenliggende laag. De SPE’s hebben echter enkel toegang tot
het hoofdgeheugen via Direct Memory Access (DMA) commando’s van en naar een beperkt lokaal geheugen waarin zowel data als instructies worden opgeslagen. De loads en
stores uit de SPE-instructieset werken enkel in op dit lokaal geheugen in plaats van op
het hoofdgeheugen. Vanwege de asynchrone werking tussen de rekeneenheid van de SPE
en de DMA-controller, wordt er parallellisme bekomen op het gebied van berekeningen en
opvragen van data. De achterliggende redenering is dat de latentie van het hoofdgeheugen
stijgt in verhouding met de processorsnelheid. Uitgedrukt in klokcycli is er sprake van een
factor honderd en meer over de laatste 20 jaar [20]. Dit heeft als gevolg dat de theoretische
piekprestatie van een processor beperkt wordt door de latentie van het geheugen. Indien
een load bij een traditionele processor een cachemisser op alle niveaus tot gevolg heeft,
wordt de processor voor enkele honderden cycli geblokkeerd. Hiertegenover staat dat het
aantal nodige cycli om een DMA-transfer op te zetten vanuit de SPE’s relatief klein is
[26]. Een bijkomend probleem bij conventionele architecturen is dat zelfs met kostelijke
speculatie vaak slechts enkele onafhankelijke geheugentoegangen tegelijk kunnen worden
aangevraagd. Bij een model met expliciete DMA-instructies kan elke SPE een groot aantal gelijktijdige toegangen hebben uitstaan, zonder dat er nood is aan speculatie. Hiertoe
moeten geheugentoegangen echter voldoende op voorhand worden aangevraagd, zodat de
DMA-controller deze kan ordenen en op tijd kan afhandelen. Indien dit efficiënt gebeurt,
is de data beschikbaar op het moment dat de SPE er berekeningen wil op uitvoeren.
2.1.2
De drie prestatielimiterende muren
De Cell probeert de drie factoren die prestatie het meest beperken te omzeilen: vermogen,
geheugen en processorfrequentie.
De vermogenmuur
Recent wordt er steeds meer aandacht besteed aan de dissipatie van vermogen in een
geı̈ntegreerd circuit. De limiterende factor in het ontwerp is dan ook steeds meer dit
vermogenverbruik onder controle krijgen, en niet de beschikbare werkmiddelen zoals tran-
7
sistoren. Indien de prestatie van een processor moet toenemen, is het dus nodig om
vermogenefficiëntie aan een zelfde tempo te laten meeschalen met prestatieverhogingen.
De manier waarop dit in de Cell wordt aangepakt is door een onderscheid te maken
tussen processors geoptimaliseerd voor het uitvoeren van besturingssystemen en programma’s met een moeilijk controleverloop enerzijds, en processors geoptimaliseerd voor rekenintensieve taken anderzijds. Deze specialisatie vinden we terug in de PPE en de SPE
respectievelijk. Beide soorten processors kunnen worden geoptimaliseerd voor hun specifieke toepassing, wat de verhouding tussen prestatie en vermogenverbruik in zo’n gevallen
sterk vergroot. Doordat de processors relatief simpel kunnen worden ontworpen, kan een
hoge klokfrequentie worden bekomen aan een laag werkingsvoltage.
De geheugenmuur
Ondanks innovatieve aanpassingen, zoals het integreren van de geheugencontroller, is de
latentie tussen het hoofdgeheugen en processors de grens van 1000 klokcycli aan het naderen [19]. De prestatie van een programma wordt dan ook gedomineerd door de manier
waarop datatransfer wordt aangepakt. Optimaliseren van programma’s wordt dan ook
steeds vaker een oefening in het optimaal benutten van de caches.
De Cell lost dit op twee manieren op.
• Drie lagen in de geheugenhiërarchie: hoofdgeheugen, lokaal geheugen voor elke SPE,
en een groot aantal registers voor elke SPE.
• Asynchrone DMA-aanvragen tussen het hoofdgeheugen en het lokaal geheugen.
Door deze aanpassingen kan de programmeur een groot aantal zinnige datatransfers
aanvragen, terwijl er in parallel wordt gerekend met de data die zich reeds in het lokaal
geheugen bevindt.
De frequentiemuur
Om de frequentie van nieuwe processors op te voeren, zijn steeds diepere pijplijnen nodig1 . Moderne processors zijn echter op een punt gekomen waar de meerwaarde van extra
pijplijntrappen sterk vermindert, zeker wanneer vermogenverbruik in rekening wordt genomen.
Door het gespecialiseerd ontwerp van de PPE en de SPE zijn diepe pijplijnen niet
nodig. De SPE bekomt zijn efficiëntie uit een groot registerbestand, waardoor een groot
1
De Intel Pentium IV Prescott-core met 31 pijplijntrappen is daar een extreem voorbeeld van.
8
Figuur 2.1: Overzicht van de opbouw van de Cell
aantal lusoptimalisaties mogelijk is. De latentie van het lokaal geheugen is bovendien deterministisch en bedraagt 6 klokcycli. Hierdoor is de compiler in staat om een zo optimaal
mogelijk statische scheduling van de instructies te bepalen, zodat out-of-order technieken
niet nodig zijn.
2.2
2.2.1
Overzicht van de Cell Broadband Engine architectuur
De processorelementen
Figuur 2.1 geeft een schematisch overzicht van de opbouw van de Cell . Een afbeelding van
de fysieke realisatie is weergegeven in Figuur 2.2. Deze bestaat uit een enkele PPE en acht
SPE’s, die met elkaar, het hoofdgeheugen en randapparaten zijn verbonden door middel
van de Element Interconnect Bus (EIB). Het hoofdgeheugen bestaat uit Extreme Data
Rate (XDR) RAM die wordt benaderd vanuit de Memory Interface Controller (MIC).
Communicatie met andere Cell -processors is mogelijk via de Cell Broadband Engine Interface (BEI).
De PPE bevat een 64-bits PowerPC RISC2 rekenkern die twee draden tegelijk kan
uitvoeren. Er zijn standaard caches aanwezig, namelijk 32KiB L1 data- en instructiecaches
en 512 KiB L2 geünificeerde cache (zowel voor data als instructies). De instructieset is
een uitbreiding van de PowerPC instructieset, en de PPE is in staat bestaande code voor
de PowerPC architectuur uit te voeren. Het vooropgestelde doel van de PPE is om het
besturingssysteem te draaien, de controle op hoog niveau te verzorgen en de verschillende
SPE-draden te beheren.
2
RISC : Reduced Instruction Set Computer.
9
Figuur 2.2: Fysieke implementatie van de Cell
De acht SPE’s zijn onderling identieke vectorprocessors. Door het gebruik van Single
Instruction, Multiple Data (SIMD) instructies zijn ze uitermate geschikt voor operaties
die dataintensief zijn. De rekenkern van de SPE is een RISC kern, met 256 KiB lokaal
geheugen en 128 registers van 128 bits breedte. Naast een SIMD instructieset zijn er
gespecialiseerde instructies voor DMA-transfers en communicatie met de andere processorelementen. De DMA-instructies worden vanuit de rekenkern van de SPE aangeleverd
aan een Memory Flow Controller (MFC) die aanwezig is in elke SPE. Deze communicatie
gebeurt via unidirectionele kanalen. De status van deze kanalen kan door andere SPE’s of
de PPE worden benaderd door Memory Mapped Input Output(MMIO) registers.
2.2.2
Element Interconnect Bus
De Element Interconnect Bus is een communicatienetwerk die de verschillende componenten van de Cell met elkaar verbindt. Over dit netwerk wordt data en instructies
uitgewisseld tussen de processorelementen onderling en met het hoofdgeheugen.
De EIB is opgebouwd uit vier dataringen van 16 bytes breed, die werken op de helft
van de kloksnelheid van de processors3 . Twee lopen in wijzerzin, de andere twee in tegenwijzerzin. Op elke ring kunnen er tot drie gelijktijdige datatransfers worden toegelaten,
zolang hun pad op de ring niet overlapt. Om toegang tot de bus te krijgen, wordt vanuit
de elementen verbonden met de bus een aanvraag gedaan. Een arbiter die deel uitmaakt
van de EIB zal de aanvragen toewijzen aan een ring die de afstand tussen bron en bestem3
Standaard komt dit neer op 1.6 GHz.
10
ming minimaliseert. Er wordt verder op toegezien dat er geen aanvragen worden gestart
die interfereren met transfers die reeds in uitvoering zijn. Belangrijk hierbij is om op te
merken dat de volgorde waarin aanvragen worden gedaan niet noodzakelijk de volgorde is
waarin ze worden uitgevoerd. Er kan wel een relatieve volgorde worden vastgelegd aan de
hand van fences en barriers.
Aangezien de vernieuwende geheugenstructuur aan de basis staat van de prestatieverhoging van de Cell , kan dan ook worden gesteld dat de EIB het hart van de Cell is. De
theoretische piekbandbreedte is 204.8 GB/s4 , waaruit blijkt dat de prestatielimiterende
factor van het geheugen op een doeltreffende manier is aangepakt in de Cell .
Om multiprocessorsystemen met meerdere Cell processors te ondersteunen, is er op
elke chip een I/O interface waarmee twee processors op een coherente manier met elkaar kunnen worden verbonden door gebruik te maken van het broadband interface (BIF)
protocol.
2.2.3
Memory Interface Controller
De geı̈ntegreerde geheugencontroller biedt een interface tussen de EIB en het fysieke hoofdgeheugen. Het biedt toegang tot een maximum van twee Rambus Extreme Data Rate
(XDR) geheugeninterfaces, voor een maximum van 64 GiB XDR RAM. In totaal kunnen
er 64 lees- en 64 schrijftoegangen in een wachtrij worden geplaatst. Er bestaan verder
verschillende werkingsmodi, die onder andere voorrang kunnen geven aan de SPE’s of een
tragere werking forceren om vermogen te besparen.
2.3
PowerPC Processor Element
Het PowerPC Processor Element (PPE) is een 64-bits RISC-processor die voldoet aan de
specificaties van de PowerPC architectuur. Hij biedt ondersteuning voor het uitvoeren van
twee draden in parallel door middel van Simultaneous multithreading (SMT). Daarnaast
is de standaard PowerPC instructieset uitgebreid met SIMD multimedia extensies.
De PPE heeft als hoofdtaak de algemene controle van de volledige Cell te verzorgen.
Het besturingssysteem wordt uitgevoerd door de PPE, zodat oproepen naar het besturingssysteem afkomstig van de SPE’s zullen gebieren via de PPE om. De PPE bestaat
uit twee grote onderdelen, de PowerPC Processor Unit (PPU) en het PowerPC Processor
Storage Subsystem (PPSS), zoals te zien op Figuur 2.3.
4
De maximale bandbreedte is beperkt door de snooping van adressen, wat slechts 1 adres per cyclus
bedraagt. Elk gesnooped adres kan potentieel 128 bytes transfereren, dus is de theoretische piekbandreedte
128bytes × 1.6GHz = 204.8GB/s
11
Figuur 2.3: Overzicht van de opbouw van de PPE
De instructies worden uit een L1 instructiecache gehaald, en een totaal van zes functionele eenheden werkt in op data uit de L1 datacache. De PPSS behandelt geheugenaanvragen van de PPU en externe aanvragen naar de PPE vanuit de SPE’s. Het bevat ook
een geünificeerde L2 data- en instructiecache. Een gedetailleerd overzicht van de PPU en
PPSS wordt in Figuur 2.4 weergegeven, en hierna meer in detail toegelicht.
2.3.1
PowerPC Processor Unit
De PPU bestaat uit de volgende functionele eenheden:
• Instruction Unit (IU): De IU bevat de front-end van de pijplijn (instruction-fetch,
decode, dispatch, issue en branch) en de completion fase. Het bevat daarom de L1
instructiecache, een 32 KiB grote 2-wegs set-associatieve cache met cachelijnen van
128 bytes en foutbescherming door pariteitsbits.
• Load and Store Unit (LSU): De load and store unit voert de datatoegangen vanuit
de processor uit. Hiertoe behoren voornamelijk de load en store instructies. De LSU
bevat de L1 data cache die dezelfde eigenschappen heeft als de L1 instructiecache,
met dat verschil dat ze 4-wegs set-associatief en write-through is.
• Vector/Scalar Unit (VSU): De VSU is nog eens in twee onderverdeeld. De FloatingPoint Unit (FPU) en de 128-bit brede Vector/SIMD Multimedia Extension Unit
(VXU) staan in voor het uitvoeren van floating-point operaties en vectorinstructies
respectievelijk.
12
Figuur 2.4: Gedetailleerd overzicht van de opbouw van de PPE
• Fixed-Point Unit (FXU): De FXU voert de instructies op fixed-point (gehele) getallen, zoals optellen, vermenigvuldigen, delen, vergelijken en logische instructies.
• Memory Management Unit (MMU): De adresvertaling voor alle geheugentoegangen
gebeurt in de MMU. Naast een Segment Lookaside Buffer (SLB) van 64 elementen
bevat ze een Translation Lookaside Buffer (TLB) met 1024 elementen. De TLB
ondersteunt drie verschillende paginagroottes tegelijkertijd: de standaard 4 KiB, en
twee vrije keuzes uit 64 KiB, 1 MiB en 16 MiB.
Om parallelle uitvoer van fixed-point en floating-point operaties te vergemakkelijken, genereert de SIMD instructieset van de PPE geen excepties en ondersteunt ze geen complexe
functies. Er worden verder slechts weinig resources of communicatiepaden gedeeld met de
andere uitvoeringseenheden van de PPE.
De adresvertaling in de MMU voldoet aan de eisen van de 64-bits PowerPC architectuur, en ondersteunt volgende groottes van de verschillende adresruimtes:
• Werkelijke adresruimte: 242 bytes. Dit zijn de adressen die zich in het werkelijke
geheugen bevinden. Dit kan zowel het lokaal geheugen van de SPE’s op de chip zijn
als RAM-geheugen en I/O-apparaten.
13
• Effectieve adresruimte: 264 bytes. Deze adressen worden gegenereerd door programma’s.
• Virtuele adresruimte: 265 bytes. Dit zijn de adressen die gebruikt worden tussen de
MMU van de PPE en de MFC van elke SPE onderling, om te vertalen tussen de
werkelijke en relatieve adresruimte.
2.3.2
PowerPC Processor Storage Subsystem
De PPSS is het subsysteem dat alle geheugentoegangen van de PPU afhandelt en geheugenconsistentie met de EIB bewaart door snooping operaties. Er is een geünificeerde
L2-cache met volgende eigenschappen: 512 KiB groot, 8-wegs set-associatief, write-back
en error-correction code (ECC). Ook hier is de cachelijn 128 bytes. Er is slechts een enkele
lees/schrijf-poort naar het hoofdgeheugen, maar er is ondersteuning tot acht data-prefetch
streams. De data van de L1 datacache is gedupliceerd, en er is ondersteuning voor coherente multiprocessorsystemen. Data wordt uitgewisseld tussen de PPU en de PPSS via
een 32-bytes brede leespoort en een 16-bytes brede schrijfpoort.
De interface tussen de PPSS en de EIB is zowel voor lezen als schrijven 16 bytes
breed. Er is slechts één datatoegang tegelijk, en alle gebeuren in programmavolgorde.
In dat opzicht verschilt de PPE met de SPE, waar datatoegangen niet noodzakelijk in
programmavolgorde op de EIB worden geplaatst.
Voor de volledigheid volgt een kort overzicht van de belangrijkste registers in de PPE.
Een uitgebreidere bespreking is te vinden in [23]. Bewerkingen gebeuren steeds op registers, en nooit rechtstreeks op het hoofdgeheugen. De belangrijkste PPE registers zijn:
• General-Purpose Registers: In totaal zijn er 32 registers van 64 bits breedte die
gebruikt worden voor fixed-point operaties.
• Floating-Point Registers: Ook hiervan zijn er 32 registers van 64 bits. Het interne
formaat van floating-point getallen is het IEEE 754 dubbele-precisie formaat [25].
Enkele-precisie resultaten worden intern in dubbele-precisie formaat opgeslagen.
• Vectorregisters: Er zijn in totaal 32 vectorregisters, maar deze zijn 128 bits breed
om efficiënte SIMD-instructies toe te laten.
Omdat de PPE hardwarematige ondersteuning biedt voor de gelijktijdige uitvoering
van twee uitvoeringsdraden, moeten alle architecturale registers en registers die een speciale functie hebben worden gedupliceerd. De meeste niet-architecturele werkmiddelen zoals
de caches worden gedeeld.
14
Tabel 2.1: Overzicht van de belangrijkste PPU instructies.
Omschrijving
Aantal instructies
Latentie(cycli)
Uitvoeringseenheid
Arithmetisch
59
1 of 2
FXU
Spronginstructie
12
1 of 2
BRU
Vergelijking
5
1
FXU
Data-cache controle
5
N/A5
LSU
Deling
8
10 tot 70
FXU
Logisch
12
2
FXU
Vermenigvuldiging
9
6 tot 15
FXU
Load
18
2
LSU
Store
17
N/A
LSU,FXU
FP vergelijking
2
1
FXU
FP deling
2
74
FPU
FP arithmetisch
20
10 of 11
FPU
FP vermenigvuldiging
16
10 of 11
FPU
FP afronden of converteren
12
10 of 11
FPU
FP worteltrekking
2
84
FPU
2.3.3
PowerPC instructies
Tabel 2.1 geeft een overzicht van de belangrijkste instructies in de PowerPC instructieset
van de PPU. Voor een gedetailleerde uitleg wordt verwezen naar Appendix A in [19].
2.4
Synergistic Processor Element
De acht Synergistic Processor Elements zijn 128-bit RISC-processors die geoptimaliseerd
zijn om rekenintensieve programma’s met een grote databehoefte uit te voeren. Omdat de
SPE’s een volledig nieuw ontwerp zijn, voeren ze een volledig nieuwe SIMD instructieset
uit. De SPE bestaat uit twee subsystemen, de Synergistic Processor Unit (SPU) en de
Memory Flow Controller (MFC).
5
N/A geeft aan dat de instructie geen registers aanpast of enkel kan verklaard worden indien een breder
beeld van de processor in acht wordt genomen.
15
Figuur 2.5: Overzicht van de opbouw van de SPE
2.4.1
Synergistic Processor Unit
De SPU is de rekenkern die deel uitmaakt van de SPE. Alle instructies die worden uitgevoerd op de SPU worden uit het lokaal geheugen (local store, LS) gehaald. Dit lokaal
geheugen is 256 KiB groot, en bevat zowel data als instructies. Alle loads en stores van
data, ongeacht het datatype, gebeuren tussen het lokaal geheugen en een enkel registerbestand. Het registerbestand bevat 128 registers van 128 bits breed. De reden voor dit grote
aantal is dat compileroptimalisaties zoals lusontvouwing veel efficiënter kunnen worden
uitgevoerd indien het aantal beschikbare registers groot is. Verder laat de breedte van 128
bits SIMD-instructies toe die op een grote dataset tegelijk kunnen worden uitgevoerd (vb.
4 integers van 32 bit of 2 floating-point getallen van dubbele precisie).
In plaats van rechtstreekse toegang tot het hoofdgeheugen, kan elke SPU maar werken
op data die zich in zijn eigen beperkt lokaal geheugen bevindt. Data wordt van en naar
het hoofdgeheugen geschreven door DMA-instructies, die door de DMA-controller in de
MFC worden afgehandeld. Er zijn communicatiekanalen aanwezig vanuit de SPU naar de
MFC, de PPE en andere apparaten zoals de andere SPE’s.
De belangrijkste componenten van de SPU zijn weergegeven in Figuur 2.6. Deze bevatten de Synergistic Execution Unit (SXU), het lokaal geheugen (LS), en het SPU registerbestand (SRF). De SXU bevat zes uitvoeringseenheden, die hierna in meer detail zullen
16
Figuur 2.6: Gedetailleerd overzicht van de opbouw van de SPU
worden besproken.
Uitvoeringseenheden
De SPU heeft twee functioneel verschillende pijplijnen, die de even en oneven pijplijn
worden genoemd (zie fig. 2.6). Een overzicht van de verdeling van de instructies over
de verschillende pijplijnen wordt gegeven in [22]. De SPU is in staat om per klokcyclus
een instructie te starten voor de even én de oneven pijplijn, en kan tegelijk voor elke
pijplijn de resultaten van een uitgevoerde instructie wegschrijven. Alle instructies worden
in programmavolgorde uitgevoerd. De verschillende uitvoeringseenheden zijn:
• SPU Odd Fixed-Point Unit (SFS): Deze eenheid voert shift-,roteer- en maskoperaties
toe op bits, bytes, halfwoorden en woorden6 , en shuffleoperaties op bytes.
• SPU Even Fixed-Point Unit (SFX): Voert aritmetische en logische instructies uit,
verschuivingen en rotaties op SIMD woorden, vergelijkingen van floating-point getallen, en benadering van de inverse en inverse wortel van floating-point getallen.
• SPU Floating-Point Unit (SFP): De SFP voert instructies op enkele en dubbele precisie floating-point getallen uit, net als integer vermenigvuldigingen en conversies.
6
In deze scriptie wordt een woord steeds als 32 bits lang beschouwd.
17
De SPU ondersteunt echter maar vermenigvuldigingen van 16-bit, zodat 32-bit vermenigvuldigingen in software worden geı̈mplementeerd door 16-bit operaties: drie
16-bit vermenigvuldigingen en twee optellingen om de tussenproducten samen te
tellen.
• SPU Load And Store Unit (SLS): De SLS voert de load en store instructies tussen
het lokaal geheugen en het registerbestand uit. Het handelt ook DMA-aanvragen
naar het lokaal geheugen af.
• SPU Control Unit (SCN): Deze uitvoeringseenheid is verantwoordelijk voor het ophalen van instructies en het toewijzen ervan aan de twee pijplijnen. Spronginstructies
worden hier uitgevoerd, met de belangrijke opmerking dat er geen sprongvoorspeller
aanwezig is in de SPU. Het is wel mogelijk om branch hints aan de SPU te leveren.
Indien de sprong niet correct kan worden voorspeld door de branch hints of er geen
branch hints aanwezig zijn, bedraagt de branch penalty 18 tot 19 cyvli
• SPU Channel and DMA Unit (SSC): Verzorgt de communicatie, datatransfer en
controle van en naar de SPU. De SSC is nauw verbonden in werking met de DMAcontroller, die is beschreven in 2.4.2.
Lokaal geheugen
Het lokaal geheugen is 256 KiB groot en bevat zowel de data als de instructies voor de
SPU. Er is foutbescherming in de vorm van ECC aanwezig. Gegevens worden nergens
gecached, zodat de programmeur volledige controle heeft over wat er zich op elk moment
in het lokaal geheugen bevindt. De latentie van het geheugen is steeds 6 cycli. Omdat
het lokaal geheugen wordt gedeeld voor loads en stores, DMA lees- en schrijfoperaties en
het ophalen van instructies, worden volgende prioriteiten toegekend voor toegang tot het
lokaal geheugen:
1. DMA lees- en schrijfopdrachten krijgen voorrang. DMA-instructies kunnen maar
om de acht cycli toegang proberen te krijgen tot het lokaal geheugen (ondertussen
worden ze gebufferd), zodat de impact op gewone loads en stores beperkt blijft.
2. SPU loads en stores.
3. Instructies worden geprefetched door tenminste 17 instructies sequentieel op te halen
vanaf het sprongdoel.
18
Floating-point ondersteuning
De SPU ondersteunt bewerkingen op zowel enkele als dubbele precisie floating-point getallen. Bewerkingen op enkele-precisie floating-point getallen kunnen 4-wegs geparallelliseerd worden door SIMD-instructies, en kunnen volledig gepijplijnd uitgevoerd worden.
Operaties op floating-point getallen met dubbele precisie zijn slechts gedeeltelijk parallelliseerbaar (gedurende de laatste 7 van in totaal 13 klokcycli), en laten geen instructies in
de andere pijplijn toe.
Het dataformaat ondersteund door de SPU is de IEEE 754 standaard, met de volgende
uitzonderingen voor enkele precisie:
• Er kan enkel naar beneden worden afgerond.
• Operandi die niet genormaliseerd zijn worden als nul beschouwd, en resultaten die
niet genormaliseerd zijn worden op nul geforceerd
• Getallen met een exponent die enkel 1’en bevat worden beschouwd als genormaliseerde getallen en niet als oneindig of not-a-number (NaN).
2.4.2
Memory Flow Controller
Op elke SPU is een aparte Memory Flow Controller aanwezig (MFC) die de verbinding
vormt tussen de SPU en de EIB, het hoofdgeheugen en andere processorelementen. De
hoofdtaak bestaat erin om data tussen het lokaal geheugen en het hoofdgeheugen uit te
wisselen. Voor communicatiedoeleinden zijn er aparte structuren zoals mailboxen. Figuur
2.7 geeft een gedetailleerd overzicht van de MFC.
De verschillende verbindingen zijn:
1. LS Read en Write Interface: Dit verbindt het lokaal geheugen met de EIB, en wordt
gebruikt voor alle DMA transfers.
2. LS DMA List Element Read Interface : De DMA aanvragen worden in een wachtlijst
geplaatst om ze op een efficiënte manier te ordenen. Een speciaal geval van een DMA
instructie is de DMA-lijst, waarbij een transfer wordt gestart naar een aangesloten
blok geheugen in het lokaal geheugen, maar waarvan de data niet in een continu stuk
van het hoofdgeheugen hoeft te liggen. De maximale grootte van een DMA-lijst is
16 KiB, met een maximum van 2048 elementen. De LS DMA List Element Read
Interface vormt de verbinding tussen het lokaal geheugen en de wachtlijst met DMA
commando’s om DMA-lijsten doeltreffend te kunnen implementeren.
19
Figuur 2.7: Overzicht van de opbouw van de MFC
3. Channel Interface : Over dit kanaal worden DMA commando’s tussen de SPU en
de MFC uitgewisseld.
4. EIB Command and Data Interfaces : Dit is de verbinding tussen de MFC en de
EIB. De MFC kan tot 16 uitstaande DMA-aanvragen tegelijk hebben.
5. SPU Load and Store : Hoewel deze verbinding geen deel uitmaakt van de MFC,
wordt ze hier voor de volledigheid vermeld. Via deze interface worden load- en
store-instructies tussen de SPU en het lokaal geheugen afgewerkt.
Mailboxen
Elke SPE heeft twee mailboxen, één om boodschappen naar de PPE te sturen en één
om boodschappen van de PPE te ontvangen. Deze mailboxen worden voornamelijk gebruikt voor de controle van de applicatie vanuit de PPE. Door de SPE in de mailboxen
signaalboodschappen te laten schrijven, kan de PPE de werklast over de SPE’s verdelen
en beheren. De uitgaande mailbox van de SPE kan ook gebruikt worden om aan te geven
wanneer de SPE zijn taak heeft uitgevoerd.
Direct Memory Acces Controller
De DMAC implementeert de DMA transfers van data en instructies tussen het lokaal
geheugen en het hoofdgeheugen. De DMAC voert autonoom alle instructies uit, waardoor
20
de SPU in parallel instructies kan uitvoeren terwijl een DMA-transfer wordt afgehandeld.
Het verloop van een DMA-transfer wordt geı̈llustreerd in Figuur 2.8 en ziet er als volgt
uit:
1. De SPU gebruikt de channel interface om het DMA commando in de wachtlijn van
de MFC te plaatsen.
2. De DMAC selecteert uit de lijst een commando om uit te voeren. Vereenvoudigd
zijn de prioriteitsregels dat er afgewisseld wordt tussen data opvragen of plaatsen,
en dat het commando klaar moet zijn om uit te voeren (de adresresolutie is compleet
en er zijn geen afhankelijkheden met andere instructies).
3. Indien het commando een DMA-lijst bevat moet de lijst van elementen worden
opgevraagd. De DMAC plaatst hiervoor een aanvraag in een wachtlijn naar het
lokaal geheugen. Ondertussen wordt het DMA-commando terug in de wachtlijst
van de MFC geplaatst. Op het moment dat de lijst van elementen door het lokaal
geheugen wordt teruggeven moet het oorspronkelijk DMA commando terug worden
geselecteerd om verder te gaan met uitvoering.
4. Indien er adresvertaling nodig is, wordt deze vertaling aan de Memory Management Unit (MMU) aangevraagd. Ook hier wordt het DMA-commando terug in de
lijst geplaatst. Na de adresvertaling moet het commando terug uit de lijst worden
geselecteerd.
5. Vervolgens wordt een busaanvraag aangemaakt voor het volgende blok data van het
commando. Een aanvraag is maximaal 128 bytes, maar kan ook minder zijn. De
busaanvraag wordt aan de bus interface unit (BIU) aangeboden.
6. De BIU selecteert de aanvraag uit zijn lijst en plaatst het commando op de EIB.
Het commando wordt door de EIB geordend, rekening houdend met de commando’s
van de andere elementen. Indien de transfer het hoofdgeheugen nodig heeft, zal de
memory interface controller (MIC) het commando bevestigen aan de EIB, die op
zijn beurt aan de BIU aangeeft dat het commando werd aanvaard. Hierna kan de
datatransfer beginnen.
7. De BIU in de MFC voert de nodige leesoperaties op het lokaal geheugen uit. De
EIB transfereert de data tussen de BIU en de MIC, die de data van of naar het
hoofdgeheugen verplaatst.
8. Vervolgens wordt er voor elk nieuw blok data een nieuwe busaanvraag uitgestuurd.
Het DMA commando blijft in de lijst met uitvoerende commando’s van de MFC tot
21
Figuur 2.8: Schematische voorstelling verloop DMA transfer
alle busaanvragen zijn verwerkt. Ondertussen kunnen wel andere DMA commando’s
worden verwerkt.
Hoewel dit proces omslachtig lijkt, valt de latentie voor de SPU heel goed mee. Kistler
et al. [26] meten in hun werk het aantal klokcycli in elke stap. De resultaten zijn te
zien in Tabel 2.2. Hoewel de totale bijkomende latentie 290 klokcycli bedraagt, is dit
Tabel 2.2: Aandeel latentie DMA-fases bij kloksnelheid van 3.2 GHz.
Fase
Latentie(cycli)
DMA issue
10
DMA naar EIB
30
Lijstelementen ophalen
10
Coherentieprotocol
100
Datatransfer naar LS
140
Totaal
290
22
Tabel 2.3: Overzicht van de belangrijkste SPU instructies.
Omschrijving
Aantal instructies
Latentie(cycli)
Pijplijn
Uitvoeringseenheid
Load en store
12
6
oneven
SLS
Branch hints
4
15
oneven
SLS
Branch resolutie7
36
4
oneven
SCN
Shuffle
35
4
oneven
SFS
Enkele precisie FP
6
6
even
SFP
Dubbele precisie FP
10
13
even
SFP
Byte operaties
3
4
even
SFP
Rotatie en shift
17
4
even
SFX
niet de latentie die door de SPU wordt geobserveerd. De SPU kan immers doorgaan met
uitvoering nadat een DMA commando in de wachtlijst van de MFC is geplaatst. Dit
vergt een totaal van slechts 10 klokcycli, waaruit blijkt dat de geheugenlatentie in de Cell
uitermate laag is. Dit werkt uiteraard maar in het geval er relevante instructies zijn die
kunnen worden uitgevoerd op de SPU terwijl het DMA commando wordt verwerkt.
2.4.3
SPU instructieset
Tabel 2.3 geeft een overzicht van de belangrijkste instructies in de instructieset van de
SPU. Voor een gedetailleerde uitleg wordt verwezen naar Appendix B in [19].
2.5
Programmeeromgeving
Bij het ontwikkelen van code voor de Cell werd steeds gebruik gemaakt van de door IBM
aangeboden Cell SDK versie 2.0. De code voor de PPU werd gecompileerd met behulp van
gcc 4.1.1 met een aangepaste back-end voor de PPU en de SPU’s. Programma’s werden
getest en hun uitvoeringstijd gemeten op een DD3.1 dual Cell blade met 1 GiB XDR RAM
ter beschikking gesteld door het Barcelona Supercomputing Center.
7
Sprongen die correct werden voorspeld door branch hints hebben geen delay. Verkeerd voorspelde
branches kosten 18 tot 19 cycli
23
Hoofdstuk 3
Matrixvermenigvuldiging
3.1
3.1.1
Inleiding
Keuze algoritme
Matrixvermenigvuldiging is een welbekend en vaak bestudeerd probleem. De elementen
van het product van twee matrices A en B worden als volgt berekend.
(AB)ij =
n
X
Air Brj
(3.1)
r=1
Er is reeds veel onderzoek gebeurd, zowel naar algemene methodes om matrixvermenigvuldiging te versnellen, als naar specifieke methodes op gedistribueerde en parallelle computersystemen. In dit hoofdstuk is het dan ook niet de bedoeling om deze algoritmes te
bestuderen of zelf een nieuw te ontwikkelen, maar eerder om vanuit de naı̈eve implementatie te vertrekken en incrementele verbeteringen toe te passen, gebruik makend van de
kennis van de cellarchitectuur. De basis van waaruit wordt vertrokken, is Programma 1
op pagina 25.
De basis van dit algoritme zal steeds behouden blijven, maar optimalisaties zullen
de prestatie verbeteren door specifieke aanpassingen die de onderliggende architectuur
uitbuiten. Het doel van dit hoofdstuk is dan ook om de verschillende noodzakelijke tussenstappen en pijnpunten aan te duiden die nodig zijn om een bestaand algoritme zo
efficiënt mogelijk te implementeren op de Cell .
3.1.2
Gelijkaardig werk
Er is reeds veel onderzoek verricht over matrixvermenigvuldiging, zowel op puur wiskundig
gebied als op het gebied van de computerwetenschappen. Hoewel er in dit hoofdstuk
24
Programma 1 Basisalgoritme voor matrixvermenigvuldiging.
void
matrix mul ( int A[N ] [ N] , int B [N ] [ N] , int C [N ] [ N ] )
{
f o r ( i = 0 ; i < N; i ++) {
f o r ( j = 0 ; j < N; j ++) {
sum = 0 ;
f o r ( k = 0 ; k < N; k++) {
sum += A[ i ] [ k ] ∗ B [ k ] [ j ] ;
}
C [ i ] [ j ] = sum ;
}
}
}
niet op deze verwezenlijkingen wordt verder gebouwd, is het toch interessant om enkele
resultaten te bespreken.
De naı̈eve implementatie van het algoritme voor matrixvermenigvuldiging is O(n3 )
met n de dimensie van de matrix. Het onderzoek op wiskundig gebied streeft ernaar om
algoritmes te ontwikkelen die een betere complexiteit hebben. De twee belangrijkste zijn
de algoritmes van Strassen [33] en CoppersmithWinograd [10], met een complexiteit van
O(n2.807 ) en O(n2.376 ) respectievelijk. Hoewel deze algoritmes dus asymptotisch sneller
zijn, zijn ze vaak moeilijker te implementeren of bieden ze pas significante verbeteringen
voor matrices met een grote dimensie.
Omdat matrixvermenigvuldiging in veel wetenschappelijke en andere software voorkomt, is er reeds gedurende vele jaren intensief onderzoek gedaan naar matrixvermenigvuldiging op gedistribueerde systemen. Enkele veelgebruikte algoritmes zijn Parallel Universal Matrix Multiplication Algorithms on Distributed Memory Concurrent Computers
(PUMMA)[9] en de implementatie van matrixvermenigvuldiging in Parallel Basic Linear
Algebra Subroutines (PBLAS)[8].
Aangezien er reeds vele decennia onderzoek is gebeurd naar matrixvermenigvuldiging,
is het niet de bedoeling om hier een incrementele bijdrage te leveren. De bedoeling is eerder
om stapsgewijs het efficiënt implementeren van een algoritme op de Cell te illustreren. Er
bestaat reeds een volledig geoptimaliseerde implementatie van matrixvermenigvuldiging
voor de Cell, die de theoretische piekprestatie van 200 GFLOPS zeer dicht benadert [14].
25
3.2
Programmeermodel
Omdat programmeren voor de Cell heel wat complexer is in vergelijking met traditionele
architecturen, is het aangewezen om stapsgewijs te werk te gaan. In elke stap wordt het
programma incrementeel geoptimaliseerd, waarbij steeds meer mogelijkheden van de Cell
worden gebruikt. Het vooropgestelde schema dat in dit hoofdstuk wordt gehanteerd, is
als volgt:
1. Schrijf scalaire code voor de PPU. Omdat de PPU architecturaal veel overeenkomsten vertoont met normale microprocessors, is dit een vrij eenvoudige en
pijnloze stap. Programma’s die werken op een PowerPC processor, zullen zonder
aanpassingen werken op de PPU indien de gebruikte bibliotheken beschikbaar zijn
op de Cell.
2. Vorm de scalaire code om naar code voor de SPU. In de eerste stap worden
alle berekeningen nog gedaan op de PPU, terwijl in een optimaal programma de
SPU’s het meeste werk voor hun rekening nemen. Daarom bestaat de volgende stap
erin om de originele code van de PPU te laten draaien op de SPU. In deze stap
moet vooral aandacht worden besteed aan de geheugenstructuur. Eerst en vooral
moet alle nodige data van het hoofdgeheugen naar het lokaal geheugen worden getransfereerd via een DMA-transfer. De uiteindelijke resultaten moeten terug naar
het hoofdgeheugen zijn geschreven wanneer de SPU klaar is met uitvoeren. Hierbij
komt onmiddellijk het beperkt lokaal geheugen naar voor. Traditionele algoritmen
gaan vaak uit van een groot hoofdgeheugen, en gebruiken dan ook grote datastructuren. De SPU heeft echter maar 256 KiB voor zowel code als data, zodat een
voorzichtiger gebruik noodzakelijk is. Dit kan door niet het volledige programma in
een enkele SPU-draad uit te voeren, maar door een fijnere granulariteit te kiezen,
vb. een nieuwe draad aanmaken per nieuwe functieoproep. Een middenweg is vaak
de oplossing, waarbij een aantal functies worden gegroepeerd tot een enkele SPUdraad om het creëeren van nieuwe draden te beperken. Om het geheugengebruik
te beperken wordt dan enkel die data in het lokaal geheugen bewaard die op het
moment van de berekeningen noodzakelijk is.
3. Partitioneer over verschillende SPUs . De vorige stap levert code op die functioneel correct is, maar niet optimaal gebruik maakt van alle acht de SPU’s. In
deze stap wordt het oorspronkelijk algoritme geparallelliseerd over de SPU’s. Naast
de normale problemen zoals synchronisatie en communicatie, speelt de granulariteit
van de opsplitsing en het aantal dynamisch aangemaakte draden een belangrijke rol.
26
Indien teveel code per draad wordt uitgevoerd, is de kans groot dat het geheugen te
klein is. Langs de andere kant is het aanmaken van een groot aantal draden vaak niet
optimaal vanwege de tijd die nodig is om nieuwe draden aan te maken. Niet alleen
het detecteren van parallellisme in het programma, maar het optimaal opsplitsen is
dan ook van groot belang in deze stap.
4. Implementeer specifieke optimalisaties. De voorgaande stappen laten vaak
een significante versnelling zien ten opzichte van de oorspronkelijke implementatie.
Om echter alle mogelijkheden van de Cell te benutten, zijn een aantal specifieke
optimalisaties noodzakelijk. Hoewel deze quasi eindeloos zijn, worden hier slechts
enkele opgesomd:
• Balanceren berekeningen en geheugengebruik. De SPE’s hebben een MFC en
een SPU die in parallel DMA-transfers kan verwerken en kan rekenen op data
die reeds aanwezig is. Om de latentie van het geheugen te verbergen, moeten datatransfers dan ook overlapt worden met nuttige berekeningen. DMA-transfers
worden dan ook best zo vroeg mogelijk begonnen.
• Vermijden ingewikkeld controleverloop. Omdat de SPU geen sprongvoorspeller
heeft, is de impact van een complex controleverloop vaak groot. Indien mogelijk
moet dit dus vermeden worden, eventueel door gebruik te maken van branch
hints of het verwijderen van sprongen daar gebruik te maken van vectorinstructies.
• Lusontvouwing en andere lusoptimalisaties. Vanwege het groot aantal registers,
zijn optimalisaties zoals lusontvouwing en software pipelining vaak heel effectief.
• Vectorinstructies. De belangrijkste versnelling in de vierde stap komt echter
door gebruik te maken van vectorinstructies. Hoewel niet alle algoritmes zich
hiertoe lenen, is de potentiële versnelling aanzienlijk. Zo kunnen operaties
op enkele-precisie floating-point getallen met een factor vier worden versneld,
indien de code zich hiertoe leent.
• Gebruik dubbele pijplijn. De SPU heeft twee gediversifieerde pijplijnen, die
het aantal instructies per klokcyclus flink kunnen opdrijven indien hier rekening mee wordt gehouden. Dit kan samenwerken met andere technieken zoals
lusontvouwing, waarbij de oneven pijplijn de resultaten van de vorige opslaat
terwijl de even pijplijn de resultaten van de volgende iteratie berekent.
Wanneer dit programmeermodel wordt gevolgd, wordt de bijkomende complexiteit in
elke stap beperkt, terwijl het uiteindelijke programma toch zo optimaal mogelijk wordt ge-
27
houden. Een opmerking die hier moet worden gemaakt is dat er nog weinig ondersteuning
is voor profilering van een volledig programma op de Cell. In de eerste stappen kan echter
nog worden afgegaan op kennis opgedaan op een ander platform met betere ondersteuning.
3.3
3.3.1
Matrixvermenigvuldiging op de PPU
Aanpassingen
Aangezien de PPU gebaseerd is op de PowerPC architectuur, gebeurt de initiële implementatie zonder aanpassingen ten opzichte van de implementatie voor traditionele microprocessors. Tegenover deze grote eenvoud staat wel een slechte prestatie, aangezien
slechts een heel beperkt deel van de mogelijkheden van de Cell worden gebruikt. Om toch
een implementatie te bekomen die relevanter is als referentie, wordt gebruik gemaakt van
lustegeling[37, 35]. Lustegeling is een techniek die het datagebruik aanpast zodat optimaal gebruik wordt gemaakt van caches. Bij de naı̈eve implementatie wordt elk element
uit matrix B N keer gelezen. De afstand tussen dit hergebruik is N 2 , waardoor een cachehit1 enkel voorkomt indien de cache groot genoeg is om N 2 waarden van de matrix op te
slaan. Lustegeling zorgt voor een kleinere hergebruiksafstand van de data, zodat er meer
cachetreffers optreden. De code van ons algoritme ziet er dan als in Programma 2.
Indien de waarde van block voldoende klein wordt gekozen, kunnen alle leestoegangen
tot B (behalve de eerste van elk element) gebeuren naar de caches. Aangezien deze techniek
veronderstelt dat data wordt opgeslagen in caches, is ze enkel voordelig op de PPU, en
niet op de SPU.
3.3.2
Resultaten
De uitvoeringstijd met en zonder lustegeling voor verschillende dimensies is weergegeven
in Figuur 3.1. Voor voldoende kleine matrices is de prestatiewinst van lustegeling een
factor 4. Wanneer de matrices echter te groot worden en er meer cachemissers als gevolg
optreden, heeft lustegeling een versnelling van ongeveer 35. Het overgangspunt ligt bij een
dimensie van 512x512.
Hoewel deze techniek niet bruikbaar is op de SPU vanwege het ontbreken van caches,
wordt voor verdere vergelijkingen tussen de PPU en de SPU steeds lustegeling verondersteld waar mogelijk. Hoewel dit op het eerste zicht de PPU bevoordeelt, is dit noodzakelijk
om een oneerlijke vergelijking te voorkomen. Bij de SPU kunnen we immers zelf ten allen
1
Er wordt hierbij een Last Recently Used -vervangingschema verondersteld.
28
Programma 2 Matrixvermenigvuldiging met lustegeling.
void
matrix mul ( int A[N ] [ N] , int B [N ] [ N] , int C [N ] [ N ] )
{
f o r ( kk =0;kk<N; kk+=b l o c k ) {
f o r ( j j =0; j j <N; j j+=b l o c k ) {
f o r ( i =0; i <N; i ++){
f o r ( k=kk ; k<min ( kk+block ,N ) ; k++){
f o r ( j= j j ; j <min ( j j +block ,N ) ; j ++){
C[ i ] [ j ]+=A[ i ] [ k ] ∗B [ k ] [ j ] ;
}
}
}
}
}
}
tijde bepalen wat er zich in het lokaal geheugen bevindt, zodat grote latenties voor leesof schrijfoperaties naar het geheugen niet zullen voorkomen.
De schaling van de uitvoeringstijd voldoet aan de verwachtingen, namelijk kubisch in
functie van de dimensie. Dit stemt overeen met de bevinding dat het basisalgoritme van
orde O(N 3 ) is. Voor de gedetailleerde resultaten van de metingen wordt verwezen naar
bijlage A.
3.4
3.4.1
Matrixvermenigvuldiging op een enkele SPU
Aanpassingen
Het grootste probleem bij het aanpassen van een algoritme voor uitvoering op de SPU is de
keuze van de opdeling. In het algemeen is een volledig programma te groot om rechtstreeks
op een enkele SPU uit te voeren, en dus moet het algoritme worden opgesplitst in kleinere
afzonderlijke delen. Een goede keuze is om de hete code2 in een aparte SPU-draad uit te
voeren.
De keuze bij matrixvermenigvuldiging is echter triviaal, omdat het algoritme zeer beperkt is qua codegrootte, en de hete code zich uiteraard in de binnenste lus bevindt. Bij
2
Hete code is code die het grootste deel van de uitvoeringstijd in beslag neemt.
29
Figuur 3.1: Uitvoeringstijd matrixvermenigvuldiging op de PPU met en zonder lustegeling.
meer complexe algoritmes is deze keuze echter minder eenvoudig, zoals in hoofdstuk 4
duidelijk zal worden.
Hoewel de keuze van de uit te voeren code eenvoudig was, is het geheugengebruik
minder eenvoudig op te lossen. Als we algoritme 1 rechtstreeks willen uitvoeren, moeten
we ervoor zorgen dat er voldoende geheugen is gealloceerd voor matrix A, B én C. Voordat
het algoritme kan worden uitgevoerd moeten de matrices A en B via DMA-transfers van
het hoofdgeheugen naar het lokaal geheugen worden gekopieerd. Na uitvoering moet
het resultaat van matrix C via DMA-transfers terug naar het hoofdgeheugen worden
geschreven. Het probleem hierbij is de beperkte grootte van het lokaal geheugen. In de
veronderstelling dat de matrices elementen van 32 bit bevatten, zoals integers of enkeleprecisie floating-point getallen, is het algoritme zonder aanpassingen slechts werkbaar voor
matrices tot grootte 147x147. Dit is zelfs een overschatting, omdat niet de volledige 256
KiB beschikbaar is voor data. Uiteraard moet dit gebrek aan schaalbaarheid worden
opgelost om een degelijk algoritme te bekomen.
De oplossing voor het geheugengebruik is om enkel die data in het lokaal geheugen op
te slaan die strikt noodzakelijk is. Om het geheugengebruik op een zo eenvoudig mogelijke
manier te beperken, is er voor gekozen om steeds één element van de matrix C te berekenen.
Dit houdt in dat er slechts een enkele rij van matrix A en een enkele kolom van matrix B
nodig is. Hierdoor stijgt het geheugengebruik slechts lineair in plaats van kwadratisch met
de dimensie van de matrices. De maximale grootte is nu 32768x32768. Om te voorkomen
dat voor elk element telkens opnieuw zowel een rij als kolom moet worden getransfereerd,
worden de nieuwe elementen van matrix C rij per rij berekend, waardoor de huidige rij
30
van matrix A kan worden hergebruikt. In totaal wordt voor een grootte van NxN matrix
A dan 1 keer getransfereerd naar het lokaal geheugen, en matrix B N keer. De herhaling
van DMA-transfers voor dezelfde data is de prestatie die moet worden ingeboet om het
algoritme schaalbaar te maken. Uiteindelijk ziet het algoritme op de SPU in pseudocode
er als volgt uit.
Programma 3 Matrixvermenigvuldiging op de SPU.
void
matrix mul ( int A[N ] [ N] , int B [N ] [ N] , int C[N ] [ N ] )
{
A array=m a l l o c (N∗ s i z e o f ( int ) ) ;
B column=m a l l o c (N∗ s i z e o f ( int ) ) ;
f o r ( i = 0 ; i < N; i ++) {
DMA in ( A array ,A[ i ] [ ] ) ;
f o r ( j = 0 ; j < N; j ++) {
DMA in ( B column , B [ ] [ j ] ) ;
f o r ( k = 0 ; k < N; k++) {
C [ i ] [ j ] += A array [ k ] ∗ B column [ k ] ;
}
}
}
}
3.4.2
DMA-transfers
Om de DMA-transfers tussen de SPU’s en het hoofdgeheugen correct te laten verlopen,
moet er aan een aantal randvoorwaarden worden voldaan. In de eerste plaats moet het
startadres van de data die wordt geDMA’ed bekend zijn bij uitvoering op de SPU. Concreet in dit voorbeeld komt dit er op neer dat de SPU het adres van matrices A,B en C
nodig heeft tijdens de uitvoer. Om ervoor te zorgen dat een correct aantal bytes wordt
getransfereerd, is ook de grootte van de matrices nodig. Al deze argumenten worden
gegroepeerd in een contextstructuur. Bij het aanmaken van de SPU-draad wordt een
verwijzing hiernaar meegegeven. Vóór het eigenlijke algoritme voor de matrixvermenigvuldiging wordt uitgevoerd, zal de SPU deze contextstructuur kopiëren naar zijn lokaal
geheugen. Daarna zijn alle nodige variabelen bekend om de rest van de DMA-transfers
correct te laten verlopen.
31
Omdat DMA-instructies expliciet door de programmeur moeten worden geschreven,
moet de programmeur er ook zelf voor zorgen dat aan enkele bijkomende randvoorwaarden
wordt voldaan. De belangrijkste zijn de alignatie van geheugenadressen en de grootte van
getransfereerde data.
De grootte van een transfer moet steeds 1, 2, 4, 8 of een veelvoud van 16 bytes lang zijn.
De maximale grootte is 16 KiB. Het eerste probleem is op te lossen door de lengte van een
DMA-transfer naar boven af te ronden tot een correct getal en indien nodig padding bytes
toe te voegen. Transfer van data van meer dan 16 KiB kan eenvoudig worden opgesplitst
in een aantal kleinere transfers. Dit probleem is dan ook eenvoudig te omzeilen.
Een moeilijker probleem om op te lossen is de alignatie van geheugenadressen. Er
wordt geëist dat een adres in het hoofdgeheugen, zowel voor bron-als doeladressen, steeds
is afgerond op een veelvoud van 128 bytes. Wanneer geheugen wordt gereserveerd met de
malloc-functie, is dit niet gegarandeerd het geval. Daarom moeten maatregelen worden
getroffen om dynamische geheugenallocatie op een correcte manier te laten verlopen die
DMA-transfers mogelijk maakt.
Een eerste manier bestaat erin om het adres naar het gealloceerd geheugen dat wordt
teruggeven, af te ronden naar een veelvoud van 128. De maximale verschuiving ten opzichte
van het originele adres is dan 127, zodat er 127 bytes extra moeten worden gealloceerd
om te voorkomen dat data wordt gelezen of geschreven in een adres dat niet toebehoort
aan de gealloceerde structuur. Een efficı̈ente manier om dit te verwezenlijken is als volgt.
Programma 4 Alignatie geheugenadres.
r e t = ( int ∗ ) m a l l o c (127+ s i z e ∗ s i z e o f ( int ) ) ;
r e t= (127+( unsigned ) r e t )& 0 x 7 f ;
Hoewel deze manier voor een correct adres zal zorgen, is ze niet vrij van problemen.
Wanneer namelijk het geheugen terug wordt vrijgegeven met behulp van de free-functie,
zal dit voor een foutmelding zorgen. Indien immers de data vanaf het nieuwe adres wordt
vrijgegeven, blijft het geheugen tussen het oude en het nieuwe geheugen gealloceerd. Dit
geheugen kan nooit meer worden vrijgegeven, omdat het originele startadres is overschreven. Een eenvoudige manier om dit te omzeilen is het originele adres bij te houden, en
het afgeronde adres in een andere variabele bij te houden. Er wordt dan verder gewerkt
met het nieuwe adres, behalve wanneer het geheugen terug moet worden vrijgegeven. Dit
wordt echter snel omslachtig wanneer er veel datastructuren zijn waarvoor dynamisch geheugen wordt gealloceerd. De kans op fouten is ook reëel, omdat het oude adres enkel kan
en moet gebruikt worden bij het vrijgeven van data.
32
Een betere manier, en diegene die in de rest van alle geschreven code is toegepast, is
om eigen functies te schrijven voor geheugenallocatie en -vrijgave. Het idee is om terug
het oude adres verkregen met malloc af te ronden, maar het oude adres bij te houden in
het gealloceerde deel geheugen zelf. Bij oproepen van de aangepaste free-functie wordt
dit oud adres uitgelezen, waarna het geheugen startend op dit adres opnieuw kan worden
vrijgegeven. De aangepaste functies zijn hierna weergegeven.
Programma 5 Aangepaste functies voor gealigneerde geheugenallocatie
void ∗ m a l l o c a l i g n ( int s i z e , unsigned long a l i g n )
{
void ∗ r e t ;
char ∗ r e a l ;
unsigned long o f f s e t ;
r e a l = ( char ∗ ) m a l l o c ( s i z e + s i z e o f ( void ∗ ) + ( a l i g n − 1 ) ) ;
o f f s e t = ( a l i g n − ( unsigned long ) ( r e a l + s i z e o f ( void ∗ ) ) ) ;
o f f s e t = o f f s e t & ( a l i g n −1);
r e t = ( void ∗ ) ( r e a l + s i z e o f ( void ∗ ) ) + o f f s e t ;
∗ ( ( void ∗ ∗ ) ( r e t ) −1) = ( void ∗ ) ( r e a l ) ;
return ( r e t ) ;
}
void f r e e a l i g n ( void ∗ p t r ) {
void ∗ r e a l ;
r e a l = ∗ ( ( void ∗ ∗ ) ( p t r ) −1);
free ( real );
}
3.4.3
Resultaten
In Figuur 3.2 wordt een vergelijking gegeven tussen de uitvoering van matrixvermenigvuldiging op de PPU en de SPU. Bij het algoritme op de PPU is er lustegeling gebruikt,
33
Figuur 3.2: Vergelijking uitvoering matrixvermenigvuldiging op de PPU en de SPU.
de uitvoering op de SPU is de implementatie van algoritme 3. Er is een betere prestatie
te zien voor de PPU dan voor de SPU. Dit prestatievoordeel neemt af bij toenemende
dimensie. Er zijn hier een aantal verklaringen voor:
• Het aanmaken van een SPU-draad vergt een zekere tijd. Uit uitgevoerde testen
blijkt dit rond de 5ms te liggen. Bij korte uitvoeringstijden is deze vaste vertraging
nadeliger dan bij lange uitvoeringstijden: bij dimensie 128 is dit aandeel ongeveer
13% , bij dimensie 1024 is dit nog slechts 0.04%.
• Voordat de SPU zijn berekeningen kan uitvoeren, moet de data via DMA-transfers
in het lokaal geheugen worden geplaatst. Dit vergt een vaste vertraging zoals weergegeven in Tabel 2.2 op pagina 22. Verder is er een vertraging evenredig met de grootte
van de datastructuren. De vaste vertraging zal terug een groter effect hebben bij
kleine dimensies, terwijl de veranderlijke component evenredig met de grootte belangrijker zal worden bij grote dimensies. De totale grootte van de matrices neemt
echter kwadratisch toe met de dimensie, terwijl de berekeningen een kubisch verband
hebben. In het aangepast algoritme op de SPU is het zelfs zo dat de DMA-transfers
slechts een enkele rij of kolom transfereren, waardoor er slechts lineaire schaling is.
Dit zorgt er voor dat voor toenemende dimensie het aandeel van de berekeningen
belangrijker wordt voor de totale uitvoeringstijd. Hierbij is de SPU in het voordeel vanwege de geoptimaliseerde implementatie van de architectuur voor reken- en
dataintensieve toepassingen. Tabel 3.1 geeft de opgemeten procentuele verhouding
tussen de latentie van DMA-transfers en de berekeningen zelf.
34
Tabel 3.1: Opgemeten verhouding DMA-transfer en berekeningen bij matrixvermenigvuldiging
op 1 SPU.
3.5
3.5.1
Dimensie
DMA-transfer
Berekeningen
128
45,66%
54,34%
256
35,54%
64,46%
512
29,28%
70,72%
1024
27,32%
72,68%
Parallellisatie over meerdere SPU’s
Keuze parallellisatieschema
In de vorige stap werd de uitvoering van de PPU naar de SPU verplaatst. Het sequentiële
karakter van het algoritme werd echter behouden, waardoor slechts één SPU kon worden
gebruikt. Om optimale prestatie’s te bekomen moeten alle SPU’s worden betrokken bij
de uitvoering op een manier die de overlast door parallellisatie beperkt en voor optimale
schaalbaarheid zorgt.
Matrixvermenigvuldiging is embarrassingly parallel [15], waardoor deze stap voor dit
specifiek probleem heel eenvoudig wordt. We kiezen ervoor om data-partitionering [5] toe
te passen, waarbij elke SPU inwerkt op een subset van de data. Concreet komt dit er op
neer dat elke SPU slechts een beperkt deel van het resultaat berekent. De PPU berekent op
voorhand welke rijen van het resultaat in matrix C moeten worden uitgerekend door welke
SPU. Deze indices worden meegegeven in de contextstructuur voor de betreffende SPU.
Bij voldoende grote matrices zorgt dit voor een ongeveer gelijke verdeling van de werklast,
aangezien de bewerkingen voor de berekingen een deterministische uitvoeringstijd hebben
maar de DMA-transfers niet.
3.5.2
Resultaten en schaalbaarheid
Figuur 3.3 geeft een overzicht van de uitvoeringstijd voor een variërend aantal SPU-draden
en voor verschillende dimensies. Bij een dimensie 512 en meer blijkt de uitvoeringstijd
bijna perfect lineair mee te schalen met het aantal gebruikte SPU’s. De versnelling bij
het gebruik van 8 SPU’s ten opzichte van 1 SPU is ongeveer 6,7. Dit is minder dan het
35
Figuur 3.3: Schaling matrixvermenigvuldiging in functie van aantal SPU’s en dimensie.
theoretisch maximum van 8, maar deze bovengrens wordt niet bereikt omdat de DMAtransfers voor een enkele SPU nog steeds even lang duren. In realiteit zal het zelfs zo
zijn dat DMA-transfers langer zullen duren omdat de toegang tot het hoofdgeheugen een
flessenhals zal vormen indien meerdere SPU’s tegelijk hier toegang tot willen. De quasi
lineaire schaling toont echter aan dat de keuze voor de parallellisatie goed meeschaalt,
wat te verwachten was aangezien er geen communicatie nodig is tussen de SPU-draden
onderling.
Voor kleine dimensies, en dan vooral voor dimensie 128, zien we een negatief effect
bij het toewijzen van meer SPU-draden aan het algoritme. Dit is te verklaren door de
vertraging voor het aanmaken van meerdere SPU-draden, die de bekomen versnelling van
de berekeningen teniet doen.
3.6
Optimalisaties
De vorige aanpassingen zorgden voor een evenwichtige verdeling van de werklast over
de SPU’s. Hoewel er al een grote versnelling merkbaar was, is het algoritme nog niet
geoptimaliseerd voor uitvoering op de SPU. Hierna volgen enkele optimalisaties die de
prestatie verbeteren door de onderliggende architectuur maximaal te benutten. Tenzij
expliciet anders vermeld, wordt er steeds van alle 8 SPU’s gebruik gemaakt.
3.6.1
Dubbele buffering
Matrixvermenigvuldiging is een dataintensief algoritme. Samen met de aanpassing voor
een beperkter geheugengebruik zorgt dit voor een groot aantal DMA-transfers. In de
36
Figuur 3.4: Conceptuele werking dubbele buffering.
laatste versie van het algoritme worden deze DMA-transfers nog inefficiënt verstuurd:
wanneer data nodig is wordt een aanvraag verstuurd, en wordt er gewacht met uitvoering
tot deze data beschikbaar is. Deze manier maakt geen gebruik van de parallelle werking
van de SPU en de MFC. Een betere manier is om de aanvraag voor nieuwe data zo vroeg
mogelijk te versturen, en ondertussen verder te werken met de data die reeds aanwezig is in
het lokaal geheugen. Op die manier wordt de latentie geminimaliseerd. Een eenvoudige en
doeltreffende manier om dit te realiseren is door gebruik te maken van dubbele buffering.
Bij dubbele buffering wordt er voor de datastructuur die de latentie veroorzaakt in
twee buffers voorzien. Tijdens elke iteratie is er één buffer waarop de berekingen worden uitgevoerd, en één buffer waarin nieuwe data wordt geschreven door DMA-transfers.
Wanneer de berekeningen op de ene buffer zijn beëndigd en de volgende iteratie wordt
gestart, wisselen de buffers qua functionaliteit. Indien er voldoende berekeningen worden
uitgevoerd in parallel met de datatransfer, kan zo de latentie van de DMA-transfer bijna
volledig worden verborgen. Het concept van dubbele buffering is grafisch voorgesteld in
Figuur 3.4.
Toegepast op het probleem van matrixvermenigvuldiging, is het aangewezen om de
kolommen van de B-matrix te bufferen. Aangezien in de binnenste lus van het algoritme er
een nieuwe DMA-transfer voor deze structuur wordt gestart, zal dit in het niet gebufferde
geval voor een grote latentie zorgen. Omdat het vermenigvuldigen van een groot aantal
elementen veel tijd in beslag neemt, kan dubbele buffering deze latentie verbergen. In
pseudocode krijgen we het algoritme in Programma 6.
37
Programma 6 Matrixvermenigvuldiging met dubbele buffering
void
matrix mul ( int A[N ] [ N] , int B [N ] [ N] , int C [N ] [ N ] )
{
A array=m a l l o c (N∗ s i z e o f ( int ) ) ;
B column [ 0 ] = m a l l o c (N∗ s i z e o f ( int ) ) ;
B column [ 1 ] = m a l l o c (N∗ s i z e o f ( int ) ) ;
f o r ( i = 0 ; i < N; i ++) {
DMA in ( A array ,A[ i ] [ ] ) ;
DMA in ( B column [ 0 ] , B [ ] [ 0 ] ) ;
f o r ( j = 0 ; j < N; j ++) {
DMA in ( B column [ ( j +1)%2] ,B [ ] [ j + 1 ] ) ;
f o r ( k = 0 ; k < N; k++) {
C [ i ] [ j ] += A array [ k ] ∗ B column [ j % 2 ] [ k ] ;
}
}
}
}
Om de impact van dubbele buffering op te meten, wordt bij de uitvoeringstijd van het
algoritme een differentiatie gemaakt tussen de tijd die besteed wordt aan het wachten op
het afronden van DMA-transfers, en het berekenen van een element uit de C-matrix. Het
aandeel van DMA-instructies en berekeningen voor zowel het origineel als het algoritme
met dubbele buffering is grafisch voorgesteld in Figuur 3.5. De verhouding werden steeds
berekend ten opzichte van de totale uitvoeringstijd van het originele, niet-gebufferde algoritme. In het origineel algoritme is DMA-latentie verantwoordelijk voor ongeveer 40%
van de totale uitvoeringstijd, terwijl dit door dubbele buffering is teruggebracht naar 3%
voor dimensie 128, en minder dan 0,5% voor dimensie 1024. Dubbele buffering slaagt er
dus in om de latentie van de DMA-transfer nagenoeg volledig te verbergen.
Hoewel de DMA-latentie bijna verdwenen is, is de invloed van dubbele buffering op de
berekeningen minder positief. Op Figuur 3.5 is eveneens de verhouding van de uitvoeringstijd voor berekeningen en DMA-transfers tussen het gebufferd en het origineel algoritme
te zien. Hieruit blijkt duidelijk dat de latentie sterk is gereduceerd, maar dat de berekeningen ongeveer 20% meer in beslag nemen in verhouding met het originele algoritme. De
reden hiervoor is de extra berekeningen die nodig zijn om na elke iteratie de buffers van
38
Figuur 3.5: Verhouding DMA-latentie en berekeningen tussen origineel en gebufferd algoritme.
functie te doen veranderen. In algoritme 6 worden hiervoor modulo-operaties gebruikt,
die zeer veel extra tijd zullen vragen. Deze implementatie is dan ook niet in de eigenlijke
implementatie gebruikt. Er werd gekozen voor een enkele rijstructuur die beide buffers
bevat. De buffer voor DMA-opslag is dan een verwijzing naar deze structuur, met een
offset afhankelijk van de huidige iteratie. Analoog zullen de berekeningen dan inwerken
op de andere helft van de rijstructuur. Op deze manier kan dubbele buffering worden
bekomen door een simpele aanpassing van een enkele offset per iteratie.
Omdat globaal gezien de versnelling van de DMA-transfers veel groter is dan de vertraging van de berekeningen, wordt de matrixvermenigvuldiging versneld met een factor
1,2 tot 1,5. Uit Figuur 3.6 blijkt dat de versnelling groter wordt naarmate de dimensie
toeneemt. Dit is te verklaren door het feit dat de DMA-latentie bij het gebufferd algoritme
slechts toeneemt met het aantal DMA-transfers, en niet met de grootte. Tijdens het transfereren van de data zijn er immers voldoende instructies om deze vertraging te verbergen,
zodat enkel de vaste latentie voor het aanvragen van een DMA-transfer overblijft. Bij het
origineel algoritme is de totale latentie van de DMA-transfers kubisch afhankelijk van de
dimensie, net als de berekeningen. Dit is ook de verklaring voor de constante verhouding
tussen DMA-latentie en berekeningen bij het basisalgoritme, terwijl bij dubbele buffering
de DMA-latentie procentueel gezien steeds minder tijd in beslag neemt.
Om maximale prestatie te bekomen, moet de vertraging van de berekeningen worden
aangepakt.
39
Figuur 3.6: Uitvoeringstijd voor origineel, gebufferd en ontvouwd algoritme.
3.6.2
Lusontvouwing
Dubbele buffering werkt de negatieve impact van DMA-transfers op de uitvoeringstijd
nagenoeg volledig weg, maar heeft een negatieve invloed op de uitvoeringstijd van de
berekeningen zelf. Om verdere prestatieverhogingen te bekomen, moet er dus gefocust
worden op de binnenste lus van het algoritme. Een eerste eenvoudige techniek is gebruik
maken van lusontvouwing.
Lusontvouwing is een techniek die reeds lange tijd wordt toegepast, en een standaard
optimalisatie is in de meeste compilers. Het idee is om in de binnenste lus meerdere iteraties van het origineel probleem uit te voeren. Indien er geen afhankelijkheden zijn tussen
de verschillende iteraties, kan de compiler de instructies dan herordenen om zo een betere
prestatie te bekomen. Deze techniek heeft echter als grootste nadeel dat er meer registers
nodig zijn om alle data van de nieuwe lusiteraties te kunnen opslaan. Elke SPU heeft
echter 128 registers, waardoor de beperkingen van lusontvouwing nagenoeg onbestaande
zijn. Verder beschikt de SPU over een dubbele pijplijn, die nu beter benut kunnen worden
omdat er meer instructies zijn om te verdelen over de pijplijnen. De aangepaste versie
van het algoritme waarbij per iteratie 4 originele iteraties worden uitgevoerd is hierna
weergegeven3 .
Omdat lusontvouwing een versnelling van de binnenste lus tot gevolg heeft, verwachten
we een aanzienlijke versnelling van de berekeningen. Figuur 3.7 geeft de uitvoeringstijd
van de berekeningen voor het algoritme met dubbele buffering en lusontvouwing in verhouding tot het basisalgoritme. Hieruit blijkt duidelijk dat de berekeningen sterk versneld
3
Om de eenvoud te bewaren is de code voor dubbele buffering weggelaten. De resultaten veronderstellen
echter dat dubbele buffering is geı̈mplementeerd.
40
Programma 7 Lusontvouwing voor matrixvermenigvuldiging.
void
matrix mul ( int A[N ] [ N] , int B [N ] [ N] , int C [N ] [ N ] )
{
f o r ( i = 0 ; i < N; i ++) {
f o r ( j = 0 ; j < N; j ++) {
f o r ( k = 0 ; k < N/ 4 ; k++) {
m1 = A array [ 4 ∗ k ] ∗ B column [ 4 ∗ k ] ;
m2 = A array [ 4 ∗ k+1] ∗ B column [ 4 ∗ k + 1 ] ;
m3 = A array [ 4 ∗ k+2] ∗ B column [ 4 ∗ k + 2 ] ;
m4 = A array [ 4 ∗ k+3] ∗ B column [ 4 ∗ k + 3 ] ;
C [ i ] [ j ] += m1+m2+m3+m4 ;
}
}
}
}
zijn, namelijk tot slechts de helft van de tijd van het origineel. Op Figuur 3.6 is te zien
dat de totale versnelling hier sterk door stijgt. Bij een dimensie van 128 is de uitvoeringstijd een factor 1,25 korter in vergelijking met het origineel, bij een dimensie van 1024 is
dit reeds 3,89. De invloed van de dimensie is ook hier sterk aanwezig. De bovengrens
op de versnelling is te berekenen door de wet van Amdahl. Uit de resultaten blijkt dat
40% van de originele uitvoeringstijd besteed wordt aan DMA-transfers. Deze fractie is
in de limiet oneindig versneld door dubbele buffering. De overige 60% besteed aan berekeningen hebben een empirisch bepaalde maximale versnelling van ongeveer 2,35 door
gebruik te maken van lusontvouwing. Uit de wet van Amdahl volgt dan de theoretische
maximumversnelling:
1
0.6
+ 2.35
= 3.91666 . . .
Smax =
0.4
∞
We benaderen deze maximumversnelling reeds zeer dicht voor een dimensie van 1024.
41
Figuur 3.7: Verhouding berekeningen gebufferd en ontvouwd algoritme op origineel.
42
Hoofdstuk 4
Clustal W
4.1
4.1.1
Algemene beschrijving
Achtergrond
Clustal W [34] is een programma voor de gelijktijdige alignatie van meerdere DNAsequenties. Het maakt deel uit van BioPerf [4], een benchmark suite die de belangrijkste
applicaties uit de bioinformatica verzamelt. Bij het aligneren van sequenties wordt er gezocht naar gelijkaardige delen, wat kan wijzen op functioneel, structureel of evolutionair
verwantschap. Op deze manier kunnen de eigenschappen van nieuwe DNA-sequenties snel
achterhaald worden door vergelijking met reeds gekende sequenties.
Wanneer twee sequenties een gelijke voorouder hebben, kunnen hun verschillen verklaard worden door ofwel puntmutaties ofwel het verwijderen van een aminozuur bij een
van de sequenties. Bij het aligneren wordt er dan gezocht naar delen van de sequenties
die overeenkomen met zo weinig mogelijk aanpassingen. Daarbij kan onderscheid gemaakt
worden tussen globale alignatie en lokale alignatie. Globale alignatie zal de sequenties
over hun volledige lengte zo goed mogelijk proberen af te stemmen op elkaar, terwijl lokale
alignatie slechts afzonderlijke delen zal onderzoeken. Lokale alignatie is meestal interessanter, omdat zelfs als sequenties globaal gezien sterk van elkaar verschillen, de eventuele
gelijkenissen toch zullen worden teruggevonden. Daar staat tegenover dat het vinden van
gelijkaardige afzonderlijke delen een bijkomende moeilijkheid is.
Voor het aligneren van twee sequenties wordt meestal gebruik gemaakt van dynamisch
programmeren. Het Smith-Waterman algoritme [32] wordt hierbij het vaakst gebruikt.
De belangrijkste elementen in het algoritme zijn het gebruik van een substitutiematrix
en gap penalties. De substitutiematrix kent een score toe voor de overeenkomst of het
verschil van twee aminozuren in de sequenties. Mutaties die weinig van elkaar verschillen
43
krijgen op die manier een grotere score toebedeeld. Wanneer er een gat in de sequenties
wordt geı̈ntroduceerd, wordt de score negatief beı̈nvloed door een gap penalty. Er bestaan
hier verschillende vormen van, gaande van een constante waarde tot een lineair afnemende
waarde wanneer een bestaand gat wordt uitgebreid. Dynamisch programmeren garandeert
een optimale oplossing gegeven een substitutiematrix en gap penalties. De moeilijkheid zit
hem dan ook in het opstellen van deze scores, wat vaak empirisch gebeurd.
Wanneer meerdere sequenties tegelijkertijd met elkaar moeten worden vergeleken, blijkt
dynamisch programmeren al snel ontoereikend te zijn. De substitutiematrix moet immers
van een twee-dimensionale structuur worden uitgebreid naar een n-dimensionale structuur, met n het aantal sequenties. Hierdoor neemt de zoekruimte kwadratisch toe met
stijgende n. Er is zelfs aangetoond dat een globaal optimum vinden voor n sequenties een
NP-compleet probleem is [24]. De zoekruimte wordt dan ook meestal beperkt door de
sequenties eerst paarsgewijs met elkaar te vergelijken. Hieruit worden dan de twee sequenties die de meeste overeenkomsten met elkaar vertonen gekozen. Er wordt dan progressief
verder gealigneerd door steeds de volgende sequentie die het best op de reeds gealigneerde
lijkt, aan te passen aan de reeds bestaande alignatie [13]. Deze methode levert echter geen
gegarandeerd optimale oplossing, en is dan ook een heuristiek. De grootste gevoeligheid
zit in de initiële alignatie, die zeker bij sterk verschillende sequenties een grote fout kan
introduceren. Er zijn dan ook veel uitbreidingen gebeurd op deze techniek om de foutgevoeligheid te verminderen. Clustal W is één van de meer populaire varianten op deze
progressieve alignatie.
4.1.2
Beschrijving algoritme
Het basisalgoritme voor het aligneren van meerdere sequenties bestaat uit drie fasen:
1. De sequenties worden paarsgewijs gealigneerd.
2. Uitgaande van de scores van de paarsgewijze alignatie wordt een boom opgebouwd.
3. De sequenties worden progressief gealigneerd volgens hun plaatsing in de boom.
De verschillende fasen en hun implementatie in Clustal W worden hierna in meer detail
besproken.
Paarsgewijze alignatie (PW)
In de eerste fase worden de sequenties per paar gealigneerd. Hierbij wordt de bestaande
techniek van dynamisch programmeren gebruikt. Er bestaan twee gap penalties: één voor
44
het openen van een gat en één voor het uitbreiden van een bestaand gat. De score wordt
berekend als het aantal identieke aminozuren in de beste alignatie gedeeld door het aantal
vergeleken aminozuren. Alle scores worden bijgehouden in een scorematrix die de basis
vormt van de volgende stap.
Opstellen boomstructuur (GT)
Uitgaande van de afstandsmatrix uit de eerste fase wordt een Neighbour-Joining algoritme [31] uitgevoerd, waarbij gelijkaardige sequenties zo dicht mogelijk bij elkaar worden
geplaatst. Dit levert een aantal bomen zonder ouderknoop op, waarbij het gewicht van
de takken evenredig is met het onderlinge verschil van de sequenties op de bladeren van
de takken. De ouderknoop wordt dan zodanig gekozen dat de gemiddelde lengte van de
takken aan beide kanten van de boom even lang zijn. Deze boomstructuur wordt dan
verder gebruikt om een gewicht toe te kennen aan de sequenties. Hoe verder een sequentie
zich van de ouderknoop bevindt, hoe groter de score. Het gewicht van takken die gedeeld
worden door meerdere sequenties wordt evenredig verdeeld over deze takken. Het idee
hierachter is dat sequenties die quasi identiek zijn een lage score krijgen, zodat er een
beter onderscheid kan gemaakt worden tussen sequenties die sterk van elkaar verschillen.
Progressieve alignatie (PA)
In de laatste stap wordt een serie van paarsgewijze alignaties gebruikt om steeds grotere
groepen van sequenties te aligneren. Er wordt van de bladeren van de boom vertrokken,
waarbij de twee sequenties die het meest op elkaar lijken eerst worden samengenomen.
Door stelselmatig naar de ouderknoop toe te werken, is het verschil tussen de nieuwe
sequentie en de reeds gealigneerde groep sequenties zo klein mogelijk. Het aligneren in
deze fase gebeurt opnieuw op basis van dynamisch programmeren. Reeds bestaande gaten
in de alignatie worden niet aangepast, en nieuwe gaten krijgen de volledige gap penalty
toegerekend. Om een score toe te kennen aan een positie in een sequentie en een groep
gealigneerde sequenties, wordt een gewogen gemiddelde genomen op basis van de substitutiematrix en de scorematrix. Na deze stap is de alignatie van de sequenties compleet.
Clustal W biedt nog enkele verbeteringen bovenop de bovenvermelde implementatie.
Alle aanpassingen hebben betrekking op de laatste stap. De progressieve alignatie is namelijk verantwoordelijk voor de grootste fout in het uiteindelijke resultaat, aangezien er
slechts sequentie per sequentie wordt bijgevoegd, in tegenstelling tot een alignatie waarbij
rekening gehouden wordt met alle sequenties in één keer. Omdat Clustal W een groot aantal uitbreidingen biedt ten opzichte van het originele algoritme voor progressieve alignatie,
45
worden hier slechts de belangrijkste verbeteringen aangehaald.
• Weging van sequenties. De gewichten die worden toegekend aan de sequenties
worden rechtstreeks bekomen uit de boomstructuur. Gelijkaardige sequenties krijgen een verlaagde score omdat ze weinig extra informatie bevatten. Omgekeerd
krijgen afwijkende sequenties een hoog gewicht. Deze gewichten worden gebruikt als
gewichtscoëfficiënten bij het berekenen van de score van een alignatie in de laatste
stap. Hiermee wordt voorkomen dat sequenties die goed op elkaar lijken een te grote
invloed hebben op de alignatie, terwijl ze incrementeel minder meerwaarde hebben.
• Initiële gap penalties. Bij aanvang van het algoritme worden er twee gap penalties
berekend, die worden gebruikt bij het beginnen van een gat in de alignatie enerzijds
(GOP), en het verder uitbreiden ervan anderzijds (GEP). Deze waarden worden
aangepast bij elke alignatie, op basis van de volgende factoren.
1. Afhankelijk van de gebruikte substitutiematrix wordt een andere schalingsfactor
gebruikt voor de GOP.
2. Wanneer sequenties grote gelijkenis vertonen, wordt de kost van het openen
van een gat groter. Analoog wordt het openen van gaten bij sterk verschillende
sequenties gestimuleerd door de GOP te verlagen.
3. Omdat bij langere sequenties er meer aminozuren gealigneerd zullen zijn, wordt
de GOP geschaald met de logaritme van de lengte van de sequenties. Dit zal
ervoor zorgen dat de score van een alignatie minder afhankelijk is van de lengte
van de gebruikte sequenties.
4. Wanneer de lengte van de sequenties sterk verschillen, wordt de GEP verhoogd
om te voorkomen dat er lange gaten ontstaan in de alignatie van de kortste
sequentie.
• Positie-specifieke gap penalties. Als er zich reeds een gat bevindt op een positie
wordt zowel de GOP als de GEP verlaagd. Dit zorgt ervoor dat gaten makkelijker
worden uitgebreid. Wanneer er echter geen gat is op de beschouwde positie, maar
wel binnen een afstand van 8 aminozuren, dan wordt de gap opening penalty verhoogd. Dit voorkomt gaten die te dicht bij elkaar liggen. Verder kunnen de waarden
nog worden aangepast indien er een bepaalde opeenvolging van aminozuren wordt
waargenomen.
• Keuze van de substitutiematrix. Er bestaan twee types substitutiematrices,
namelijk de PAM [11] en de BLOSUM [17] series. Deze matrices hebben een verschillende prestatie wat de alignatie betreft afhankelijk van de gelijkenis van de
46
beschouwde sequenties. Doorheen het algoritme worden er vier verschillende versies
van deze matrices gebruikt, waarbij gekozen wordt op basis van de score die in de
boom wordt toegekend.
4.1.3
Aanverwant werk
Omdat Clustal W een veel gebruikt algoritme is, en de databanken met sequenties steeds
groter worden, is er al onderzoek gebeurd naar het versnellen van de originele implementatie. Een eerste studie uitgevoerd door SGI [27] onderzoekt de nodige aanpassingen om
Clustal W te versnellen op een multiprocessorsysteem met gedeeld geheugen. Er blijkt
ook een variatie te zitten in de uitvoeringstijd van de alignatie van dezelfde sequenties,
afhankelijk van de ordening van de sequenties in het inputbestand. Om de beperkingen
van een systeem met gedeeld geheugen te omzeilen, werd een aangepaste versie gemaakt
van Clustal W die draait op een cluster van PC’s die communiceren via een eenvoudig
netwerk [7]. Hierbij wordt gebruik gemaakt van de MPI communicatiebibliotheek [1].
Hoewel deze studies een goede basis vormen bij het zoeken naar parallellisme in het
origineel algoritme, zijn ze niet altijd even bruikbaar voor uitvoering op de Cell . Vaak
worden er bibliotheken gebruikt die nog niet beschikbaar zijn op de Cell , of wordt een
volledig andere architectuur verondersteld. De gebruikte parallellisatie houdt uiteraard
geen rekening met het beperkte geheugen van de SPU’s, waardoor de opdeling niet rechtstreeks bruikbaar is. Er is ook nog veel prestatiewinst mogelijk door te optimaliseren naar
de specifieke architectuur van de Cell , door bv. gebruik te maken van vectorinstructies,
lusontrolling toe te passen of de geheugenlatentie verbergen door gebruik te maken van
dubbele buffering.
4.2
4.2.1
Analyse
Theoretische analyse
Om een idee te krijgen van de meest tijdrovende functies bij de uitvoering van Clustal W,
wordt er in eerste instantie een theoretische analyse uitgevoerd. Hierbij wordt enkel uitgegaan van de broncode om een idee te vormen van de tijdscomplexiteit van de verschillende
fasen van het programma, en de invloed van de input op de uitvoeringstijden. Indien
mogelijk wordt deze analyse verduidelijkt aan de hand van pseudocode van de beschouwde functies. Omdat sommige delen omwille van de complexiteit niet altijd eenvoudig te
verduidelijken zijn, wordt voor een beter begrip verwezen naar de broncode van Clustal W. Tabel 4.1 geeft een overzicht van de tijdscomplexiteit van de verschillende deelfasen,
47
Tabel 4.1: Tijdscomplexiteit van het originele Clustal W-algoritme.
Fase
Tijdscomplexiteit
Paarsgewijze alignatie
O(N 2 L2 )
Boomstructuur opbouwen
O(N 4 )
Progressieve alignatie
O(N 3 + N L2 )
Totaal
O(N 4 + N L2 )
waarbij N het aantal sequenties en L de lengte van de sequenties voorstelt.
Paarsgewijze alignatie
Zoals reeds eerder vermeld, wordt in de eerste fase de sequenties paarsgewijs met elkaar vergeleken. Aangezien geldt dat alignatie(seq[i],seq[j])=alignatie(seq[j],seq[i])
moeten er in totaal N(N-1)/2 alignaties worden uitgevoerd. In pseudocode ziet de eerste
fase uit zoals in Programma 8:
Programma 8 Paarsgewijze alignatie in Clustal W.
f o r ( i =0; i <N; i ++){
f or ( j =0; j <i ; j ++){
s c o r e m a t r i x [ i ] [ j ]= p a i r w i s e a l i g n m e n t ( s e q [ i ] , s e q [ j ] ) ;
}
}
Het aligneren van de sequenties zelf is uiteraard afhankelijk van de lengte van de
sequenties, en dit volgens een kwadratisch verband. De uiteindelijke complexiteit bedraagt
O(N 2 L2 ). Hieruit blijkt dat het aantal en de lengte van de sequenties eenzelfde invloed
hebben op de tijdsduur van de paarsgewijze alignatie.
Boomstructuur opstellen
In deze fase neemt het zoeken van het kleinste matrixelement overeenstemmend met de
volgende tak in de boom, het meest tijd in beslag. Het opzoeken van dit element gebeurt
aan de hand van het (vereenvoudigd) algoritme in Programma 9.
De variabele SeqEnd is afhankelijk van het aantal sequenties, en geeft de grens aan
tussen de sequenties die wel en niet zijn opgenomen in de boom. Aangezien de grootte
48
Programma 9 Opstellen boomstructuur in Clustal W.
f o r ( nc =1; nc<=SeqEnd ; nc++){
f or ( j j =2; j j <=SeqEnd ; j j ++){
f or ( i i =1; i i < j j ; i i ++){
f or ( i =1; i <SeqEnd ; i ++){
sumi+=s c o r e m a t r i x [ i ] [ i i ] ;
sumj+=s c o r e m a t r i x [ i ] [ j j ] ;
}
t o t a l=r e s c a l e ( sumi , sumj ) ;
i f ( t o t a l <tmin ) {
tmin=t o t a l ;
mini= i i ;
minj= j j ;
}
}
}
}
van de scorematrix enkel afhankelijk is van het aantal sequenties, is deze fase dus niet
afhankelijk van de lengte van de sequenties. De uiteindelijke orde is O(N 4 ). Deze fase zal
dus snel in uitvoeringstijd stijgen als het aantal sequenties toeneemt, maar geen invloed
ondervinden van langere sequenties. Deze fase zal dan ook enkel belangrijk zijn wat de
uitvoeringstijd betreft, als de verhouding tussen het aantal sequenties en de lengte ervan
zeer groot is.
Progressieve alignatie
De laatste stap in het algoritme zal de sequenties incrementeel samennemen en aligneren. De analyse van deze deelstap wordt bemoeilijkt door een recursieve functieoproep in
het belangrijkste deel van de code. De schaling van de uitvoeringstijd in functie van de
parameters van de sequenties is bijgevolg intuı̈tief moeilijker te verklaren. Onderstaande
pseudocode bevat dan ook slechts een deel van het totale algoritme. Aangezien deze code
in een later stadium van de optimalisaties voor de Cell zal worden aangepast én ze het
grootste deel van de uitvoeringstijd voor de progressieve alignatie in beslag neemt, wordt
ze hier toch opgenomen.
49
Programma 10 Progressieve alignatie in Clustal W.
f o r ( i =1; i <Iend ; i ++){
f or ( j =1; j<=N; j ++){
hh=s+s c o r e ( i , j ) ;
}
}
Deze (sterk verkorte) code maakt deel uit van een recursieve functie. De diepte van
de recursie is afhankelijk van het aantal sequenties in de input. Dit deel van de code
schaalt dan ook kubisch met het aantal sequenties. Aangezien andere delen van de progressieve alignatie die hier niet zijn opgenomen, wel afhankelijk zijn van de lengte van
de sequenties, is de uiteindelijke orde van het algoritme complexer. Uiteindelijk wordt
bekomen dat de progressieve alignatie van de orde O(N 3 + N L2 ) is. De invloed van het
aantal sequenties is dus groter dan de lengte van de sequenties. Er is echter nog steeds een
kwadratische afhankelijkheid van de lengte, zodat deze geen te verwaarlozen effect hebben
op de uitvoeringstijd van deze fase.
De uiteindelijke tijdscomplexiteiten van de deelfasen zijn weergegeven in Tabel 4.1
en stemmen overeen met de bevindingen uit andere studies [6]. Om een beter beeld te
krijgen van de uiteindelijke verhouding van de uitvoeringstijden van de verschillende fasen,
is een meting van de uitvoeringstijd nodig over een breed aantal inputs met variërende
parameters.
4.2.2
Empirische analyse
Uit de theoretische analyse volgt een beeld van de structuur van het algoritme. Alhoewel
de tijdscomplexiteit van de fasen een aanduiding geven voor hun aandeel in de uitvoeringstijd, is er slechts een correct besluit te nemen nadat verschillende inputsets effectief zijn
uitgevoerd en hun uitvoeringstijden opgemeten. Indien de aangelegde inputs voldoende
van elkaar verschillen wat de lengte en het aantal sequenties betreft, zal dit een duidelijk
beeld geven van een breed gamma aan mogelijke situaties.
Het probleem bij deze analyse is het aantal beschikbare inputs. De twee inputsets die
worden meegeleverd bij de broncode van Clustal W hebben gelijkaardige eigenschappen.
Het aantal sequenties is relatief beperkt (50 voor input A en 66 voor input B) terwijl
de lengte van de sequenties sterk toeneemt (150 voor input A en tot 6000 voor input
B). Dit geeft dus een onvolledig beeld van het schalingsgedrag van de verschillende fasen.
50
Daarbij komt nog dat statistieken van databanken met echte sequenties [2] aantonen dat
de gemiddelde lengte van sequenties veel lager is dan bij de meegeleverde inputsets. De
gemiddelde lengte van de sequenties is 366 aminozuren, en er zijn heel weinig sequenties
met een lengte van meer dan 2000 aminozuren. Hoewel het dus mogelijk is dat er zeer
lange sequenties optreden, is het interessanter om inputsets te hebben met sequenties die
in lengte niet veel van elkaar verschillen en een gemiddelde lengte rond het realistische
gemiddelde van 366. Op die manier kan de schaling van de uitvoeringstijd beter in beeld
worden gebracht zonder rekening te moeten houden met enkele eventuele sterk afwijkende
sequenties.
Om een meer realistisch beeld te kunnen vormen van het tijdsgedrag van het algoritme,
zijn er dus meer inputs nodig met een grotere verscheidenheid. Om volledige controle te
hebben op de parameters van de input (lengte en aantal) is ervoor gekozen om de inputs
random te genereren op basis van de gevraagde parameters. Het uiteindelijke resultaat
van het algoritme, namelijk de gezamenlijke alignatie van de sequenties, heeft dan ook
geen zinnige informatie, maar dat is hier niet relevant aangezien enkel de uitvoeringstijd
in deze stap van belang is. De informatie die wordt bekomen uit het uitvoeren van de
zelf gegenereerde inputs heeft dan ook enkel betrekking op de verhouding van de uitvoeringstijden van de deelfasen van Clustal W. Ter controle worden inputs aangemaakt die
analoge eigenschappen vertonen met de meegeleverde inputs. Hieruit blijkt dat de uitvoeringstijden volledig gelijklopend zijn tussen random gegenereerde inputs en werkelijke
inputs.
Figuur 4.1 geeft de uitvoeringstijd weer van het volledige programma in functie van de
lengte van de sequenties (Figuur 4.1(a)) en het aantal sequenties (Figuur 4.1(b)). Wanneer
de lengte variëert zien we dat de uitvoeringstijd vergroot volgens een kubisch verband met
de lengte. Deze schaling komt voor bij alle inputs, en is dus niet afhankelijk van de absolute grootte van het aantal inputs. Een analoog beeld duikt op wanneer de uitvoeringstijd
in functie van het aantal sequenties wordt uitgezet. De uitvoeringstijd schaalt op eenzelfde
manier mee als bij variërende lengte van de sequenties. Opnieuw is deze schaling onafhankelijk van de lengte van de sequenties indien deze vast wordt gehouden. Dit komt overeen
met de bevinding dat de tijdscomplexiteit van het volledige algoritme O(N 4 + N L2 ) is.
Deze grafieken helpen echter niet in het bepalen van de deelfasen die verantwoordelijk zijn
voor het grootste deel van de uitvoeringstijd en geven ook geen inzicht in de verschuivingen
in het aandeel van de uitvoeringstijden wanneer de inputparameters veranderen.
Om een beeld te kunnen vormen van de uitvoeringstijd van de verschillende deelfasen,
wordt in Figuur 4.2 de uitvoeringstijden van de verschillende fasen uitgezet. In Figuur
4.2(a) wordt het aantal sequenties vast op 100 gehouden om de invloed van de lengte van
51
(a) Uitvoeringstijd ifv lengte
(b) Uitvoeringstijd ifv aantal sequenties
Figuur 4.1: Uitvoeringstijd van Clustal W bij variërende lengte (boven) en aantal sequenties
(onder).
de sequenties op de verschillende fasen te kunnen nagaan. De uitvoeringstijden van de
paarsgewijze en progressieve alignatie schalen zoals verwacht sterker dan lineair mee. In
52
(a) Uitvoeringstijd ifv lengte (N=100)
(b) Uitvoeringstijd ifv aantal sequenties (L=100)
Figuur 4.2: Uitvoeringstijd deelfasen bij vaste lengte en aantal sequenties.
absolute waarde is de uitvoeringstijd van de paarsgewijze alignatie in het algemeen het
grootst. Deze bevindingen stemmen overeen met de resultaten van de theoretische analyse.
De uitvoeringstijd voor het opbouwen van de boomstructuur blijft steeds gelijk, omdat
deze in uitvoeringstijd niet afhankelijk is van de lengte van de sequenties.
Wanneer de lengte van de sequenties constant wordt gehouden op 100, wordt de grafiek uit Figuur 4.2(b) bekomen. Een eerste observatie is dat de paarsgewijze alignatie met
53
afstand het meeste tijd in beslag neemt, en op ongeveer dezelfde manier meeschaalt als
wanneer het aantal sequenties werd vastgehouden. Het schalingsgedrag van de progressieve alignatie is moeilijker te verifiëren omdat de tijdscomplexiteit twee termen bevat die
afhankelijk zijn van het aantal sequenties. In ieder geval kan worden besloten dat er sneller
dan lineair wordt meegeschaald met het aantal sequenties. Het opbouwen van de boom
stijgt duidelijk snel in uitvoeringstijd wanneer het aantal sequenties groter wordt. Dit ligt
in de lijn van de verwachting, omdat de tijdscomplexiteit van deze fase O(N 4 ) is.
Een eerste conclusie die we hier kunnen trekken in verband met de meest tijdrovende
functie is dat de paarsgewijze alignatie het meest tijd in beslag lijkt te nemen. Om een
beter gefundeerde beslissing te kunnen nemen moeten er echter metingen worden genomen
over een breder gamma aan inputs, met extremere waarden voor de inputparameters om
randgevallen beter te kunnen onderscheiden.
Figuur 4.3 illustreert de procentuele uitvoeringstijd van de verschillende fasen. Per
deelgrafiek wordt de uitvoeringstijd in functie van de lengte van de sequenties uitgezet.
De deelgrafieken verschillen van elkaar in het aantal sequenties van de verwerkte input.
Deze grafieken leveren heel wat interessante informatie op.
• Wanneer het aantal sequenties een redelijke waarde aanneemt, is de paarsgewijze
alignatie met voorsprong de fase die het meest tijd in beslag neemt. Behoudens
enkele uitzonderingsgevallen is het aandeel van deze fase groter dan 50% bij 50
sequenties of meer. Een stijgende lengte van de sequenties vergroot het aandeel nog
meer.
• Wanneer het aantal sequenties relatief beperkt blijft, is de progressieve alignatie verantwoordelijk voor bijna de volledige overige uitvoeringstijd die niet door de paarsgewijze alignatie in beslag wordt genomen. Een stijgende lengte blijkt hier bijna
geen invloed op te hebben. Wanneer het aantal sequenties echter zeer groot wordt
(500 en meer), daalt dit aandeel sterk.
• Het opbouwen van de boomstructuur neemt slechts in uitzonderlijke gevallen relatief
gezien veel tijd in beslag. Wanneer het aantal sequenties beperkt blijft, is het aandeel
van deze fase slechts marginaal. Enkel bij een groot aantal sequenties (500 en meer)
en zeer korte sequenties (tot 10 aminozuren lang) stijgt de uitvoeringstijd boven de
10% van het totaal. Hoewel deze situaties over het geheel gezien weinig voorkomen,
is de uitvoeringstijd veel groter dan de uitvoeringstijd van de paarsgewijze alignatie
en de progressieve alignatie.
De gegevens van de metingen maken het mogelijk om een goed onderbouwde keuze te
maken van de fasen die eerst moeten worden geoptimaliseerd om een zo groot mogelijke
54
(a) Aantal sequenties = 10
(b) Aantal sequenties = 50
(c) Aantal sequenties = 100
(d) Aantal sequenties = 500
(e) Aantal sequenties = 1000
Figuur 4.3: Procentueel aandeel van de verschillende deelfasen op de uitvoeringstijd.
versnelling te bekomen. Dit blijkt duidelijk de paarsgewijze alignatie te zijn, die in het
merendeel van de situaties meer dan 50% van de totale uitvoeringstijd bedraagt. Aangezien bij de meegeleverde inputs het aantal sequenties vrij beperkt is, is de progressieve
alignatie zeker niet te verwaarlozen. De tweede fase, waarin de boom wordt opgebouwd,
is echter enkel in de randgevallen waar de verhouding tussen het aantal en de lengte van
de sequenties zeer groot is belangrijk. Er wordt dan ook verder geen inspanning geleverd
om deze fase te versnellen. Voor mogelijke manieren om deze fase te parallelliseren, wordt
verwezen naar aanverwant werk [6].
55
4.3
Initiële aanpassingen
Clustal W is geschreven in C, waardoor het na hercompilatie zonder aanpassingen uitvoerbaar zou moeten zijn op de Cell . Dit is echter niet het geval, enkele kleine aanpassingen
zijn noodzakelijk.
• De math-bibliotheek van de PPU ondersteunt standaard geen bewerkingen op floatingpoint getallen met dubbele precisie [21]. Om te voorkomen dat overal in de originele
code variabelen van dubbele naar enkele precisie moeten worden veranderd, wordt
er een wrapper geschreven. Deze zal oproepen naar functies uit de math-bibliotheek
met argumenten van dubbele precisie omzetten naar functieoproepen met argumenten van enkele precisie. Deze functieoproepen worden wel ondersteund op de PPU.
Hierbij gaat uiteraard wel een deel precisie verloren, zodat na uitvoering van het
programma er moet worden op gelet dat het resultaat nog steeds correct is.
• Hoewel in de documentatie staat dat de math-bibliotheek alle functies uit de ANSI
C99 standaard bevat, blijkt dit niet het geval te zijn. Zo is de functie lgamma niet
geı̈mplementeerd, terwijl deze wordt opgeroepen in Clustal W. Deze functie berekent
de natuurlijke logaritme van de absolute waarde van gamma(x), waarbij de gammafunctie als volgt is gedefiniëerd.
Z
Γ(x) =
∞
t(x−1) e−t dt
(4.1)
0
Deze functie kan echter geı̈mplementeerd worden aan de hand van een look-up table
en oproepen naar log en exp [30].
Na deze relatief eenvoudige aanpassingen kan de code gecompileerd worden voor uitvoering op de PPU. De output stemt overeen met de referentie-output van Clustal W. Het
verlies aan precisie door gebruik te maken van enkele precisie en de alternatieve implementatie van lgamma heeft dus geen negatieve invloed op de resultaten van de alignatie.
Vanuit deze aangepaste code wordt verder gewerkt naar een geoptimaliseerde versie die
gebruik maakt van de SPU’s, rekening houdend met de opgedane kennis van de empirische
analyse en de ervaring opgedaan bij het optimaliseren van matrixvermenigvuldiging.
56
4.4
4.4.1
Paarsgewijze alignatie
Uitvoering op de SPU
Om een programma ten volle te laten profiteren van de Cell-architectuur, moeten de SPU’s
zoveel mogelijk aan het werk worden gezet. Een eerste stap hierbij is de keuze van de code
die door de SPU’s zal worden uitgevoerd. Bij het aanpassen van het algoritme voor
matrixvermenigvuldiging was de keuze eenvoudig omdat de code zeer beperkt in grootte
is. Voor een realistisch programma zoals Clustal W is de keuze minder triviaal, omdat
de code vaak te groot is om in één keer op een SPU uit te voeren. Een eerste poging om
Clustal W in zijn totaliteit op een SPU uit te voeren bleek dan ook niet te slagen.
Uit het opmeten van de uitvoeringstijden blijkt dat de eerste fase, de paarsgewijze
alignatie, ruimschoots de meerderheid van de tijd in beslag neemt. De meeste aandacht
bij het overbrengen van code van de PPU naar de SPU gaat dan ook uit naar deze fase van
het algoritme. Hierbij wordt het programmeermodel beschreven in 3.2 opnieuw gehanteerd
om de aanpassingen in incrementele en relatief eenvoudige stappen te laten verlopen. Bij de
keuze van de code die in een aparte SPU-draad zal worden uitgevoerd moet een evenwicht
gezocht worden tussen code die voldoende berekeningen bevat om de creatie van een nieuwe
draad te rechtvaardigen, maar toch klein genoeg is om zonder problemen in het beperkt
lokaal geheugen te passen.
Een eerste logische keuze was om de alignatie van de sequenties in een nieuwe draad uit
te voeren. De alignatie zelf is de enige vorm van berekeningen die wordt uitgevoerd tijdens
de eerste fase van Clustal W. De nodige communicatie van en naar de SPU is zeer beperkt
omdat eens alle nodige data aanwezig is in het lokaal geheugen van de SPU, de berekeningen volledig kunnen worden uitgevoerd. Het resultaat dat moet worden teruggeschreven
naar het hoofdgeheugen is de optimale alignatie en de bijhorende score. Het probleem
is echter dat het aantal uit te voeren alignaties N(N-1)/2 bedraagt, en dus kwadratisch
afhankelijk is van het aantal sequenties. Een enkele alignatie neemt dus relatief weinig tijd
in beslag, waardoor de overhead van het creëeren van een draad voor elke nieuwe alignatie
te groot is. In de plaats daarvan wordt er bij het begin van de paarsgewijze alignatie één
SPU-draad aangemaakt. Door communicatie met de PPU weet de SPU welke sequenties
met elkaar moeten worden gealigneerd, en wordt het resultaat van deze alignatie teruggegeven. Wanneer alle sequenties paarsgewijs met elkaar zijn gealigneerd, geeft de PPU
het signaal aan de SPU om de draad te termineren. Het controlealgoritme in de PPU is
hieronder weergegeven.
Merk op dat het in principe mogelijk is om zonder enige vorm van communicatie de
SPU-draad de sequenties te laten aligneren. Op het einde van de eerste fase is immers enkel
57
een matrix nodig waarin de scores van de paarsgewijze alignaties worden weergegeven.
Deze aanpak is vermeden omdat het moeilijker is om op deze manier uitbreiding te doen
naar de controle van de SPU-draden. Indien immers de PPU beslist wat de SPU uitvoert,
is het ook eenvoudiger om de werklast te verdelen met het oog op parallellisatie.
Programma 11 Controle-algoritme voor paarsgewijze alignatie met één SPU-draad
s p e i d=s p e c r e a t e t h r e a d (& p a i r w i s e ,& c o n t e x t ) ;
f o r ( i =0; i <N; i ++){
f o r ( j =0; j <i ; j ++){
mailbox write ( spe id , i ) ;
mailbox write ( spe id , j ) ;
mailbox read ( ) ;
}
}
//SPU−draad t e r m i n e e r t b i j l e z e n i n d e x −1
mailbox write ( spe id , −1);
De functie spe_create_thread maakt een nieuwe draad, en krijgt als argumenten een
verwijzing naar de uit te voeren code (hier de code voor de paarsgewijze alignatie) en
naar een contextobject dat de nodige data voor de uitvoering bevat. Als terugkeerwaarde
wordt een identificatienummer gegeven waarmee communicatie met de SPU waarop de
draad wordt uitgevoerd mogelijk is. Bij het uitvoeren van de code op de SPU wordt
gewacht op een signaal van de PPU. Dit zal aangeven welke sequenties met elkaar moeten
worden vergeleken. De communicatie gebeurt via het mailboxsysteem. De PPU schrijft de
indices van de sequenties naar de mailbox van de SPU met mailbox_write. Daarna wordt
gewacht tot de SPU een bericht terugschrijft in de mailbox van de PPU, wat aangeeft dat
de alignatie klaar is. Dit bericht wordt uitgelezen met mailbox_read, een blokkerende
functie. De waarde van het bericht speelt hier geen rol. Wanneer alle sequenties met
elkaar zijn vergeleken, schrijft de PPU een negatieve waarde in de mailbox van de SPU.
Dit is het signaal voor de SPU om de draad af te sluiten.
Om de alignatie op de SPU mogelijk te maken, moeten alle daartoe benodigde variabelen en datastructuren in het lokaal geheugen van de SPU worden geplaatst. Om
te voorkomen dat er overbodige data wordt meegegeven, is een liveness analyse van de
beschouwde code nodig. Dit wordt aanzienlijk bemoeilijkt door het overvloedig gebruik
van globaal gedefinieerde variabelen in de code. De declaratie en initialisatie van de no-
58
dige data gebeurt met andere woorden verspreid over een aantal codebestanden. Na het
nodige speurwerk kan een contextstructuur worden opgebouwd die de nodige variabelen
of referenties naar de datastructuren bevat. Het adres van deze contextstructuur wordt
meegegeven bij de creatie van de SPU-draad.
Net als bij de matrixvermenigvuldiging, moet ook hier erop gelet worden dat geheugenadressen zijn afgerond op een veelvoud van 128. Hiertoe worden terug de aangepaste
functies malloc_align en free_align gebruikt. De allocatie van het geheugen is eveneens verspreid over een ruim aantal bestanden, waardoor grote zorg moet worden besteed
dat alle datastructuren die nodig zijn voor de paarsgewijze alignatie correct worden gealigneerd en vrijgegeven. Pas na deze stap kan de paarsgewijze alignatie zonder problemen
worden uitgevoerd op de SPU.
De uitvoeringstijd zou naar verwachting iets hoger moeten liggen wanneer dezelfde
code zonder aanpassingen aan het algoritme zelf wordt verplaatst van de PPU naar de
SPU. In het geval van de paarsgewijze alignatie blijkt de uitvoering echter een stuk trager
te zijn, tot een factor 100 toe. Dit valt uiteraard niet alleen te verklaren door de overhead
van het creëren van de SPU-draad, het DMA-en van de data of de communicatie met de
PPU. Deze elementen waren immers ook aanwezig bij de uitvoering van matrixvermenigvuldiging, en daar was de uitvoeringstijd op de PPU en de SPU van dezelfde grootte-orde.
Bovendien is het vinden van de oorzaak van de vertraging moeilijk aangezien er weinig
tools voorhanden zijn die profilering of uitgebreide analyse van de uitvoering op de SPU
mogelijk maken. Na heel wat zoekwerk met behulp van de tools FDPR-pro en Visual
Performance Analyzer, die geen profilering uitvoeren maar het aantal functieoproepen
meten en voorstellen, werd opgemerkt dat functie fflush zeer veel werd opgeroepen. Dit
is uiteraard geen referentie wat de uitvoeringstijd betreft, maar indien het probleem bij
deze functie zit kan het wel de grote vertraging verklaren. Uiteindelijk blijkt dat na uitcommentariëren van alle oproepen naar deze functie de uitvoeringstijd terug in de lijn
van de verwachtingen ligt. De oorzaak ligt in het feit dat alle systeemoproepen vanaf de
SPU gebeuren door de oproep door te sturen naar de PPU, aangezien daar het besturingssysteem wordt op uitgevoerd. De fflush-functie zorgt er voor dat alle data die naar de
standaardoutput moet wordt geschreven en zich nog in de buffers bevinden, verplicht worden weggeschreven (in dit geval naar het scherm). Omdat hiervoor het besturingssysteem
moet worden aangesproken, is er een call naar de PPU noodzakelijk. Wanneer de functie
vanop de PPU zelf wordt opgeroepen zal dit geen bijkomende problemen veroorzaken,
maar op de SPU moeten alle oproepen naar het besturingssysteem worden doorgegeven
naar de PPU. Deze zal wachten tot de PPU de aanvraag heeft verwerkt. Aangezien dit
aanzienlijk hogere wachttijden zal opleveren, zal een groot aantal oproepen in totaal leiden
59
tot een enorme vertraging. Wanneer functies zoals fflush en printf worden vermeden,
wat het geval is in de matrixvermenigvuldiging, is de uitvoering op de PPU en de SPU
ongeveer gelijklopend. De resultaten voor verschillende inputsets zijn te zien in Figuur 4.4,
waarbij de uitvoeringstijd van de fasen zijn genormaliseerd op de uitvoeringstijd van die
fase bij uitvoering op de PPU. Hierop is terug te zien dat bij overgang van de PPU naar
de SPU zonder aanpassingen van de code, de uitvoeringstijd iets toeneemt. De andere
fasen draaien op de PPU, waardoor hun uitvoeringstijd niet verandert.
(a) Inputset A
(b) Inputset B
Figuur 4.4: Verhouding van de uitvoeringstijden bij enkeldradige uitvoering.
4.4.2
Parallellisatie
Om uiteindelijk toch een versnelling van de uitvoeringstijd te verkrijgen, moet de code
van alle SPU’s gebruik maken. Voor de paarsgewijze alignatie is dit eenvoudig omdat het
berekenen van de elementen in de scorematrix door twee sequenties te aligneren, onafhankelijk is van elkaar. Het komt er dus enkel op neer om het berekenen van de scorematrix
te verdelen over de SPU’s.
Er zijn in het extreme geval twee mogelijkheden om de werklast over de SPU’s te
verdelen. In het eerste geval wordt vóór het starten van de SPU-draden berekend welke
elementen door welke SPU zullen worden berekend. Het toewijzen van de werklast gebeurt
dus volledig statisch. Het voordeel van deze techniek is dat de communicatie met de PPU
tot een minimum wordt herleid, waardoor er geen vertraging ten gevolge van communicatie
en synchronisatie zullen optreden. Het grote nadeel is echter dat er op voorhand niet met
zekerheid kan worden bepaald of de werklast evenredig is verdeeld. De scorematrix is
immers symmetrisch, waardoor enkel de elementen van de bovendriehoeksmatrix moeten
worden berekend. Dit maakt het gelijk verdelen van de werklast aanzienlijk moeilijker,
60
omdat een driehoeksstructuur minder eenvoudig is op te delen in gelijke delen dan een
volledige matrix. Zeker wanneer het aantal sequenties beperkt is, zal door benaderingen
in het berekenen van de stop- en startindices een significante fout optreden, met een
onevenwichtige verdeling tot gevolg.
Het andere uiterste is om de toewijzing van een te berekenen element aan een SPU volledig dynamisch te laten gebeuren. Hierbij zal de PPU op zoek gaan naar een beschikbare
SPU om een nieuw element te berekenen. Wanneer een SPU klaar is met uitvoeren, wordt
een bericht teruggestuurd naar de PPU om aan te geven dat de SPU terug beschikbaar
is en nieuwe elementen kan uitvoeren. Deze techniek garandeert een zo goed mogelijke
werkverdeling, en een minimale wachttijd voor de SPU’s. Er zijn echter in totaal N(N-1)
communicatieboodschappen nodig, twee per te berekenen matrixelement, waardoor er zeer
veel overhead door communicatie optreedt. Dit kan eventueel nog worden beperkt door
groepen van elementen toe te wijzen, wat de kans op een onevenwichtige verdeling echter
groter maakt. Enkel indien het communicatienetwerk voldoende snel is, zal deze techniek
geen onaanvaardbaar prestatieverlies met zich meebrengen.
Uiteindelijk wordt voor een tussenoplossing gekozen. Een eerste groot deel van de
matrix wordt statisch toegewezen over de SPU’s. In plaats van een enkel element per
keer toe te wijzen, wordt een volledige rij van de scorematrix aan een SPU toegewezen.
Dit vereenvoudigt de berekeningen die de start- en stopindices per SPU bepalen, maar
hebben ook een grotere afwijking tot gevolg. Daarom wordt een laatste deel van de
matrix volledig dynamisch toegewezen. Wanneer een SPU klaar is met zijn eerste statisch
toegewezen deel te berekenen, wijst de PPU de volgende te berekenen rij toe aan die SPU.
Omdat het aantal te berekenen elementen per rij lineair afneemt, zal dit uiteindelijk tot
een evenwichtige werkverdeling leiden, met een beperkt aantal communicatieberichten.
De gekozen opdeling van het statisch en dynamisch te berekenen deel is geı̈llustreerd
in Figuur 4.5. Het statische deel moet nu worden opgesplitst in een aantal delen die
evenveel elementen bevat. Die grenzen worden steeds gelegd op het einde van een rij,
zodat eenzelfde rij steeds door dezelfde SPU wordt berekend. We kiezen ervoor om 75%
statisch toe te wijzen, en de overige 25% dynamisch. Dit zal ervoor zorgen dat de initiële
werklast voldoende groot is, maar er toch genoeg elementen zijn die dynamisch worden
toegewezen om onevenwichtigheden uit te vlakken. Het bepalen van de grenzen gebeurt
volledig analytisch in de PPU.
Bij de berekeningen van de grenzen worden de volgende variabelen gebruikt.
61
Figuur 4.5: Keuze statisch en dynamisch verdeelde werklast.
n:
Totaal aantal rijen in de scorematrix
ji :
Startindex voor SPU i
ki :
Stopindex voor SPU i
T :
Beschikbare SPU’s
De grootste index hoort bij de rij met het meeste elementen die moeten berekend
worden, met andere woorden rij n bevat n − 1 te berekenen elementen. Algemeen geldt
dus dat rij j in totaal j − 1 relevante elementen bevat. Het totaal aantal elementen vervat
tussen twee willekeurige rijen j en k met j > k is dan:
Sj→k =
j−1
X
i−
0
=
k−1
X
i
0
j(j − 1) − k(k − 1)
2
Om ervoor te zorgen dat het statische deel van de werklast 75% van de totale werklast
bedraagt, moet een index n0 worden gevonden waarvoor geldt dat
eenvoudig in te zien dat
n0
=
n
2.
62
Sn→n0
Sn→0
= 3/4. Het is
Sn→ n2
=
=
Sn→0 =
⇒ lim
n→∞
Sn→ n2
Sn→0
=
=
n(n − 1) − n2 ( n2 − 1)
2
n 3n − 2
(
)
2
4
n(n − 1)
2
lim
n→∞
3n−2
4
n−1
3
4
Uit de eis dat de volledige werklast evenredig verdeeld moet zijn over T SPU’s, krijgen
we volgende formule voor het bepalen van de start- en stopindices van de SPU.
⇔
Sji →ki
Sn→ n2
=
1
T
ji (ji − 1) − ki (ki − 1)
4
2
n(3n − 2)
=
1
T
Dit geeft echter slechts één vergelijking voor twee onbekenden ji en ki . De verschillende
onderverdelingen van de werklast moeten echter op elkaar aansluiten, zodat er nog andere
vergelijkingen kunnen worden opgesteld die het mogelijk maken om de start- en stopindex
voor elke SPU te berekenen.
j0 = n
ji = ki−1
n
kT =
2
Deze vergelijkingen maken het mogelijk om de indices iteratief te berekenen. Het
controle-algoritme voor de PPU is weergegeven in Programma 12 op pagina 64.
63
Programma 12 Controle-algoritme voor paarsgewijze alignatie met werklastverdeling
over meerdere SPU-draden
f o r ( i =0; i <T ; i ++){
s p e i d [ i ]= s p e c r e a t e t h r e a d (& p a i r w i s e ,& c o n t e x t [ i ] ) ;
}
// S t a t i s c h e w e r k v e r d e l i n g
j=n ;
f o r ( i =0; i <T ; i ++){
k=c a l c u l a t e s t o p i n d e x ( n , j ,T ) ;
mailbox write ( spe id [ i ] , j ) ;
mailbox write ( spe id [ i ] , k ) ;
j=k ;
}
// V e r d e e l de o v e r i g e w e r k l a s t dynamisch
r e m a i n i n g s e q u e n c e s=k ;
while ( r e m a i n i n g s e q s >0){
f o r ( i =0; i <T ; i ++){
data = m a i l b o x r e a d ( s p e i d [ i ] ) ;
i f ( data !=0xFFFFFFFF) {
mailbox write ( spe id [ i ] , remaining seqs ) ;
m a i l b o x w r i t e ( s p e i d [ i ] , r e m a i n i n g s e q s −1);
r e m a i n i n g s e q s −−;
}
}
}
f o r ( i =0; i <T ; i ++){
mailbox write ( spe id [ i ] , −1);
}
64
Door gebruik te maken van dit communicatieschema wordt de werklast efficiënt verdeeld over het aantal beschikbare SPU’s. Uit de grafiek in Figuur 4.6 is duidelijk op te
maken dat de versnelling van de paarsgewijze alignatie meeschaalt met het aantal gebruikte SPU’s. De versnelling van de paarsgewijze alignatie bij gebruik van 8 SPU’s ten opzichte
van 1 SPU is 6.93 voor input A en 7.7 voor input B. Theoretisch is de maximumversnelling bij perfecte versnelling 8, waaruit blijkt dat de uitvoeringstijd zeer goed meeschaalt
met het aantal gebruikte SPU’s. De werklast wordt dus evenwichtig verdeeld zonder een
grote vertraging te introduceren door de gebruikte communicatie met de PPU. De totale
versnelling is 2.18 voor input A en 1.54 voor input B wanneer de paarsgewijze alignatie
op de SPU’s in plaats van op de PPU wordt uitgevoerd. Om een grotere versnelling van
het totale programma te bekomen, moet de progressieve alignatie worden versneld. Bij de
geparallelliseerd versie van het programma is deze immers verantwoordelijk voor 87% van
de totale uitvoeringstijd voor input B.
(a) Inputset A
(b) Inputset B
Figuur 4.6: Genormaliseerde verhouding van de uitvoeringstijden bij meerdradige uitvoering.
Om ervoor te zorgen dat de code op de SPU in staat is om grote inputsets te verwerken, moet er terug aandacht besteed worden aan het geheugengebruik. Het huidige
algoritme kopiëert immers nog steeds alle data die live-in is bij het begin van de paarsgewijze alignatie. Dit heeft tot gevolg dat een kopie van alle sequenties zich in het lokaal
geheugen van elke SPU bevindt. Eveneens wordt er voldoende geheugen gereserveerd voor
alle te berekenen elementen, die aan het einde van het algoritme worden teruggeschreven
naar het hoofdgeheugen. Dit heeft tot gevolg dat het geheugengebruik sterk toeneemt,
niet alleen bij een stijgend aantal sequenties, maar ook als de sequenties langer worden.
Daarom wordt eenzelfde techniek als bij de matrixvermenigvuldiging gehanteerd, waarbij
data slechts aanwezig is in het lokaal geheugen zolang ze ook effectief nodig is voor de
uitvoering. Wanneer data niet of gedurende een lange tijd niet nodig is, wordt het gebruikte geheugen terug vrijgegeven ten voordele van nieuwere data. Daarenboven worden
65
de berekende elementen gegroepeerd per rij en, van zodra een volledige rij is berekend,
wordt deze teruggeschreven naar het hoofdgeheugen. De verkorte versie van het algoritme
op de SPU is beschreven in Programma 131 .
Programma 13 Algoritme voor paarsgewijze alignatie op de SPU met beperkt geheugengebruik
f o r ( i=I s t a r t ; i <Iend ; i ++){
s e q [ i ]= m a l l o c ( s e q l e n g t h [ i ] ) ;
DMA in ( s e q [ i ] ) ;
mat [ i ]= m a l l o c ( i ) ;
f o r ( j =0; j <i ; j ++){
s e q [ j ]= m a l l o c ( s e q l e n g t h [ j ] ) ;
DMA in ( s e q [ j ] ) ;
mat [ i ] [ j ]= p a i r w i s e a l i g n ( s e q [ i ] , s e q [ j ] ) ;
f r e e ( seq [ j ] ) ;
}
DMA out ( mat [ i ] ) ;
f r e e ( mat [ i ] ) ;
f r e e ( seq [ i ] )
}
1
In het beschouwde algoritme is enkel de code relevant voor het beperken van het geheugengebruik
opgenomen. Code voor onder andere de communicatie is voor de eenvoud weggelaten.
66
Door de grote datastructuren komt een andere beperking van DMA-transfers aan het
licht: de maximumgrootte van een enkele DMA-transfer is 16 KiB. Zeker voor de standaardinputsets waar de sequenties al snel enkele honderden aminozuren lang zijn, wordt
deze grootte regelmatig overschreden. De oplossing is eenvoudigweg om de DMA-transfers
op te delen in verschillende blokken tot 16 KiB, zodat grote datastructuren met meerdere
DMA-transfers in het lokaal geheugen worden gekopiëerd. Met deze aanpassingen is de
aangepaste versie van Clustal W met geparallelliseerde uitvoering van de paarsgewijze
alignatie in staat om een groot aantal inputsets correct te verwerken.
4.5
Progressieve alignatie
Om een verdere versnelling van Clustal W te bekomen moet de laatste fase, de progressieve alignatie, worden versneld. Na versnelling van de paarsgewijze alignatie stijgt het
procentuele aandeel op de uitvoeringstijd van deze fase namelijk tot 62% voor inputset
A en 87% voor inputset B. Paarsgewijze alignatie is nog slechts verantwoordelijk voor
36% en 12% respectievelijk, waardoor het verder optimaliseren van deze fase slechts een
marginaal effect zal hebben.
Het vinden van parallellisme bij de progressieve alignatie is heel wat moeilijker, omdat
het aantal afhankelijkheden tussen de recursief opgeroepen functies groot is. Dit staat
in schril contrast met het parallelliseren van de paarsgewijze alignatie, die bijna triviaal
is door het ontbreken van afhankelijkheden. Na een grondige studie van de broncode en
relevante literatuur blijkt er toch mogelijkheid tot parallellisatie te zijn. Na profilering
van de originele code van Clustal W wordt opgemerkt dat de pdiff-functie 80% van de
uitvoeringstijd van de progressieve alignatie beslaat. Deze recursieve functie bestaat in
hoofdzaak uit twee lussen die de sequenties in een voorwaartse en achterwaartste fase
overlopen om zo een optimale alignatie te bekomen. Deze twee lussen zijn onafhankelijk
van elkaar, en kunnen dus in parallel op de SPU’s worden uitgevoerd. Theoretisch gezien
is er met andere woorden een versnelling met een factor twee mogelijk voor de hete code
van de progressieve alignatie.
In een eerste poging worden twee nieuwe SPU-draden aangemaakt voor elke oproep
van pdiff waarin de twee lussen in parallel worden uitgevoerd. Dit leidt echter tot een
grote vertraging omdat, onder andere wegens het recursieve karakter, deze functie zeer
vaak wordt opgeroepen. De tijd die gewonnen wordt door de lussen in parallel uit te
voeren wordt dus meer dan teniet gedaan door de tijd die nodig is om nieuwe draden
aan te maken en de nodige data via DMA-transfers door te sturen. Een intelligentere
aanpak dringt zich op, waarbij SPU-draden op een statische manier in het begin van de
67
progressieve alignatie worden gecreëerd. Een oproep van pdiff kan op die manier worden
vervangen door een bericht naar de SPU’s te sturen die, na het transfereren van de nodige
data, de nodige berekeningen in parallel uitvoeren. Om te voorkomen dat er redundante
data wordt getransfereerd, gebeurt de controle in de SPU-draden door een eenvoudige
toestandsmachine. Aan de hand van het signaal dat door de PPU wordt verstuurd, kan
de SPU bepalen of het om een recursieve oproep gaat of niet. Bij recursieve oproepen
veranderen namelijk enkel de argumenten van de functie, en blijft alle andere data dezelfde.
Op deze manier wordt zowel de kost van het aanmaken van nieuwe draden als het aantal
DMA-transfers beperkt.
Het nadeel van het gebruik van statisch aangemaakte draden is dat er veel extra communicatie met de PPU nodig is. Dit zou kunnen voorkomen worden door een groter deel
van de programmacode op de SPU’s uit te voeren, maar dit maakt het mogelijke parallelisme ongedaan en is niet mogelijk vanwege het beperkte lokaal geheugen. Uiteindelijk blijkt
de uitvoeringstijd van de progressieve alignatie na het gebruik van statisch aangemaakt
draden een aantal grote-ordes lager te liggen dan bij het gebruik van dynamisch aangemaakte draden, maar nog steeds hoger dan bij het origineel: 174 ms tegenover 140 ms
origineel voor inputset A. Na het uitvoeren van enkele optimalisaties kan de uitvoeringstijd nog gereduceerd worden tot 150 ms, maar dit levert nog steeds geen versnelling op.
Uit het opmeten van de tijd besteed aan de eigenlijke berekeningen en de DMA-transfers,
blijkt dat de SPU-draden slechts een fractie van de tijd nuttige berekeningen doen. De
vertraging is dus te wijten aan de overvloedige communicatie en het aanmaken van de
contextstructuren die nodig zijn voor de DMA-transfers van de SPU’s. Desondanks de
parallelisatie en de optimalisatie van de progressieve alignatie, kan er op deze manier dus
geen versnelling worden bekomen.
Het aanpassen van de laatste fase van Clustal W toont aan dat niet alle code even
eenvoudig valt te versnellen bij uitvoering op de Cell. Indien de code niet parallelliseerbaar
of vectoriseerbaar is, is het moeilijk om een versnelling te bekomen. Als er bijkomend veel
communicatie nodig is of veel draden worden aangemaakt, is het gebruik van de SPU’s
zelf contra-productief.
4.6
Mogelijke uitbreidingen
Bij de aanpassingen van Clustal W in dit werk is voornamelijk gezocht naar parallellisme
om versnelling te bekomen. Er blijven nog veel mogelijkheden over om de uitvoering op
de Cell nog verder te optimaliseren. Er wordt dan hoofdzakelijk gedacht aan het gebruik
van vectorinstructies, die niet alleen de berekeningen kunnen versnellen maar ook het
68
controleverloop kan vereenvoudigen. Wegens het beperkte karakter van deze scriptie is
deze potentie niet benut.
Meer aandacht kan ook nog worden besteed aan de progressieve alignatie, om zo alsnog
een versnelling voor deze fase te bekomen. Niet alleen vectorinstructies kunnen deze fase
versnellen, ook een ander communicatieschema kan de prestatie vergroten. Verder onderzoek naar meer parallellisme in deze fase kan er ook voor zorgen dat alle SPU’s worden
benut, eventueel door resultaten op voorhand in parallel door de SPU’s te laten berekenen
en op te slaan in het hoofdgeheugen, waar ze later terug kunnen worden opgevraagd.
69
Hoofdstuk 5
Besluit
Om de beperkingen inzake frequentieschaling en vermogenverbruik te doorbreken, bevatten moderne processoren steeds vaker meerdere rekenkernen. Een ver doorgedreven
implementatie van deze recente trend is de Cell BE architectuur.
In eerste instantie werd deze architectuur nader onderzocht. Het betreft een heterogene multiprocessor met een innovatieve geheugenhiërarchie. De processor bestaat uit
negen rekenkernen: één PPU die het sterkst lijkt op traditionele microprocessoren, en
acht vectorprocessoren, de SPU’s. Deze processorelementen beschikken over hun eigen
beperkt lokaal geheugen. Data wordt tussen alle processorelementen getransfereerd via
DMA-instructies. Het geheel maakt een hoge piekprestatie mogelijk, maar is een grote
uitdaging op het gebied van efficiënt programmeren.
Om de architectuur en haar moeilijkheden toe te lichten, werd een eenvoudig voorbeeld
uitgewerkt, namelijk matrixvermenigvuldiging. Dit probleem wordt systematisch aangepast om optimaal gebruik te maken van de mogelijkheden van de Cell. Van een initiële
implementatie die enkel gebruik maakt van de PPU, werd via incrementele verbeteringen
een implementatie uitgewerkt die geparallelliseerd werd over alle acht de SPU’s. Om de
beperkingen van de geheugenstructuur te omzeilen, werd extra aandacht besteed aan het
geheugengebruik en buffering van de DMA-transfers. De berekeningen zelf werden versneld door lusontvouwing. De versnelling tegenover de originele uitvoering op de PPU
bedraagt 19 voor een dimensie van 1024.
Aangezien matrixvermenigvuldiging een zeer eenvoudig probleem is, werd een complexer algoritme geoptimaliseerd, namelijk Clustal W. Clustal W is een programma voor
de alignatie van meerdere DNA-sequenties, opgebouwd uit drie grote fasen: paarsgewijze
alignatie, het opbouwen van een boomstructuur en progressieve alignatie. Na een beknopte uiteenzetting van de werking moest een analyse worden uitgevoerd om de tijdsverdeling
van de verschillende fasen na te gaan. De meegeleverde inputsets bleken echter te weinig
70
verschillend, zodat er random inputsets moesten worden gecreëerd om de invloed van de
verschillende parameters op de uitvoeringstijd na te gaan. Hieruit bleek vooral de paarsgewijze alignatie in het merendeel van de gevallen de meeste tijd in beslag te nemen, gevolgd
door progressieve alignatie. Het opbouwen van de boomstructuur nam enkel in de gevallen
waar het aantal sequenties groot is maar de lengte in verhouding zeer klein, de meeste tijd
in beslag.
De paarsgewijze alignatie werd versneld door deze deelfase te parallelliseren over alle
SPU’s. Om dit mogelijk te maken moest ook hier het geheugengebruik sterk verminderd
en geoptimaliseerd worden. De werklast werd op een doeltreffende manier verdeeld, zodat
alle SPU’s met een minimum aan communicatie een gelijk deel van de totale werklast
krijgen toegewezen. Hierdoor schaalt de versnelling van de paarsgewijze alignatie quasi
perfect mee met het aantal gebruikte SPU’s, voor een totale versnelling tussen 1.5 en 2.2
voor de meegeleverde inputsets.
Als laatste aanpassing van Clustal W werd de progressieve alignatie geoptimaliseerd.
Deze laatste stap bleek moeilijker te parallelliseren wegens het recursieve karakter van de
functies. Uiteindelijk kan de vaakst uitgevoerde functie van deze stap worden opgesplitst
in twee afzonderlijke delen. Omdat de functie zo vaak wordt opgeroepen was het niet
aangewezen om bij elke nieuwe functieoproep een nieuwe SPU-draad aan te maken. Er
werd daarom gekozen voor twee statisch gecreëerde SPU-draden die door communicatie
met de PPU de berekeningen uitvoeren op de juiste data. Ondanks een grote versnelling
tegenover dynamische draadcreatie, zorgt de extra communicatie voor een vertraging. De
progressieve alignatie kon op deze manier dus niet worden versneld.
Als algemeen besluit kan men stellen dat de Cell -architectuur dankzij tal van innovatieve vernieuwingen een zeer grote prestatie kan bekomen. Om een programma te bekomen
dat daadwerkelijk optimaal gebruik maakt van de Cell, zijn er veel bijkomende optimalisatiestappen nodig. Niet alle programma’s zijn echter even eenvoudig aan te passen.
Tegenover de grote prestatiewinst staat dus een grote complexiteit.
71
Bijlage A
Bijkomende resultaten
A.1
Gedetailleerde resultaten matrixvermenigvuldiging
Hierna volgen de gedetailleerde resultaten van de matrixvermenigvuldiging besproken in
hoofdstuk 3. De versnelling wordt steeds berekend tegenover de overeenkomstige uitvoering op de PPU.
72
Tabel A.1: Resultaten matrixvermenigvuldiging origineel algoritme.
Dimensie
PPU/SPU
Draden
DMA[%]
Berekeningen[%]
Totaal[ms]
Versnelling
128
PPU
1
N/A
N/A
16
1
128
SPU
1
45,65
54,35
37
0,44
128
SPU
2
45,89
54,10
21
0,78
128
SPU
4
46,75
53,25
15
1,10
128
SPU
8
48,56
51,44
16
1,02
256
PPU
1
N/A
N/A
142
1
256
SPU
1
35,54
64,46
235
0,61
256
SPU
2
35,67
64,33
120
1,19
256
SPU
4
36,67
63,33
65
2,19
256
SPU
8
37,92
62,08
42
3,41
512
PPU
1
N/A
N/A
1168
1
512
SPU
1
29,28
70,72
1679
0,70
512
SPU
2
31,11
68,89
863
1,35
512
SPU
4
31,48
68,52
438
2,67
512
SPU
8
38,18
61,82
248
4,70
1024
PPU
1
N/A
N/A
9587
1
1024
SPU
1
27,31
72,69
12962
0,74
1024
SPU
2
28,37
71,63
6573
1,46
1024
SPU
4
29,76
70,24
3350
2,86
1024
SPU
8
39,72
60,28
1941
4,94
73
Tabel A.2: Resultaten matrixvermenigvuldiging gebufferd algoritme.
Dimensie
PPU/SPU
Draden
DMA[%]
Berekeningen[%]
Totaal[ms]
Versnelling
128
PPU
1
N/A
N/A
16
1
128
SPU
1
3,01
96,99
24
0,67
128
SPU
2
3,00
97,00
15
1,12
128
SPU
4
3,04
96,96
12
1,41
128
SPU
8
3,07
96,93
14
1,21
256
PPU
1
N/A
N/A
142
1
256
SPU
1
1,41
98,59
172
0,83
256
SPU
2
1,41
98,59
88
1,61
256
SPU
4
1,43
98,57
48
2,94
256
SPU
8
1,44
98,56
32
4,42
512
PPU
1
N/A
N/A
1168
1
512
SPU
1
0,68
99,32
1330
0,88
512
SPU
2
0,68
99,32
667
1,75
512
SPU
4
0,68
99,32
338
3,45
512
SPU
8
0,69
99,31
178
6,58
1024
PPU
1
N/A
N/A
9587
1
1024
SPU
1
0,34
99,66
10514
0,91
1024
SPU
2
0,34
99,66
5260
1,82
1024
SPU
4
0,34
99,66
2634
3,64
1024
SPU
8
0,34
99,66
1326
7,23
74
Tabel A.3: Resultaten matrixvermenigvuldiging algoritme met lusontvouwing.
Dimensie
PPU/SPU
Draden
DMA[%]
Berekeningen[%]
Totaal[ms]
Versnelling
128
PPU
1
N/A
N/A
16
1
128
SPU
1
7,84
92,16
11
1,43
128
SPU
2
7,81
92,19
8
2,00
128
SPU
4
8,14
91,86
9
1,81
128
SPU
8
8,20
91,80
13
1,28
256
PPU
1
N/A
N/A
142
1
256
SPU
1
3,81
96,19
68
2,08
256
SPU
2
3,83
96,17
37
3,88
256
SPU
4
3,86
96,14
23
6,26
256
SPU
8
4,33
95,67
20
6,96
512
PPU
1
N/A
N/A
1168
1
512
SPU
1
1,88
98,12
503
2,32
512
SPU
2
1,90
98,10
254
4,60
512
SPU
4
1,93
98,07
131
8,89
512
SPU
8
2,05
97,95
74
15,68
1024
PPU
1
N/A
N/A
9587
1
1024
SPU
1
0,95
99,05
3895
2,46
1024
SPU
2
0,95
99,05
1950
4,92
1024
SPU
4
0,96
99,04
979
9,79
1024
SPU
8
0,99
99,01
499
19,23
75
A.2
Gedetailleerde resultaten empirische analyse Clustal W
De resultaten van de empirische analyse van Clustal W met variërend aantal sequenties N
en lengte van de sequenties L, zijn hieronder weergegeven. Hierbij is de procentuele uitvoeringstijd van de drie fasen weergegeven (PW: paarsgewijze alignatie, GT: boomstructuur
opstellen, PA: paarsgewijze alignatie).
76
Tabel A.4: Resultaten empirische analyse Clustal W.
N
L
PW[%]
GT[%]
PA[%]
10
10
7,72
12,00
80,28
10
50
18,58
2,34
79,09
10
100
20,83
0,81
78,36
10
500
23,60
0,04
76,35
10
1000
23,91
0,02
76,07
50
10
23,51
5,30
71,19
50
50
46,87
0,70
52,43
50
100
55,36
0,24
44,40
50
500
59,92
0,02
40,06
50
1000
60,33
0,00
39,67
100
10
31,04
7,17
61,79
100
50
61,25
1,02
37,72
100
100
67,47
0,33
32,20
100
500
73,58
0,02
26,40
100
1000
74,28
0,00
25,72
500
10
22,24
46,23
31,52
500
50
74,15
9,74
16,10
500
100
80,80
3,00
16,20
500
500
89,62
0,17
10,21
500
1000
91,45
0,05
8,50
1000
10
14,52
63,60
21,88
1000
50
66,83
21,40
11,77
1000
100
80,98
7,31
11,71
1000
500
91,42
0,37
8,21
1000
1000
94,06
0,10
5,84
77
Bibliografie
[1] MPI Documents. “http://www.mpi-forum.org/docs/”.
[2] Uniprotkb/swiss-prot protein knowledgebase release 52.5 statistics. “http://www.
expasy.ch/sprot/relnotes/relstat.html”.
[3] Sun microsystems introduces breakthrough ultrasparc t1 processor with coolthreads
technology, setting a new industry standard for performance, innovation. “”http://
www.sun.com/smi/Press/sunflash/2005-11/sunflash.20051114.2.xml”’, 2005.
[4] D.A. Bader and V. Sachdeva. An open benchmark suite for evaluating computer
architecture on bioinformatics and life science applications. SPEC Benchmark Workshop, 2006.
[5] R. Barua, D. Kranz, and A. Agarwal. Communication-minimal partitioning of parallel
loops and data arrays for cache-coherent distributed-memory multiprocessors. In
Languages and Compilers for Parallel Computing, pages 350–368, 1996.
[6] K. Chaichoompu, S. Kittitornkun, and S. Tongsima. MT-ClustalW: multithreading
multiple sequence alignment. In 20th International Parallel and Distributed Processing
Symposium, 2006.
[7] J. Cheetham, F. Dehne, S. Pitre, A. Rau-Chaplin, and P.J. Taillon. Parallel Clustal
W for PC Clusters.
[8] J. Choi, J. J. Dongarra, S. Ostrouchov, A. Petitet, D. Walker, and R. C. Whaley.
Lapack working note 100: a proposal for a set of parallel basic linear algebra subprograms. Technical Report, Computer Science Department, University of Tennessee,
Knoxville, 1995.
[9] J. Choi, J. J. Dongarra, and D. Walker. Pumma: Parallel universal matrix multiplication algorithms on distributed memory concurrent computers. Concurrency: Practice
and Experience, 6(7), 1994.
78
[10] D. Coppersmith and S. Winograd. Matrix multiplication via arithmetic progressions.
Journal of Symbolic Computation, 9, 1990.
[11] M.O. Dayhoff, R.M. Schwartz, and B.C. Orcutt. Atlas of Protein Sequence and
Structure. NBRF, 1978.
[12] R. C. Edgar. Muscle: a multiple sequence alignment method with reduced time and
space complexity. BMC Bioinformatics, 5(1), August 2004.
[13] D. Feng and R.F. Doolittle. Progressive sequence alignment as a prerequisite to
correct phylogenetic trees. Journal of Molecular Biology, 60:351–360, 1987.
[14] D. Hackenberg.
Fast matrix multiplication on cell (smp) systems.
“http:
//tu-dresden.de/die_tu_dresden/zentrale_einrichtungen/zih/forschung/
architektur_und_leistungsanalyse_von_hochleistungsrechnern/cell/”, 2007.
[15] A. Harwood. Parallel algorithms: Embarrassingly parallel. “http://www.cs.mu.oz.
au/498/notes/node40.html”, 2003.
[16] J. Held, J. Bautista, and S. Koehl. From a few cores to many: A tera-scale computing
research overview. White paper, Intel, 2006.
[17] S. Henikoff and J.G. Henikoff. Amino acid substitution matrices from protein blocks.
In Proceedings of the National Academy of Sciences, number 89, pages 10915–10919,
1992.
[18] J. Hennessy and D. Patterson. Computer architecture: a quantitative approach. Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 2002.
[19] IBM. Cell Broadband Engine programming handbook version 1.0, april 2006.
[20] IBM. Cell Broadband Engine programming tutorial version 1.1, june 2006.
[21] IBM. Cell Broadband Engine SDK Libraries Overview and Users Guide version 1.1,
2006.
[22] IBM. Synergistic Processor Unit Instruction Set Architecture version 1.1, jan 2006.
[23] IBM. Cell Broadband Engine Registers version 1.5, april 2007.
[24] W. Just. Computational complexity of multiple sequence alignment with sp-score.
Journal of Computational Biology, 8(6):615–623, 2001.
79
[25] W. Kahan. IEEE Standard 754 for Binary Floating-Point Arithmetic. Lecture notes
University of California, Berkeley, 1997.
[26] M. Kistler, M. Perrone, and F. Petrini. Cell multiprocessor communication network:
built for speed. IEEE Micro, may-june 2006.
[27] D. Mikhailov, H. Cofer, and R. Gomperts. Performance optimizations of Clustal W:
Parallel Clustal W, HT Clustal, and MULTICLUSTAL. Technical report, SGI Life
and Chemical Sciences. Technical report, SGI, 1999.
[28] G. Moore.
Cramming more components onto integrated circuits.
Electronics,
38(8):323–339, april 1965.
[29] D. Patterson and K. Yelick. Bridiging the processor memory gap. 2002.
[30] Mihai Preda. Implementing the lgamma() function in java. “http://blog.javia.
org/?p=26”.
[31] N. Saitou and M. Nei. The neighbor-joining method: A new method for reconstructing
phylogenetic trees. Molecular Biology and Evolution, 4:406–425, 1987.
[32] T.F. Smith and M.S. Waterman. Identification of common molecular subsequences.
Journal of Molecular Biology, 147:195–197, 1981.
[33] V. Strassen. Gaussian elimination is not optimal. Numerische Mathematik, 13, 1969.
[34] J. D. Thompson, D. G. Higgins, and T. J. Gibson.
CLUSTAL W: improving
the sensitivity of progressive multiple sequence alignment through sequence weighting, position-specific gap penalties and weight matrix choice. Nucleic Acids Res,
22(22):4673–4680, November 1994.
[35] M. Wolfe. More iteration space tiling. In Supercomputing ’89: Proceedings of the
1989 ACM/IEEE conference on Supercomputing, pages 655–664. ACM Press, 1989.
[36] Wm. A. Wulf and Sally A. McKee. Hitting the memory wall: Implications of the
obvious. Computer Architecture News, 23(1):20–24, 1995.
[37] J. Xue. Loop tiling for parallelism. Kluwer Academic Publishers, 2000.
80
Download