Een raamwerk voor softwarematige speculatieve...

66
Tim Bruneel parallellisatie Een raamwerk voor softwarematige speculatieve Academiejaar 2008-2009 Faculteit Ingenieurswetenschappen Voorzitter: prof. dr. ir. Jan Van Campenhout Vakgroep Elektronica en informatiesystemen Master in de ingenieurswetenschappen: computerwetenschappen Masterproef ingediend tot het behalen van de academische graad van Begeleiders: dr. ir. Hans Vandierendonck, ir. Sean Rul Promotor: prof. dr. ir. Koen De Bosschere

Transcript of Een raamwerk voor softwarematige speculatieve...

Page 1: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Tim Bruneel

parallellisatieEen raamwerk voor softwarematige speculatieve

Academiejaar 2008-2009Faculteit IngenieurswetenschappenVoorzitter: prof. dr. ir. Jan Van CampenhoutVakgroep Elektronica en informatiesystemen

Master in de ingenieurswetenschappen: computerwetenschappen Masterproef ingediend tot het behalen van de academische graad van

Begeleiders: dr. ir. Hans Vandierendonck, ir. Sean RulPromotor: prof. dr. ir. Koen De Bosschere

Page 2: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Dankwoord

Hierbij wens ik iedereen te bedanken die heeft bijgedragen tot het tot stand komen van

deze masterproef. Bijzondere dank gaat uit naar mijn promotor Koen De Bosschere

die het mogelijk maakte om rond dit onderwerp te werken, en de begeleiders Hans

Vandierendonck en Sean Rul voor de ondersteuning. Ik wens ook mijn familie en vrien-

den te bedanken voor de steun gedurende het jaar.

i

Page 3: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Toelating tot bruikleen

De auteur geeft de toelating deze masterproef voor consultatie beschikbaar te stellen

en delen van de masterproef te kopieren 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 deze masterproef.

20/08/2009

ii

Page 4: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Een raamwerk voor softwarematige speculatieve parallellisatie

door

Tim Bruneel

Promotor: prof. dr. ir. Koen De Bosschere

Thesisbegeleiders: dr. ir. Hans Vandierendonck, ir. Sean Rul

Afstudeerwerk ingediend tot het behalen van de graad van Master in de ingenieurswe-

tenschappen: computerwetenschappen

Academiejaar 2008–2009

Universiteit Gent

Faculteit Ingenieurswetenschappen

Vakgroep Elektronica en Informatiesystemen

Voorzitter: prof. dr. ir. J. Van Campenhout

Samenvatting

Steeds vaker bezitten processoren meerdere rekenkernen. De meeste programma’s

worden echter sequentieel geschreven, waardoor ze daar niet optimaal gebruik van ma-

ken. Daarom zijn er codetransformaties en parallellisatiemechanismen nodig die compi-

lers kunnen toepassen om programma’s te parallelliseren. Vanwege data afhankelijkhe-

den en alias analyse beperkingen kunnen echter niet alle parallellisatiemogelijheden bij

compilatie gedetecteerd worden. Speculatieve parallellisatie biedt daar een oplossing

voor, door toe te laten om mogelijks conflicterende code toch parallel uit te voeren, en

eventuele conflicten tijdens de uitvoering te detecteren en te herstellen. In deze mas-

terproef wordt onderzocht wat de mogelijkheden zijn van een softwarematig raamwerk

voor het speculatief parallel uitvoeren van code.

Trefwoorden: speculatieve parallellisatie, data afhankelijkheden, compilers, specula-

tieve uitvoering

iii

Page 5: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

A framework for software speculativemulti-threading

Tim Bruneel

Supervisors: prof. dr. ir. Koen De Bosschere, dr. ir. Hans Vandierendonck and ir. Sean Rul

Abstract— Parallelization of programs is becoming increasingly important with the rise of multi-core processors. However not all parallelization options can be detected at compile time, since there are some data dependencies that can only be resolved at runtime. Speculative multi-threading allows to parallelize code witch may contain dependencies, and detects and corrects them at runtime. This thesis discusses the development of a software based framework for speculative multi-threading. It describes and tests the use of a framework that would allow compilers introduce speculative multi-threading in an automated fashion.

Keywords— Speculative multi-threading, data dependencies, compilers

I.INTRODUCTION

The increasing performance of single core processors led to very complex architectures, which makes further improving them increasingly more difficult. Therefore, processor manufacturers started producing multi-core processors to gain further performance improvements. However, most programs are written sequentially, and do not benefit from multiple cores. Therefore, automatic parallelization techniques are an important research issue, as multi-core processors contribute an increasingly larger share of the market while most programmers still write sequential code.

The problem with sequential programs is that they contain data dependencies. For example, instructions that read and write the same address, can't be executed in parallel, because the result would be undefined, and dependent on which instruction finishes first and which last. When automatically parallelizing a program, the compiler has to detect code without such dependencies, to determine which code can be executed in parallel and which must be executed sequentially. However, not all dependencies can be resolved at compile time, which means some possible parallelization opportunities are lost because the compiler can't be sure whether a dependency will occur or not. Speculative multi-threading allows to execute code with unresolved dependencies in parallel, monitors the executed operations at runtime to detect any dependency violations that may occur and provides a recovery mechanism to restore a consistent program state if a dependency is violated. This thesis discusses the development of a software framework for speculative multi-threading that doesn't require a big knowledge of or many changes to the parallelized code.

II.SPECULATIVE CONTROL TECHNIQUES

For speculative execution, there's need for a framework that monitors memory operations, detects conflicts and can execute rollbacks when conflicts occur. Such a framework can be implemented with hardware support, which can minimize code

overhead by implementing speculative control techniques in the hardware.[1][2] Good speedups can be achieved this way. However the possibilities are limited, because the dependence on the hardware doesn't leave much room to differentiate the control framework for different types of speculative code. For that, a software framework can be useful. In this master test we will consider a software framework for speculative multi-threading, which could be used by compilers for automatic parallelization of code. Other software frameworks have been designed.[3][4] We use methods derived from those frameworks and try to implement them in a fashion that allows speculative parallelization without much knowledge of the parallelized code.

III. THE FRAMEWORK

To implement a software-based speculative multi-threading framework we considered two main approaches. In the following, when we speak of lower threads, we mean threads that execute code that comes first in the sequential version of the program, higher threads means threads that execute code that comes later in the sequential order.

A.Shared memory

With shared memory, we mean that the threads can execute operations on original memory locations of the variables. So if different threads use the same variables, they read and write to the same memory locations.

1) Possible conflictsIf the threads work with the same data, we can be faced with

three types of conflicts:- Read after write conflicts : if a higher thread reads an

address that is later written by a lower thread, the wrong value has been read by the higher thread: the instruction in the higher thread read the original value, while it should have read the value written by the lower thread.

- Write after read conflicts : if a higher thread writes to a memory location which is later read by a lower thread, the lower thread reads the wrong data: it reads the modified data while it should have read the original data.

- Write after write conflicts : if a lower thread writes to a location after a higher thread has written to that location, the location has the value of the earliest write instruction, while it should have the one of the latter.

2) Conflict DetectionTo detect conflicts, each read and write operation is

redirected to the framework, which logs the time for each operation. After execution of the speculative code, or after a configurable amount of speculative iterations, the threads compare their logged operations with each other to find if there were any dependencies and if they were violated. Write after write conflicts can be eliminated during execution by only writing to a location if no higher threads have already written to it.

Page 6: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

3) Conflict recoveryWhen executing write operations, the framework backs up

the original value of the address if it is the first time that address is written. That way there are copies for all the original values from memory locations that are written during the speculative execution. When a conflict is detected, these backups are used to restore the memory to the state it was before the speculative execution, and the code will be executed sequentially.

4) SynchronizationWhen no conflicts are detected, there is no synchronization

needed. The threads executed their operations on the original data locations, which now have the correct values

B.Local memory

In this approach, each thread has it's own local copy of the memory locations it writes. All the write operations are executed on the local copies.

1) Possible conflictsThe threads can't influence each other with their memory

operation, so we can only be faced with read after write conflicts: if a higher thread reads a location written by a lower thread, it will read the original data instead of the data written by the lower thread. As for write after read conflicts: since threads execute their write operations on their own copy, a write operation by one thread can't influence what is read by another thread, so write after read dependencies can't be violated. The same goes for write after write dependencies: when different threads have written copies of the same location, the copy of the highest thread will be selected at the synchronization, so the location will get the right value.

2) Conflict DetectionTo detect the read after write dependencies between the

threads, each thread keeps track of the original addresses of the locations that have been written and the locations that have been read before they were written. After execution of the speculative code, or after a configurable amount of speculative iterations, each thread compares addresses that were read before they were written with the addresses written by previous threads. If there's a match, there's a read after write dependency and it's violated because the thread read the original value of the address instead of the copy written by the previous thread. This doesn't require the framework to log each memory operation. The speculative code just has to ask the framework for a pointer to a local copy of each used location once, and can directly access it afterwards.

3)Conflict recoveryWhen there's a conflict, the local copies of the thread in

which the conflict was detected are discarded, along with the local copies of all higher threads. If there are no conflicts in the previous threads, they can be synchronized, since their execution was correct. That way we obtain a consistent memory status which corresponds to the beginning of the conflicting thread, and we can continue execution from there.

4)SynchronizationWhen there are no conflicts, we need to synchronize the

local copies of the different threads to get the right values in the original memory locations. Each thread receives a mapping that maps the original memory locations on a local copy from the previous thread, and adds to that its own mapping for locations that aren't in the map yet, and replaces the local copy address for values it has written that are already in the map. In the end, the map will contain all written locations and a pointer to the live-out written value of it. These are copied to the original locations, so all original addresses get the right live-out values.

IV. RESULTS

To compare the different approaches, we tested the framework on a simple example loop. The results indicated that the overhead of working with shared memory locations is way too big. The speculative code with shared memory performed a lot slower than the original. This is due to the fact that the framework has to be contacted for each memory operation. The fact that each memory operation must be logged together with the time it took place does not allow to cut back on control code overhead. Working with local copies doesn't have that restriction, since the framework doesn't need to know the time of the memory operations, that way it can perform much better by reducing the amount of times the framework is called. This led to a considerable speedup, especially when the number of iterations grew.

Other test results indicated that the use of smaller intervals after which to synchronize seems to be profitable. Conflicts are detected more quickly, less invalid iterations are executed.

We also parallelized the mcf benchmark from the SPEC 2006 benchmark suite.[5] This didn't result in a speedup, which indicates that there's need for combining the framework with code transformations when parallelizing complex loops with a lot of pointer operations.

V.CONCLUSIONS

The results indicate that working with local copies for each thread and synchronizing regularly are the best methods for implementing a software speculative multi-threading framework. The results with the mcf benchmark indicate that for parallelization of complex code with a lot of pointer operations, there is need for code transformations to be able to generate an efficient speculative version.

ACKNOWLEDGEMENTS

The authors would like to thank prof. dr. ir. Koen De Bosschere for the opportunity to work on this subject, and dr. ir. Hans Van Dierendonck and ir. Sean Rul for guidance.

REFERENCES

[1] H. Zhong, M. Mehrara, S. Lieberman, and S. Mahlke. Uncovering hidden loop level parallelism in sequential applications. In 14th International Symposium on High Performance Computer Architecture, pages 290–301. IEEE Computer Society, 2008.

[2] M. Bridges, N. Vachharajani, Y. Zhang, T. Jablin, and D. August. Revisiting the sequential programming model for multi-core. In Proceedings of the 40th Annual IEEE/ACM International Symposium on Microarchitecture, pages 69–84. IEEE Computer Society, 2007.

[3] Peter Rundberg and Per Stenström. An all-software thread-level data dependence speculation system for multiprocessors. Journal of Instruction-Level Parallelism, 3:2002, 2001.

[4] C. Oancea and A. Mycroft. Software thread-level speculation: an optimistic library implementation. In Proceedings of the 1st international workshop on Multicore software engineering, pages 23–32, 2008.

[5] Standard Performance Evaluation Corporation. Spec cpu 2006. http://www.spec.org/cpu2006/.

Page 7: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Inhoudsopgave

Dankwoord i

Toelating tot bruikleen ii

1 Inleiding 1

1.1 Situering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.1.1 Parallellisatie van sequentiele programma’s . . . . . . . . . . . . 1

1.1.2 Limieten van niet-speculatieve parallellisatie . . . . . . . . . . . 2

1.1.3 Speculatieve parallellisatie . . . . . . . . . . . . . . . . . . . . . 4

1.2 Overzicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 Speculatieve parallellisatietechnieken 6

2.1 Conflicten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.2 Speculatieve parallellisatie met hardware ondersteuning . . . . . . . . . 9

2.3 Softwarematig . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3 Softwarematige speculatieve uitvoering 11

3.1 Geheugentoegang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.1.1 Gedeelde data . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.1.2 Lokale data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

vi

Page 8: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

3.1.3 Analyse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3.2 Granulariteit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

4 Het ontworpen raamwerk 26

4.1 Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

4.2 Interne werking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

4.2.1 Gedeelde data . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

4.2.2 Lokale kopieen . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

4.2.3 Beperkingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

4.3 Resultaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

4.3.1 Gedeelde data . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

4.3.2 Lokale data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

4.3.3 Conclusie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

5 Parallellisatie van de mcf benchmark 49

5.1 MCF benchmark . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

5.2 Parallellisatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

5.2.1 Het a-blok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

5.2.2 Het b-blok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

5.2.3 De primal net simplex functie . . . . . . . . . . . . . . . . . . . 52

5.2.4 Resultaat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

5.3 Optimalisaties in de speculatieve uitvoering . . . . . . . . . . . . . . . 52

5.4 Resultaat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

6 Besluit 56

vii

Page 9: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Hoofdstuk 1

Inleiding

1.1 Situering

1.1.1 Parallellisatie van sequentiele programma’s

De toenemende prestatie van processoren zorgt voor een steeds complexere architectuur,

wat het verder opdrijven van de prestatie van processoren met een enkele kern steeds

moeilijker maakt. Daarom ontwerpt de industrie steeds vaker processoren met meerdere

kernen, en ook in de mainstream computermarkt maken deze processoren een steeds

groter deel van de markt uit.

Hoewel sommige applicaties die uit meerdere draden bestaan hiervan kunnen profi-

teren, levert dit voor traditionele sequentiele applicaties echter geen voordeel op. The-

orethisch gezien kan een processor met bijvoorbeeld twee kernen wel dubbel zoveel

instructies uitvoeren, maar het grootste deel van de applicaties is sequentieel geschre-

ven, en maakt dus helemaal geen gebruik van die mogelijkheden. Ook voor applicaties

die vandaag de dag nog geschreven worden geldt dat ze voornamelijk sequentieel zijn,

aangezien het expliciet parallel programmeren heel wat extra inspanning van de pro-

grammeur vraagt. Omdat hiermee veel potentiele snelheidswinst verloren gaat, vormen

methodes om sequentiele programma’s automatisch te parallelliseren een belangrijk on-

derzoeksthema. Een compiler kan dan parallellisatiemogelijkheden gaan detecteren in

1

Page 10: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

de code en die stukken parallel maken, zonder dat de programmeur daar aandacht aan

moet besteden.

1.1.2 Limieten van niet-speculatieve parallellisatie

Het probleem met sequentieel geprogrammeerde code is dat ze veel data afhankelijkhe-

den bevat. Opeenvolgende instructies die eenzelfde variabele bijvoorbeeld respectieve-

lijk schrijven en lezen kunnen niet parallel uitgevoerd worden.

De maximale snelheidswinst die geboekt kan worden door parallellisatie is afhanke-

lijk van de grootte van het sequentiele deel van het programma dat niet geparallelliseerd

kan worden. De wet van Amdahl zegt immers dat de maximale snelheidswinst van een

programma op een processor met N kernen als volgt berekend kan worden:

1

(1− P ) + PN

waarbij P de proportie van het programma is dat parallel uitgevoerd wordt, dus (1-P)

het gedeelte is dat sequentieel uitgevoerd wordt. Het resultaat hiervan wordt weergege-

ven in figuur 1.1, voor enkele programma’s met verschillende grootte van het parallelle

deel. Dit illustreert duidelijk het belang van het maximaliseren van het parallelle ge-

deelte van een programma. Bij een laag aantal processorkernen heeft het verhogen van

het aantal kernen nog een aanzienlijke invloed op de versnelling, maar naarmate het

aantal hoger wordt, wordt die invloed vrijwel nihil. De invloed van de grootte van het

parallelle deel neemt echter steeds toe, en zal dus in de toekomst een steeds belang-

rijkere rol spelen in het boeken van snelheidswinst op processoren met een toenemend

aantal kernen.

Bij het parallelliseren van code is het ontdekken en mogelijks elimineren van data

afhankelijkheden dus zeer belangrijk om het sequentieel uit te voeren deel te verklei-

nen. Geavanceerde alias analyse methodes om afhankelijkheden te detecteren, en code

transformaties om afhankelijkheden te elimineren, kunnen erin slagen stukken sequen-

tiele code te parallelliseren.

Een probleem is echter dat niet alle data afhankelijkheden met honderd procent

zekerheid tijdens de compilatie bepaald kunnen worden. Er zijn vaak stukken code

2

Page 11: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 1.1: De wet van Amdahl voor programma’s met verschillende groottes van de

parallelle delen

3

Page 12: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

waarvan niet met zekerheid gezegd kan worden of ze tijdens de uitvoering wel of niet

een data afhankelijkheid zullen bevatten. Dit betekent dat die stukken code niet op

de gewone manier geparallelliseerd kunnen worden, aangezien er niet ten allen tijde

een correct resultaat gegarandeerd kan worden. Compilers moeten dus conservatief te

werk gaan bij het invoeren van klassieke parallellisatie, en alle mogelijks conflicterende

stukken ongemoeid laten. Hierbij gaan veel parallellisatie mogelijkheden verloren, aan-

gezien er vaak stukken code zijn waar de compiler geen uitsluitsel over kan geven, die

in een zeer groot percentage van de uitvoeringen toch geen afhankelijkheden bevatten.

Speculatieve parallellisatie biedt daar een oplossing voor.

1.1.3 Speculatieve parallellisatie

De techiek van het speculatief parallelliseren bestaat erin stukken code die mogelijks

conflicteren, maar in de meerderheid van de uitvoeringen geen conflicten opleveren, toch

te parallelliseren, en extra controlemechanismen in te bouwen om tijdens de uitvoering

te gaan controleren of er data afhankelijkheden zijn en of die geschonden worden. Zo

kan het parallelliseerbare deel van de code vergroot worden tot boven de limieten van de

klassieke parallellisatie, weliswaar ten koste van een extra overhead voor het detecteren

van conflicten en het in stand houden van herstelmechanismen.

In deze masterproef wordt het ontwerp van een puur softwarematig raamwerk on-

derzocht dat weinig kennis en verandering vereist van de te parallelliseren code.

1.2 Overzicht

Hier volgt een korte toelichting bij de structuur van de tekst.

Hoofdstuk 2 geeft een overzicht van verschillende technieken voor speculatieve pa-

rallellisatie, de literatuur daaromtrend en wat deze thesis daaraan toevoegt.

Hoofdstuk 3 geeft een overzicht van softwarematige speculatieve uitvoeringsmecha-

nismen, hun voor en nadelen, en ontwerpsbeslissingen die de prestatie beınvloeden.

In hoofdstuk 4 wordt het gebruik en de werking van het ontworpen raamwerk toe-

4

Page 13: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

gelicht.

Hoofdstuk 5 beschrijft de speculatieve parallellisatie van de MCF benchmark uit de

SPEC CPU 2006 benchmark suite.

Hoofdstuk 6 formuleert een besluit over het ontworpen raamwerk, de resultaten, en

wat er in de toekomst mogelijk is.

5

Page 14: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Hoofdstuk 2

Speculatieve

parallellisatietechnieken

Voor het speculatief parallel uitvoeren van code is er nood aan een raamwerk dat tijdens

de uitvoering de geheugentoegangen gaat controleren, eventuele geschonden afhankelijk-

heden gaat detecteren en incorrecte uitvoeringen kan ongedaan maken. In dit hoofdstuk

wordt eerst beschreven welke conflicten er kunnen voorkomen, waarna wordt beschreven

welke mogelijkheden er zijn om een raamwerk te ontwerpen om ze te detecteren.

2.1 Conflicten

Het controlemechanisme moet volgende conflicten kunnen detecteren:

• Read after write: Een draad voert een instructie uit die data naar een geheugen-

locatie a schrijft, terwijl een andere draad een instructie uitvoert die data van

die geheugenlocatie leest. Wanneer in de sequentiele uitvoering de schrijfoperatie

voor de leesoperatie komt, maar door de parallelle uitvoering die volgorde om-

wisselt, levert de leesinstructie een verkeerd resultaat op. Dit wordt geıllustreerd

met een voorbeeld in figuur 2.1.

• Write after read: De omgekeerde situatie: de ene draad schrijft data waarna de

6

Page 15: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 2.1: illustratie van een read after write conflict

andere die leest, terwijl dit in de sequentiele uitvoering omgekeerd was. Dit wordt

geıllustreerd met een voorbeeld in figuur 2.2.

• Write after write: Wanneer twee draden schrijfoperaties naar dezelfde lokatie

uitvoeren die in de originele uitvoering in omgekeerde volgorde gebeurden, staat

na uitvoering het verkeerde resultaat in het geheugen. Dit wordt geıllustreerd

met een voorbeeld in figuur 2.3.

Er zijn verschillende aanpakken mogelijk om een omgeving te implementeren die

deze conflicten detecteerd.

7

Page 16: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 2.2: illustratie van een write after read conflict

Figuur 2.3: illustratie van een write after write conflict

8

Page 17: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

2.2 Speculatieve parallellisatie met hardware onder-

steuning

Om de extra overhead die gepaard gaat met speculatieve parallellisatie te beperken, kan

gebruikt gemaakt worden van speciale hardware die bepaalde taken op zich neemt. Zo

beschrijven Zhong et al. [1] speculatieve parallellisatie met gebruik van een hardware

transactioneel geheugen en een communicatienetwerk tussen kernen om informatie door

te geven en bepaalde afhankelijkheden te omzeilen.

Bridges et al. [2] ontwikkelen een speculatief raamwerk met ondersteuning van ge-

avanceerde compiler en hardware technieken om te kunnen parallelliseren met weinig

overhead. Men maakt daarbij bovendien gebruik van een uitbreiding van het program-

meermodel, met een extra verantwoordelijkheid voor de programmeur in het specifieren

van bepaalde parallellisatie mogelijkheden. De hardwareondersteuning betreft een ge-

heugen met verschillende versies en een communicatienetwerk tussen de processorker-

nen. Bovendien veronderstelden ze in hun resultaten geen extra kost voor misspeculatie.

Balakrishnan en Sohi [3] onderzoeken het idee om methodes uit te voeren voor ze

effectief opgeroepen worden maar wanneer vermoed wordt dat hun juiste input reeds

beschikbaar is, maar ook hier wordt hardware ondersteuning verondersteld: een uit-

breiding van de instructieset, speciale cache vlaggen, een speciale hardware buffer, en

communicatiemiddelen tussen die buffer, de cache en het geheugen.

Hardware ondersteunde speculatieve parallellisatie kan mooie resultaten behalen,

maar is gebonden aan de specifieke hardware, en dus vaak beperkt tot een specifieke

methode.

2.3 Softwarematig

Naast controlemechanismen in de hardware, kan er ook een softwarematige omgeving

gecreeerd worden voor speculatieve parallellisatie. Een softwarematige omgeving heeft

als voordeel dat het onafhankelijk is van de hardware, en er dus met verschillende

technieken geexperimenteerd kan worden, waar je in een hardware mechanisme vaak

9

Page 18: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

aan een techniek gebonden bent. Softwarematige oplossingen zijn dus interessant om de

gebruikte methodes te varieren aan de hand van de eigenschappen van de uitgevoerde

code. Het grootste nadeel van een puur softwarematig raamwerk is de extra code

overhead die gepaard gaat met het controleren van geheugentoegangen. Het komt er

dan ook op aan die zo veel mogelijk te beperken.

Papadimitriou en Mowry [4] ontwerpen een softwarematig mechanisme om afhan-

kelijkheden te detecteren aan de hand van de paging hardware van het geheugen. Het

pagina niveau blijkt echter te grote granulariteit te hebben, waardoor er te veel valse

conflicten gedetecteerd worden.

Prabhu en Olukotun [5] proberen op een puur softwarematige manier speculatieve

parallellisatie in te voeren in verscheidene SPEC CINT 2000 benchmarks, door middel

van uitgebreide manuele code transformaties. Dit verkent de limieten van wat theore-

tisch mogelijk is, maar komt neer op een zeer programma-specifieke optimalisatie, die

niet geautomatiseerd op grote schaal kan toegepast worden.

In deze masterproef wordt een puur softwarematig raamwerk voor het speculatief

uitvoeren van code besproken. Het is de bedoeling dat het raamwerk breed toepasbaar

is, en de transformatie van de programmacode eenvoudig is, en dus geautomatiseerd

door een compiler kan gebeuren, zonder complexe aanpassingen waarvoor menselijke

ingrepen in de code en een te grote kennis van het te parallelliseren programma nodig

zijn. De geımplementeerde mechanismen zijn gebaseerd op technieken die in ande-

re raamwerken gebruikt zijn [6, 7], maar geımplementeerd op een manier die minder

ingrepen en minder kennis van de te parallelliseren code vereist.

10

Page 19: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Hoofdstuk 3

Softwarematige speculatieve

uitvoering

Voor het speculatief uitvoeren van code moeten op verschillende vlakken beslissingen

genomen worden over de toegepaste methodes in het speculatieve raamwerk. De pres-

tatie van deze keuzes hangt af van de eigenschappen en het geheugengebruik van de

speculatief uit te voeren code. Verscheidene keuzes worden in dit hoofdstuk theoretisch

beschreven, in hoofdstuk 4 worden ze in de praktijk geımplementeerd en getest. We

beschouwen twee manieren om het geheugen te lezen en te schrijven in de speculatieve

code, met daarbij horend de mogelijke manieren om conflicten te detecteren, mogelijke

herstelmechanismes om in geval van een conflict toch een correcte uitvoering te kunnen

garanderen en synchronisatiemethodes om een correct resultaat na de speculatieve uit-

voering te verzekeren. Daarnaast kijken we ook naar keuzes rond de granulariteit om

het geheugen te controleren.

3.1 Geheugentoegang

We beschouwen twee methodes voor geheugentoegang in speculatief parallelle draden.

De eerste manier is de draden naar het geheugen laten lezen en schrijven van en naar

de originele geheugenlocaties. Dat noemen we het werken met gedeelde data, omdat de

11

Page 20: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

draden tegelijk dezelfde geheugenlocaties kunnen gebruiken. De tweede methode is de

draden tijdens de speculatieve uitvoering laten lezen en schrijven naar een eigen kopie

van de data. Dit noemen we het werken met lokale data, omdat de draden tijdens het

uitvoeren van de speculatieve code elkaars data niet kunnen beınvloeden.

3.1.1 Gedeelde data

Bij het werken met gedeelde data lezen en schrijven de draden van en naar de origi-

nele geheugenlocaties van de variabelen. Als verschillende draden dezelfde variabelen

gebruiken lezen en schrijven ze dus naar dezelfde geheugenlocaties.

Mogelijke conflicten

Bij het werken op de originele data kunnen zowel read after write, write after read

als write after write afhankelijkheden geschonden worden. Omdat de draden naar de-

zelfde geheugenlocaties kunnen schrijven kunnen ze elkaars werking op elk moment

beınvloeden.

Conflictdetectie

• Achteraf : we kunnen in elke draad bijhouden welke lokaties er gelezen en ge-

schreven worden, om deze data na het voltooien van de speculatieve uitvoering

met elkaar te vergelijken. Zo kunnen we achteraf de afhankelijkheden detecteren,

en als er zich een voordoet, veronderstellen dat hij geschonden is. Wordt een adres

gelezen in de ene draad, en geschreven in een andere, dan is er een afhankelijkheid.

Deze methode kan in sommige situaties echter nadelig zijn, omdat er afhanke-

lijkheden kunnen voorkomen tussen de draden die toch niet geschonden worden,

bijvoorbeeld wanneer een draad die eerdere code uitvoert eerst op een adres schrijft

waarna een draad die latere code uitvoert op dat adres leest. Er is dan een read

after write afhankelijkheid tussen de draden, maar die wordt hier niet geschonden.

In dat geval kan het nuttig zijn om niet alleen bij te houden welke adressen welke

draad gelezen of geschreven heeft, maar ook wanneer. Door de timestamps te

12

Page 21: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

vergelijken kan zo een onderscheid gemaakt worden tussen afhankelijkheden die

wel en niet geschonden zijn. Dit heeft op zijn beurt echter als nadeel dat dit extra

overhead oplevert.

• Interval: Als we de conflictdetectie helemaal op het einde van de speculatieve

uitvoering doen, dan kan er veel uitvoeringstijd verloren zijn indien zich in het

begin een conflict voordeed. Daarom kan het voordelig zijn om de speculatieve

uitvoering in intervallen te verdelen, en na elk interval te gaan controleren of er

conflicten waren, met dezelfde methode als uit de vorige paragraaf.

• Live: In plaats van achteraf kunnen we ook bij elke geheugentoegang zelf contro-

leren of er een afhankelijkheid geschonden wordt. Om dit te kunnen doen moet

er gebruik gemaakt worden van gedeelde controledata tussen de draden. Dit kan

door voor een gedeelde datastructuur bij te houden die voor elke geheugenlocatie

bijhoudt welke de hoogste draad is die er reeds naar geschreven heeft, en welke

de hoogste draad is die het gelezen heeft.

Bij het uitvoeren van een schrijfoperatie kan dan gekeken worden welke de hoogste

draad is die dat adres gelezen heeft, is die hoger dan de huidige draad, dan is er

een read after write conflict: een latere leesoperatie heeft een locatie gelezen die

door een eerdere schrijfoperatie nog moet geschreven worden.

Bij het uitvoeren van een leesoperatie wordt gekeken of er reeds een hogere draad

op dat adres geschreven heeft, is dit het geval dan is er een write after read

afhankelijkheid geschonden.

Write after write conflicten kunnen geelimineerd worden door voordat de data

weggeschreven wordt, te gaan kijken of er reeds een hogere draad naar dat adres

geschreven heeft, en niet te schrijven indien dit het geval is.

Het voordeel van achteraf controleren is dat er hierbij minder overhead is bij de

geheugentoegangen zelf, het nadeel is dat eventuele conflicten pas achteraf gedetecteerd

worden.

We kunnen ook de verschillende methodes combineren. Zo is het aangewezen om,

ook wanneer er pas achteraf op conflicten gedetecteerd wordt, op het moment van het

13

Page 22: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

schrijven zelf te controleren op write after write afhankelijkheden, omdat die conflicten

zo vermeden kunnen worden. Dit zorgt ervoor dat code waarin verschillende draden

naar dezelfde adressen schrijven toch speculatief parallelliseerbaar is.

Herstelmechanisme

In geval er een conflict gedetecteerd is, moet er actie ondernomen worden om toch

een correcte uitvoering van de code te kunnen garanderen. Als er een afhankelijkheid

geschonden wordt, zijn meestal wijzigingen aan de orgininele data aangebracht die

ongedaan gemaakt moeten worden. Daarom is er een mechanisme nodig om terug

te keren naar een consistente geheugenstatus om vanaf daar de uitvoering te hervatten.

• Undo-log: bij het werken met een undo-log wordt bij elke operatie gelogd wat ze

aan het geheugen verandert, zodat in geval van een conflict de laatste operaties

ongedaan gemaakt kunnen worden tot op een punt waar de uitvoering nog correct

was.

• Kopie van origineel: wanneer een geheugenlocatie voor het eerst geschreven

wordt, kan er een kopie genomen worden van de originele data. In geval van een

conflict kan dan alle originele data hersteld worden.

Het werken met een undo-log heeft als voordeel dat de uitvoering in geval van een

conflict niet helemaal opnieuw vanaf het begin van de speculatieve uitvoering moet her-

beginnen. Er kan teruggekeerd worden tot op het punt van het conflict en de uitvoering

kan vanaf daar hervat worden. Dit is dus voordelig voor lange speculatieve uitvoerin-

gen die naar het einde toe vaak een conflict geven, of bijvoorbeeld lussen waarvan het

aantal iteraties vooraf niet gekend is, en er dus op het einde een aantal iteraties teveel

uitgevoerd zijn die ongedaan gemaakt moeten worden. In de meeste gevallen is deze

methode echter nadelig omwille van de extra overhead bij elke schrijfoperatie. Bij het

werken met een kopie van het origineel is er een stuk minder overhead: enkel bij de

eerste schrijfoperatie naar elke locatie is er extra werk voor het kopieren van de origine-

le data. Bovendien vraagt het werken met een kopie van het origineel een veel kleiner

14

Page 23: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

geheugengebruik, vooral in uitvoeringen waar veel schrijfoperaties naar hetzelfde adres

worden gedaan.

Synchronisatie

Bij het werken op de originele data is er geen synchronisatie nodig. Indien er geen

conflicten waren bevindt het geheugen zich vanzelf al in dezelfde status als na het

uitvoeren van de originele, sequentiele code.

3.1.2 Lokale data

Een tweede mogelijkheid voor speculatieve geheugentoegang is het werken met lokale

data. Hierbij zal elke speculatieve draad zijn wijzigingen uitvoeren op een eigen kopie

van de data, die dan achteraf met elkaar gesynchroniseerd worden.

Mogelijke conflicten

Aangezien de verschillende draden elk naar andere locaties in het geheugen lezen en

schrijven, kunnen ze elkaar tijdens hun uitvoering niet beınvloeden. Dit betekent dat er

geen write after read en write after write afhankelijkheden geschonden kunnen worden.

Er kunnen wel read after write conflicten voorkomen: wanneer er een read after write

afhankelijkheid bestaat tussen twee instructies die beide in een andere draad uitgevoerd

worden, zal het conflict altijd optreden: aangezien beide instructies op een andere kopie

van de data worden uitgevoerd zal de leesinstructie nooit de data weggeschreven door

de schijfinstructie lezen, zelfs al vond de schrijfinstructie eerder plaats. Dit wordt

geıllustreerd in figuur 3.1. Bij het lezen van de variabele a zal de tweede draad eerst

kijken of hij zelf al een lokale kopie heeft, is dit niet het geval, dan haalt hij de data op

van de originele locatie, die nog ongewijzigd is.

15

Page 24: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 3.1: Illustratie van een read after write afhankelijkheid tussen draden bij het

werken met lokale kopieen van de data

Conflictdetectie

Er moet gedetecteerd worden of er read after write afhankelijkheden zijn tussen de

draden. Is dit het geval, dan zal die afhankelijkheid hoe dan ook geschonden zijn, zoals

hierboven beschreven. We kunnen deze afhankelijkheden detecteren door voor elke

draad bij te houden op welke adressen hij leest en schrijft, en die data te vergelijken:

als er in een draad die latere code uitvoert een adres gelezen wordt dat geschreven wordt

in een draad die eerdere code uitvoert, dan is er een conflict. Ook hier kunnen we, net

zoals bij het werken met gedeelde data, op verschillende manieren te werk gaan:

• live: we kunnen controledata tussen de draden delen om conflicten onmiddellijk

te kunnen detecteren. Dit zorgt er echter voor dat er opnieuw met locks gewerkt

moet worden, en de draden dus afhankelijk van elkaar worden en af en toe moeten

wachten, wat het grote voordeel van werken met lokale data deels teniet doet.

• achteraf : elke draad kan bijhouden welke variabelen hij gelezen en geschreven

heeft en die data achteraf doorgeven, zodat elke draad de gelezen variabelen kan

vergelijken met welke variabelen eerdere draden geschreven hebben om zo ge-

schonden afhankelijkheden te detecteren. Let wel: enkel de adressen die gelezen

16

Page 25: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 3.2: Vergelijken van controledata voor detecteren van een conflict

worden wanneer ze door die draad nog niet geschreven zijn, komen in aanmer-

king, want anders zijn ze afhankelijk van een schrijfoperatie in de eigen draad,

en niet van schrijfoperaties uit vorige draden. Beschouw het voorbeeld in figuur

3.2: draad 1 leest variabele a en draad 2 schrijft zijn eigen kopie van variabele a.

Dit is geen probleem, want draad 2 voert latere code uit en door het werken met

een lokale kopie kan die de data gebruikt door draad 1 niet beınvloeden. Draad

1 schrijft echter ook een lokale kopie van variabele b terwijl draad 2 variabele b

leest. Dit is wel een afhankelijkheid, en die wordt gedetecteerd doordat zowel de

schrijfdata van draad 1 als de leesdata van draad 2 variabele b bevatten.

• interval: net zoals bij het werken met gedeelde data geldt ook hier dat tussentijds

synchroniseren voordelig kan zijn. Het laat toe om conflicten sneller te detecteren

en zo minder ongeldige iteraties uit te voeren.

Herstelmechanisme

Aangezien alle wijzgingen weggeschreven worden naar lokale kopieen, is de originele data

van voor de speculatieve uitvoering nog onaangetast, dus is er geen herstelmechanisme

nodig om naar die toestand terug te keren. Aangezien elke draad zijn eigen versie van

de data heeft is het bovendien mogelijk om alle wijzigingen door te voeren tot aan de

draad waar het conflict zich voordeed, om zo niet de volledige uitvoering opnieuw te

moeten doen. Dit wordt geıllustreerd in figuur 3.3: de uitvoering van draden 1 en 2,

17

Page 26: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 3.3: Gedeeltelijke sychronisatie bij herstellen van een conflict

die code uitvoeren die in de sequentiele versie eerder kwam dan de code uit draad 3,

is correct, dus kan die data gesynchroniseerd worden, om vervolgens de uitvoering te

kunnen hervatten vanaf het begin van draad 3.

Synchronisatie

Na een correcte speculatieve uitvoering, moet de data van de verschillende lokale kopieen

gesynchroniseerd worden en gecommit worden naar de originele geheugenlocaties. Voor

elke geheugenlocatie dient de data geschreven te worden van hoogste draad die de data

geschreven heeft. Met de hoogste draad wordt de draad bedoelt die code uitvoert die

het laatst kwam in de sequentiele versie van de code.

• Een eerste mogelijkheid is om een mapping door te geven tussen de draden. De

draad die de code uitvoert die eerst kwam in de sequentiele versie geeft een da-

tastructuur door met alle geheugenadressen waarop hij geschreven heeft, samen

met de adressen van de lokale kopieen ervan. Hij geeft dit door aan de draad

die het daaropvolgende stuk code uitvoerde. Deze laatste voegt daaraan zijn ge-

schreven adressen toe, en overschrijft daarbij adressen die zowel door hem als de

voorgaande draad geschreven zijn, want zijn versie van de data is de juiste live-out

18

Page 27: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

waarde gezien zijn code later kwam in de sequentiele uitvoering. Hij geeft dan op

zijn beurt de datastructuur door aan de volgende draad, enzoverder. De laatste

draad voegt er ook zijn data aan toe, en gaat dan de datastructuur overlopen

en voor alle adressen die erin voorkomen de lokale data naar het originele adres

kopieren. Deze doorgegeven data kan meteen ook gebruikt worden als log van de

uitgevoerde schrijfoperaties van vorige draden bij het detecteren van conflicten

(de schrijfdata uit figuur 3.2).

Voorbeeld we beschouwen twee speculatieve draden, draad 1 en draad 2, waar-

bij draad 1 code uitvoert die in de sequentiele uitvoering voor de code van draad

2 komt. Stel dat draad 1 op een adres a en b schrijft, dan bezit hij lokale kopieen

van die locaties zoals aangegeven in punt (a) op figuur 3.4. Stel dat draad 2 enkel

op een adres b schrijft, dan bevat draad 2 een lokale kopie van die locatie zoals

aangegeven in punt (b). Beide draden bevatten een mapping van de originele

locaties met de lokale kopieen die ze geschreven hebben. Bij de synchronisatie

wordt de mapping van draad 1, weergegeven in punt (c) doorgegeven aan draad

2. Draad 2 voegt daar zijn gegevens aan toe, in dit geval de pointer naar zijn

lokale kopie van adres b. Hiermee overschrijft hij de entry van draad 1 in de

mapping. De uiteindelijke mapping ziet er dan uit zoals in punt (d). Deze wordt

gebruikt om de originele geheugenlocaties aan te passen, zoals aangegeven in (e).

• Een tweede mogelijkheid is om tijdens de synchronisatie een gedeelde datastruc-

tuur bij te houden, met voor elke locatie welke draad ernaar geschreven heeft.

Elke draad overloopt alle adressen waarvan hij een lokale kopie geschreven heeft

en controleert in de gedeelde datastructuur welke draad er naar die locatie ge-

schreven heeft. Is dat een draad die code uitvoert die later komt in de sequentiele

versie, dan gooit de draad zijn kopie gewoon weg. Is dit niet het geval, dan schrijft

de draad zijn kopie weg naar het originele adres en past de gedeelde datastructuur

aan.

Voorbeeld We beschouwen opnieuw hetzelfde voorbeeld, maar nu met de twee-

de synchronisatiemethode, zoals weergegeven in figuur 3.5 Stel dat draad 1 eerst

19

Page 28: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 3.4: Synchronisatie met behulp van een doorgegeven mapping

begint te synchroniseren en zijn kopie van adres a wil wegschrijven. De gedeelde

datastructuur bevat nog niks, dus schrijft hij zijn kopie weg, en maakt een entry

aan in de gedeelde datastructuur met het adres a en zijn identificatie. Draad 2

ziet deze versie van de datastructuur wanneer hij zijn data wil synchroniseren.

Vooraleer hij zijn kopie van b wegschrijft zoekt hij in de map naar de entry voor

dat adres, en stelt vast dat die er nog niet is. Hij schrijft dus zijn kopie weg,

en maakt een entry aan in de datastructuur voor adres b met zijn identificatie.

Vervolgens wil draad 1 nog zijn kopie van b synchroniseren. Wanneer hij de entry

in de gedeelde datastructuur opzoekt, ziet hij dat draad 2 reeds adres b heeft

geschreven. Aangezien dat een hogere draad is, schrijft draad 1 zijn versie van b

niet weg.

Voordeel van de tweede manier van werken is dat de draden het werk verdelen,

terwijl bij de eerste methode al het werk doorgeschoven werd naar 1 draad die dan alle

data moet gaan kopieren. Nadeel van deze manier is echter dat een deel van de operaties

20

Page 29: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 3.5: Synchronisatie door wegschrijven en bijhouden controledata

nutteloos zijn (bijvoorbeeld wanneer een lagere draad eerst zijn data wegschrijft, om

vervolgens overschreven te worden door een hogere draad), en dat er locks genomen

moeten worden voor het wegschrijven ervan, met als gevolg dat het kan dat er gewacht

moet worden op een draad die data wegschrijft die misschien niet eens weggeschreven

moest worden. Bovendien moeten de draden voordien toch nog op elkaar wachten om

controledata door te geven voor het detecteren van conflicten, vooraleer ze data kunnen

wegschrijven. Bij de eerste methode kunnen die twee gecombineerd worden. In het

raamwerk is er gekozen voor het combineren van de conflictdetectie en synchronisatie, en

wordt enkel de eerste methode geımplementeerd. De prestatieverschillen met de tweede

methode zullen voor aanzienlijke speculatieve uitvoeringen minimaal zijn vanwege het

kleine aandeel van de synchronisatie in de prestatie.

3.1.3 Analyse

Hier worden de theoretische voor en nadelen van werken met gedeelde data en werken

met lokale data kort vergeleken. Praktijkresultaten volgen in het volgende hoofdstuk.

21

Page 30: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Het werken met gedeelde data op de originele geheugenlocaties heeft als voordeel

dat er in geval van correcte speculatie geen synchronisatie nodig is. Nadeel is dat er

meer overhead is bij het uitvoeren van lees en schrijf operaties, omdat er locks geno-

men moeten worden op de geheugenlocaties. Bij het werken met een undo-log moet

bovendien nog elke keer de vorige inhoud gekopieerd worden, wat dus telkens voor twee

schrijfoperaties zorgt in plaats van een. Bovendien moet bij elke operatie het tijdstip

bijgehouden worden. Dit zorgt ervoor dat voor elke lees- of schrijfoperatie het specu-

latieve raamwerk opgeroepen moet worden. Bovendien is het herstellen van conflicten

weinig efficient: bij het werken met een undo-log moeten heel wat operaties uitgevoerd

worden om terug te keren naar een consistente status, terwijl bij het werken met een

kopie van het origineel teruggekeerd moet worden naar het begin van de speculatieve

uitvoering. Bij het werken met een undo-log kan het geheugengebruik ook hoog oplopen

wanneer er veel schrijfoperaties plaatsvinden.

Bij het werken op lokale data is er een stuk minder overhead tijdens de uitvoering:

enkel bij de eerste schrijfoperatie naar een adres moet de data gekopieerd worden en de

operatie geregistreerd worden, bij daaropvolgende operaties is er geen overhead meer

nodig. Ook het herstellen van conflicten kan efficienter: er kan gesynchroniseerd worden

tot voor code van de draad waar het conflict optrad, alle data van daaropvolgende

draden kan simpelweg genegeerd worden en de uitvoering kan vanaf de conflictdraad

hervat worden. Een nadeel van werken met lokale kopieen van de data is dat er na de

uitvoering gesynchroniseerd moet worden, en dat er bij het werken met veel draden veel

geheugen vereist is omdat elke draad een eigen kopie van zijn geschreven data heeft.

Verder kan nog opgemerkt worden dat speculatieve uitvoering met lokale data in

meer gevallen toepasbaar is dan met gedeelde data, vanwege het elimineren van write

after read en write after write afhankelijkheden.

3.2 Granulariteit

Een belangrijke vraag is hoe groot de verschillende geheugenlocaties zijn die we be-

schouwen. Als er bijvoorbeeld een datastructuur met verschillende elementen gebruikt

wordt, kunnen we elk element als een afzonderlijke locatie beschouwen, of de data-

22

Page 31: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 3.6: Conflicten detecteren met grote granulariteit

structuur op zijn geheel als 1 geheugenlocatie beschouwen. Beide methodes hebben in

verschillende situaties hun voor en nadelen.

Beschouwen we de datastructuur als 1 geheel, dan is er minder overhead bij het

uitvoeren van de operaties, omdat we operaties naar de verschillende elementen kunnen

beschouwen als 1 geheel, met de overhead van 1 operatie, zoals geıllustreerd in figuur

3.6. Dit veroorzaakt echter een minder nauwkeurige conflictdetectie, wat kan leiden

tot het detecteren van zogenaamde valse conflicten: er wordt een conflict gedetecteerd

waar er geen is, zoals geıllustreerd wordt in het voorbeeld dat volgt.

Beschouwen we de elementen van de datastructuur als afzonderlijke geheugenlo-

caties, dan hebben we voor het gebruik van elk van de elementen een overhead en

afzonderlijke controledata, zoals geıllustreerd in figuur 3.7. Het gevolg is wel dat we

de detectie van valse conflicten ten gevolge van operaties naar verschillende elementen

elimineren.

Voorbeeld Beschouw het volgende voorbeeld: draad 1 schrijft naar een element van

een array a, terwijl draad 2 een ander element van die array leest. Als we de da-

tastructuur als 1 adres beschouwen, zoals in figuur 3.8, dan wordt er een vals conflict

23

Page 32: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 3.7: Conflicten detecteren met fijne granulariteit

gedetecteerd, omdat beide draden hetzelfde adres, namelijk de plaats van datastructuur

a, lijken te lezen en schrijven. Het gevolg is dat de speculatieve uitvoering onterecht

gestopt zal worden en naar een vroeger punt teruggekeerd zal worden, waardoor er

heel wat tijd verloren gaat. Beschouwen we de elementen van a als afzonderlijke adres-

sen, zoals in figuur 3.9, dan wordt er geen conflict gedetecteerd en wordt er wel verder

uitgevoerd.

24

Page 33: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 3.8: Conflicten detecteren met grote granulariteit

Figuur 3.9: Conflicten detecteren met fijne granulariteit

25

Page 34: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Hoofdstuk 4

Het ontworpen raamwerk

4.1 Interface

Als voorbeeld beschouwen we volgend codefragment met een lus die we speculatief

willen uitvoeren:

int x[1000], *y, z, i;

...

for(i=0; i < 1000; i++) {

x[i] = *y + z;

}

We hebben een array x, een onbekende pointer y die naar een element uit x kan wij-

zen, en een variabele z. Als y naar een element uit x wijst, dan is er een afhankelijkheid

tussen de iteratie die dat element schrijft en alle daarop volgende iteraties. Dit is dus

een lus die we speculatief kunnen parallelliseren.

De luscode wordt in een nieuwe functie geplaatst en aangepast voor de speculatieve

uitvoering. Deze functie zal worden opgroepen door het speculatieve raamwerk, en

krijgt als parameter een structuur met controledata gebruikt door het raamwerk, en

een startwaarde is en een eindwaarde ie van de iteratievariabele. De functie moet

de iteraties van is tot en met ie - 1 uitvoeren. Om zinloze iteraties te vermijden

26

Page 35: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

wordt er bij elke iteratie een controlevariabele gecontroleerd die aangeeft of er in een

eerdere draad reeds een conflict gevonden is of een breakstatement in de lus uitgevoerd

is waardoor de iteraties van deze draad ongeldig zijn. Een break statement in de code

wordt vervangen door het aanroepen van de stop functie van het raamwerk, die zal de

nodige controlevariabelen instellen om ervoor te zorgen dat eerdere draden hun iteraties

nog uitvoeren en latere draden stoppen. Er zijn verschillende manieren om de code aan

te passen met behulp van de functies van het raamwerk, gedefineerd door de volgende

interface:

void write(params_struct* params, void* address, void* data, int size);

int read(params_struct* params, int* address, int size);

void* getReadOnlyPtr(params_struct* params, void* address, int size);

void* getWriteFirstPtr(params_struct* params, void* address, int size);

void* getReadAndWritePtr(params_struct* params, void* address, int size);

We kunnen de lees- en schrijfoperaties omleiden langs het raamwerk door het aan-

roepen van de read of write functie met de nodige parameters. Bij elke operatie geven

we de controlestructuur mee, het (originele) geheugenadres van de operatie, de grootte

van de geheugenregio waarop de operatie betrekking heeft, en in geval van een schrijf-

operatie de weg te schrijven data. De speculatieve luscode ziet er dan als volgt uit:

params_struct* specloop(params_struct* params, int is, int ie) {

int tmp;

for (int i = is; i<ie && *params->global_brk_thread > params->id;

i += params->ss) {

tmp = read(params, yptr, sizeof(*y)) + z;

write(params, x[i], &tmp, sizeof(x[i]));

}

}

Om de overhead te beperken kunnen we echter ook een pointer opvragen aan ons

raamwerk naar die adressen, die we dan bij meerdere lees- en/of schrijfoperaties kunnen

27

Page 36: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

gebruiken. Dit kunnen we doen met behulp van de ptr-functies van het raamwerk. Er

zijn verschillende types pointers voorzien, die het raamwerk helpen bij het beperken van

de mogelijke conflicten. Als een pointer enkel gebruikt wordt om het adres te lezen, dient

de getReadOnlyPtr methode gebruikt te worden, zo wordt aan het raamwerk duidelijk

gemaakt dat de pointer niet voor schrijfoperaties gebruikt zal worden. Wordt het adres

geschreven, waarna eventueel de weggeschreven data terug gelezen wordt, dan dient de

getWriteFirstPtr methode gebruikt te worden. Voor toegangspatronen die daar niet

aan voldoen is er de getReadAndWritePtr methode, dit zijn dus adressen die zowel

gelezen als geschreven worden, met als eerste een leesoperatie. Er mogen meermaals

verschillende types pointers opgevraagd worden naar dezelfde adressen, het raamwerk

zal ervoor zorgen dat ze naar dezelfde lokale kopie wijzen om een consistente uitvoering

binnen eenzelfde draad te garanderen. De enige vereiste die voldaan moet worden is

dat de pointers enkel gebruikt worden volgens de vereisten van de functie waarmee

ze opgevraagd zijn. Wordt een pointer opgevraagd met de getReadOnlyPtr functie

bijvoorbeeld ook gebruikt voor schrijfoperaties, dan kunnen er conflicten ontstaan die

het raamwerk niet zal detecteren.

In principe kan men voor elk adres een pointer opvragen met de getReadAndWritePtr

methode om zeker geen restricties te schenden, maar dit zal leiden tot het detecteren

van heel wat valse conflicten, omdat alle gebruikte adressen dan als zowel gelezen als

geschreven beschouwd worden. Zowel voor de efficientie als voor de correcte werking

van het raamwerk dient dus met de juiste pointers gewerkt te worden. Gezien het

raamwerk de bedoeling heeft om door een compiler gebruikt te worden, en die laatste

de juiste functie-oproepen automatisch toevoegd, zal dit geen problemen opleveren. De

functie met de speculatieve luscode ziet er dan als volgt uit:

params_struct* specloop(params_struct* params, int is, int ie) {

for (int i = is; i<ie && *params->global_brk_thread > params->id;

i += params->ss) {

xptr = getWriteFirstPtr(params, x[i], sizeof(*x));

yptr = getReadOnlyPtr(params, y, sizeof(*y));

*xptr = *yptr + z;

}

28

Page 37: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

}

De pointerfuncties laten toe de granulariteit van operaties te vergroten om zo het

aantal oproepen naar het raamwerk nog te verkleinen, en dus de overhead te beperken.

Zeker in geval van lussen waar over alle elementen geıtereerd wordt komt dit van pas,

omdat zo heel veel oproepen geelimineerd kunnen worden, en door het feit dat elk ele-

ment gebruikt wordt leidt het niet tot de detectie van valse conflicten. In ons voorbeeld

hebben we zo’n situatie: we kunnen het opvragen van de verschillende gebruikte ele-

menten van de x array groeperen en in een keer opvragen buiten de lus. Ook kunnen we

het opvragen van de pointer naar de y variabele verplaatsen naar buiten de lus, zodat

dat maar een keer gedaan wordt:

params_struct* specloop(params_struct* params, int is, int ie) {

xptr = getWriteFirstPtr(params, x[is], sizeof(*x) * (ie - is)

/ params->ss);

yptr = getReadOnlyPtr(params, y, sizeof(*y));

for (int i = is; i<ie && *params->global_brk_thread > params->id;

i += params->ss) {

*xptr = *yptr + z;

}

}

De pointerfuncties zijn alleen compatibel met het werken met lokale kopieen in het

raamwerk, omdat er dan enkel geweten moet zijn welke adressen gelezen en geschreven

worden, zonder het tijdstip van elke operatie te kennen. Bij het werken met gedeelde

data is dit niet het geval, dan moet het raamwerk wel elke operatie registreren, waardoor

elke geheugentoegang dus naar de read of write functie van het raamwerk moet worden

omgeleid.

Het oproepen van het raamwerk hoeft niet voor alle geheugentoegangen, enkel voor

locaties die mogelijk afhankelijkheden tussen de iteraties introduceren. In het voorbeeld

zijn dat zowel de array als het adres waar de y pointer naar wijst. De z variabele wordt

met zekerheid in geen enkele iteratie geschreven dus kan die geen conflict opleveren en

mag dus gewoon gebruikt worden.

29

Page 38: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Op de plaats waar de originele lus stond doen we een oproep van de execute_loop

functie van het raamwerk, met als parameters de startwaarde, eindwaarde, en stap-

grootte van de iteratievariabele, de gewenste intervalgrootte tussen synchronisaties, het

aantal gewenste speculatieve draden, en een functiepointer naar de speculatieve luscode.

In dit geval dus:

execute_loop(0,1000,1,1000,2,&specloop);

Deze functie garandeert een correcte uitvoering van de code, dus na deze oproep

kan verdergegaan worden met het verder uitvoeren van het programma.

4.2 Interne werking

In deze sectie bekijken we de interne werking van het raamwerk om een zicht te krijgen

op de overhead die het met zich meebrengt. We bekijken afzonderlijk de overhead van

het werken met gedeelde data en van het werken met lokale data. Voor het aanmaken

en opstarten van draden maakt het raamwerk gebruik van POSIX threads. [8]

4.2.1 Gedeelde data

Controledata

Er wordt een map met locks voor verschillende adressen gedeeld tussen de draden.

Telkens een nieuw adres gebruikt wordt, wordt er een nieuwe lock toegevoegd. De locks

worden genomen wanneer er operaties met dat adres uitgevoerd worden.

Elke draad bezit een map met voor elk gebruikt adres informatie over het tijdstip

van schrijf en leesoperaties, en een kopie van de originele data indien deze draad de

eerste was die het adres geschreven heeft.

30

Page 39: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Geheugenoperaties

De read functie

1. Er wordt bepaald of er in de controlestructuur al een element zit voor dit adres,

met andere woorden of dit de eerste keer is dat deze draad dat adres gebruikt.

2. Is er nog geen controledata, dan wordt die aangemaakt en wordt er gecontroleerd

of er al een lock voor dit adres bestaat.

3. Bestaat er nog geen lock voor het adres, dan betekent dit dat dit de allereerste

toegang naar dit adres is door alle draden samen. Het adres bevat dus nog de ori-

ginele waarde van voor de speculatieve uitvoering, deze waarde wordt gekopieerd

naar de controledata. Er wordt ook een lock aangemaakt voor dat adres, die door

de verschillende draden gedeeld wordt.

4. Er wordt een lock op het adres genomen, de data wordt uit het adres gelezen, en

het tijdstip wordt vastgelegd, waarna de lock weer vrijgegeven wordt.

5. Het tijdstip wordt bewaard in het last read attribuut van de controlestructuur,

en in het first read attribuut als die nog geen tijdstip bevat, aangezien dit dan de

eerste leesoperatie naar dit adres is. Op die manier wordt verzekerd dat voor elk

adres waarvan deze draad gelezen heeft, de tijdstippen van zowel de eerste als de

laatste leesoperatie beschikbaar zijn.

6. De gelezen data wordt teruggeven als returnwaarde.

De write functie

1. De write operatie controleert of er in de controlestructuur al een element zit voor

dit adres.

2. Is er nog geen element, dan wordt dat gealloceert en geınitialiseerd, en wordt er

gecontroleerd of er al een lock voor dit adres bestaat.

31

Page 40: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

3. Bestaat er nog geen lock voor het adres, dan betekent dit dat dit de allereerste

toegang naar dit adres is door alle draden samen. Het adres bevat dus nog de ori-

ginele waarde van voor de speculatieve uitvoering, deze waarde wordt gekopieerd

naar de controledata. Er wordt ook een lock aangemaakt voor dat adres, die door

de verschillende draden gedeeld wordt.

4. De bij het adres horende lock wordt genomen, de data wordt naar het adres

geschreven, en het tijdstip wordt vastgelegd, waarna de lock weer vrijgegeven

wordt.

5. Het tijdstip wordt bewaard in het last write attribuut van de controlestructuur,

en in het first write attribuut als die nog geen waarde bevat, aangezien dit dan

de eerste schrijfoperatie naar dit adres is. Op die manier wordt verzekerd dat

voor elk adres waarnaar deze draad geschreven heeft, de tijdstippen van zowel de

eerste als de laatste schrijfoperatie beschikbaar zijn.

Het vastgelegde tijdstip is geen absolute tijd maar logische tijd. Er wordt een teller

gedeeld tussen de draden. Het bepalen van het tijdstip van een operatie gebeurt door die

teller te verhogen en de waarde op te slaan bij elke operatie, in een atomische operatie.

Zo kunnen we met absolute zekerhied de volgorde van de operaties achterhalen.

Bij het werken op gedeelde data kunnen de functies voor het opvragen van poin-

ters aan het raamwerk niet geımplementeerd worden. Het raamwerk moet immers het

tijdstip van elke lees en schrijfopertie kunnen vastleggen, waarvoor bij elke operatie het

raamwerk gecontacteerd moet worden. Bij het werken met pointers is dit niet het geval,

en voert de speculatieve code operaties uit zonder dat het raamwerk weet wanneer die

precies gebeuren.

Synchronisatie van de draden

Elke draad voert zijn toegekend aantal iteraties uit van de speculatieve loopcode door

het aanroepen van de speculatieve loopfunctie met de start en stop iteratie.

32

Page 41: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Draad 0:

1. De draad geeft zijn map met gebruikte adressen en bijhorende controledata door

aan draad 1.

2. De draad wacht op het voltooien van de synchronisatie door de andere draden.

Andere draden:

1. Er wordt gewacht op de sychronisatiedata van de vorige draad.

2. Er wordt gecontroleerd of synchronisatie nog nodig is. Wanneer er door een

lagere draad reeds een conflict is gedetecteerd, heeft dit geen zin meer. In dat

geval worden de originele waarden van geheugenlocaties die deze draad als eerste

gebruikt heeft hersteld.

3. Is er wel synchronisatie nodig, dan wordt naar conflicten gezocht door het verge-

lijken van de eigen controledata met de data van vorige draden, en wordt de eigen

data daaraan toegevoegd voor de volgende draden.

4. De volgende draad wordt wakker gemaakt en er wordt doorgegeven of er al dan

niet een conflict gedetecteer is.

4.2.2 Lokale kopieen

Controledata

Informatie over geschreven adressen en hun lokale kopieen wordt bijgehouden in een set

die instanties bevat van de controlestructuur. Die houdt bij wat het orginele adres is,

wat het adres van de lokale kopie is, en hoe groot de variabele is.

Er wordt ook een set bijgehouden van adresregio’s die door de draad gelezen zijn

vooraleer ze geschreven werden.

Er is een synchronisatieset die gedeeld wordt tussen de draden, om hun controle-

structuren van geschreven locaties door te geven.

33

Page 42: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Geheugenoperaties

De getReadOnlyPtr functie: figuur 4.1

1. Er wordt bepaald of de gevraagde regio reeds lokaal bestaat.

2. Is dit het geval, dan wordt er berekend waar in de lokale data zich de kopie precies

bevindt, en wordt een pointer naar dat adres teruggegeven.

3. Is dit nog niet het geval, dan wordt er controledata geınitialiseerd om deze geheu-

gentoegang op te slaan als in de lijst van gelezen adressen die mogelijk een read

after write afhankelijkheid met een vorige draad kunnen schenden.

4. Er wordt gekeken of er reeds lokale kopieen bestaan van delen van de gevraagde

regio. Als die er zijn, dan wordt er nieuwe ruimte gealloceerd voor de volledige

gevraagde regio, en wordt de inhoud van de partiele lokale kopieen naar de juiste

plaats erin gekopieerd alvorens die te verwijderen. Dit is noodzakelijk omdat de

recentste versie van de hele gevraagde regio waarnaar een pointer wordt gegeven

contigu in het geheugen moet zitten, om pointerberekeningen binnen die regio toe

te laten. Het adres wordt bovendien geregistreerd als gelezen alvorens geschreven

te zijn, en dus mogelijk een afhankelijkheid schendend met een schrijfoperatie

ernaar in een vorige draad, vanwege de stukken eruit waarvan nog geen lokale kopie

bestond. Er wordt een pointer naar het begin van de nieuwe kopie teruggegeven.

5. Bestaan er nog geen pariele kopieen, dan is de regio in deze draad nog niet gewij-

zigd, en wordt er een pointer teruggegeven naar het originele adres vanwaar de

data gelezen mag worden. Ook hierbij wordt het adres geregisteerd als gelezen.

Deze functie bevat heel wat code, maar het grootste codeblok, blok 4, moet slechts

uitgevoerd worden wanneer er eerder al kleinere stukken van de opgevraagde regio

geschreven zijn. Dit komt niet voor wanneer er met een vaste granulariteit gewerkt

wordt, en het moet maximaal 1 keer uitgevoerd worden voor een regio, want er wordt

een grote regio van gemaakt die de volgende keer onmiddelijk teruggegeven kan worden.

34

Page 43: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 4.1: Control flow van de getReadOnlyPtr functie bij het werken met lokale

kopieen

35

Page 44: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 4.2: Control flow van de getWriteFirstPtr functie bij het werken met lokale

kopieen

36

Page 45: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

De getWriteFirstPtr functie: figuur 4.2

1. Er wordt bepaald of er reeds een lokale kopie bestaat van een regio waarin de

gevraagde regio volledig valt.

2. Bestaat die, dan wordt er berekend waar de gevraagde regio begint, en wordt er

een pointer naar teruggegeven.

3. Bestaat die niet, dan wordt die samen met de nodige controledata aangemaakt,

en worden reeds bestaande partiele kopieen van de regio verwijderd. De locatie

wordt geregistreerd als geschreven, en dus een afhankelijkheid creerend indien een

later draad dit adres leest. Een pointer naar de lokale kopie wordt teruggegeven.

Ook hier geldt dat het grootste codestuk maximaal een keer uitgevoerd wordt, en

enkel wanneer er met een niet-consistente granulariteit gewerkt wordt.

De getReadAndWritePtr functie: figuur 4.3

1. Er wordt gezocht naar een lokale kopie van een regio waarin het gevraagde adres

valt.

2. Bestaat er reeds een lokale kopie, dan wordt er een pointer daarnaar teruggegeven.

3. Bestaat er nog geen lokale kopie, dan wordt die aangemaakt

4. Bestaan er reeds lokale kopieen van stukken van de opgevraagde regio, dan worden

die naar de nieuwe grote kopie gekopieerd en zelf verwijderd

5. De regio wordt geregistreerd als gelezen, en dus mogelijk afhankelijk van schrijf-

operaties in voorgaande draden, en ook als geschreven, en dus mogelijk een af-

hankelijkheid creerend met leesoperaties in volgende draden.

Ook in deze functie wordt het grootste codefragment maximum een keer uitgevoerd

voor een bepaalde regio, bij daaropvolgende oproepen wordt enkel code in blok 1 en 2

uitgevoerd.

37

Page 46: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 4.3: Control flow van de getReadAndWritePtr functie bij het werken met lokale

kopieen

38

Page 47: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Synchronisatie van de draden

Elke draad voert zijn iteraties uit door het aanroepen van de speculatieve lusfunctie

met de start en stop iteratie.

Draad 0:

1. De set met geschreven adressen wordt doorgegeven aan de volgende draad.

2. Draad 1 wordt wakker gemaakt om te synchroniseren.

3. Er wordt gewacht tot de synchronisatiedata, een set met alle geschreven locaties

en het adres van de actueelste kopie, door de laatste draad teruggegeven wordt.

4. De data in de lokale kopieen wordt gekopieerd naar de originel geheugenlocaties.

5. Er wordt gecontroleerd of er nog intervallen uitgevoerd moeten worden en hogere

draden worden daarover ingelicht.

Andere draden:

1. De draad wacht op de set met controledata van de geschreven adressen van de

vorige draden.

2. Er wordt gecontroleerd of synchronisatie van deze draad nodig is.

3. Is er synchronisatie nodig, dan worden read after write afhankelijkheden gedetec-

teerd, de enige soort afhankelijkheden die kunnen voorkomen bij het werken met

lokale kopieen. Dit gebeurt door het vergelijken van de adressen die door vorige

draden geschreven zijn met een lijst van adressen die deze draad gelezen heeft

voordat hij er zelf naar geschreven heeft.

4. Wordt er een conflict gedetecteerd, dan wordt aan de volgende draden doorgegeven

dat die niet moeten synchroniseren, en wordt draad 0 wakker gemaakt om de

synchronisatie tot en met de vorige draad te voltooien.

5. Waren er geen conflicten, dan voegt de draad de controledata van zijn geschreven

adressen toe aan de synchronisatieset, waarbij hij de data voor dezelfde adressen

geschreven door vorige draden overschrijft.

39

Page 48: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

6. Er wordt getest of er een break statement uitgevoerd is in de lus, met andere

woorden of de lus hier onverwacht beeindigd werd. Is dit het geval, dan wordt

doorgegeven aan de volgende draden om niet meer te synchroniseren en wordt

draad 0 verwittigd om de synchronisatie tot en met deze draad te voltooien.

7. De volgende draad wordt wakker gemaakt om te synchroniseren.

8. De draad wacht tot draad 0 aangeeft of er een volgend interval uitgevoerd moet

worden.

4.2.3 Beperkingen

De versie met gedeelde data kan niet overweg met overlappende geheugenregio’s. Er

moet dus voor een bepaalde locatie altijd met dezelfde granulariteit gewerkt worden.

Wanneer rekening gehouden moet worden met overlappingen, wordt het opzoeken van

de controledata voor een bepaalde locatie een stuk ingewikkelder, wat voor een stuk

meer overhead zorgt. Aangezien dit in de versie met gedeelde data bij elke operatie

moet gebeuren, lijkt dit niet aangewezen. Elke operatie naar een bepaalde locatie in

het geheugen moet dus hetzelfde begin- en eindadres hebben.

De versie die werkt op lokale data kan wel overweg met varierende granulariteit, op

voorwaarde dat een opgevraagde regio niet deels binnen en deels buiten een andere regio

valt. Een opgevraagde regio mag dus wel een stukje zijn van een eerder opgevraagde

grotere regio, of een grotere regio zijn waar een eerder opgevraagde kleinere locatie

volledig binnen valt. Het is echter aan te raden zoveel mogelijk dezelfde granulariteit

voor een bepaalde locatie te gebruiken, omdat het vergroten van de granulariteit leidt

tot het opnieuw alloceren van de nieuwe lokale kopie, en het kopieren van de inhoud

van de kleinere kopieen.

40

Page 49: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Tabel 4.1: De hardwareconfiguratie van de uitgevoerde testen

4.3 Resultaten

De prestatie van het raamwerk werd op volgend codefragment getest:

for(int i=0; i<writes; i++) {

for(int j=0; j<reads; j++) {

*a[i] += b[j] * c[i];

}

}

Er is een array a met pointers naar variabelen waarmee een berekening wordt uitgevoerd,

en waarnaar het resultaat weggeschreven wordt. Door deze pointers te veranderen

kunnen we conflicten genereren. De reads en de writes variabele geven het aantal

iteraties van respectievelijk de buitenste en de binnenste lus aan, en bepalen de grootte

van de arrays. We testen twee versies: een waarbij de arrays statisch gedeclareerd zijn,

en een waarbij ze dynamisch gedeclareerd zijn.

Dit zijn de technische specificaties van de machine waarop alle testen zijn uitgevoerd

is weergegeven in tabel 4.1.

4.3.1 Gedeelde data

Bij het werken met gedeelde data moeten we de read en de write functies van het

raamwerk gebruiken, we krijgen dan de volgende speculatieve code:

41

Page 50: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 4.4: De uitvoeringstijd zonder conflicten bij het werken met gedeelde data

params_struct* specloop(params_struct* params, int is, int ie) {

int tmp;

for(int i=is; i<ie; i++) {

for(int j=0; j<READS; j++) {

tmp = read(params,a[i], 4) +

read(params,&b[j],4) * read(params,&c[i],4);

write(params,a[i],&tmp,4);

}

}

return params;

}

Resultaat

Het resultaat toont meteen aan dat het werken met gedeelde data veel te traag is.

Het oproepen van het raamwerk voor elke geheugenoperatie levert een veel te grote

42

Page 51: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

overhead op. Het aantal uit te voeren instructies per iteratie wordt verveelvuldigd. Die

overhead kan enkel verminderd worden door niet alle operaties te moeten registreren,

en geen locks te moeten nemen bij het uitvoeren van geheugenoperaties. Deze twee

zaken kunnen enkel verwezenlijkt worden door te werken met lokale kopieen van de

data omdat dan het tijdstip van elke operatie niet geweten moet zijn om conflicten

te kunnen detecteren en de data dan zonder locks gelezen en geschreven kan worden

omdat elke draad op een eigen kopie werkt.

4.3.2 Lokale data

Voor het testen van het werken met lokale data kunnen we gebruik maken van de

pointerfuncties. We vertrekken van volgende speculatieve luscode:

params_struct* specloop(params_struct* params, int is, int ie) {

int* bptr = (int*)getReadOnlyPtr(params, b, sizeof(b));

int* cptr = (int*)getReadOnlyPtr(params, &c[is], sizeof(int) * (ie-is));

for(int i=is; i<ie; i++) {

int* aptr = (int*)getReadAndWritePtr(params, (void*)a[i], sizeof(int));

for(int j=0; j<READS; j++) {

*aptr += bptr[j] * cptr[i];

}

}

return params;

}

Hierbij werken we met grotere granulariteit om minder pointers te moeten opvragen.

Het resultaat van de testen met gedeelde data hebben immers uitgewezen dat voor elke

operatie het raamwerk oproepen een veel te grote overhead oplevert. Voor de b array

kan de grotere granulariteit geen valse conflicten opleveren aangezien hij in elke iteratie

volledig gelezen wordt. Voor de c array vragen we enkel een pointer naar het gedeelte

dat in de draad gebruik wordt, dus ook hier levert de grotere granulariteit geen valse

conficten op.

43

Page 52: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 4.5: De uitvoeringstijd zonder conflicten bij het werken met lokale kopieen van

de versie met statische datastructuren

Parallellisatie van de statische versie

Figuur 4.5 vergelijkt de uitvoeringstijd van de sequentiele uitvoering met statische data

en speculatieve versies met een verschillend aantal draden voor 10000 iteraties van de

buitenste lus en verschillend aantal iteraties van de binnenste lus.

We zien dat er meer tijdswinst geboekt wordt naarmate de binnenste lus groter

wordt, wat logisch is, gezien het relatieve aandeel van de speculatieve overhead dan

verkleint.

Voor een verschillend aantal draden zien we weinig verschil, vanwege het uitvoeren

van de testen op een processor met twee kernen. Met meer dan twee draden kan nog

een kleine tijdswinst geboekt worden door een andere draad uit te voeren wanneer op

het geheugen gewacht moet worden om zo geheugenlatentie te verbergen, maar aan de

andere kant zorgen meerdere draden ook voor meer synchronisatieoverhead.

44

Page 53: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 4.6: De uitvoeringstijd zonder conflicten bij het werken met lokale kopieen van

de versie met dynamische datastructuren

Parallellisatie van dynamische versie

De uitvoeringstijd van de sequentiele uitvoering met dynamische data en speculatieve

versies met een verschillend aantal draden voor 10000 iteraties van de buitenste lus en

verschillend aantal iteraties van de binnenste lus wordt weergegeven in figuur 4.6.

De tijdswinst is hier groter dan bij de versie met statische data. Dit komt doordat het

raamwerk de lokale kopieen dynamisch alloceert, en dynamisch gealloceerd geheugen

een hogere toegangstijd heeft dan statisch gealloceerd geheugen. Daardoor is er in

de vorige sectie een extra vertraging van de speculatief parallelle code die werkt met

dynamisch gealloceerde kopieen, ten opzichte van de sequentiele code die werkt met

statisch gealloceerde data. In dit geval werkt de sequentiele code ook met dynamisch

geheugen, waardoor die ook die extra vertraging heeft. Ook hier is er geen merkbaar

verschil tussen de verschillende aantallen speculatieve draden, vanwege het uitvoeren

op een processor met twee kernen.

Bij de resterende testen werken we telkens met twee draden en de resultaten zijn

45

Page 54: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 4.7: De uitvoeringstijd zonder conflicten met twee speculatieve draden voor

verschillende intervalgroottes

gebaseerd op de versie met dynamisch gealloceerde data.

Parallellisatie met intervallen

In de testen hiervoor werd telkens na het voltooien van de lus pas op conflicten gecon-

troleerd en de data gesynchroniseerd. Zoals beschreven in hoofdstuk 3 kan het nuttig

zijn om dit meermaals te doen na een bepaald aantal lusiteraties. Het interval waar-

na gesynchroniseerd moet worden kan meegegeven worden bij het aanroepen van de

execute functie van het raamwerk, en kan dus voor elk stuk speculatieve code anders

gekozen worden. De uitvoeringstijd van de testlus voor verschillende intervalgroottes is

weergegeven in figuur 4.7. We maken hierbij, net zoals in de rest van deze paragraaf,

gebruik van twee speculatieve draden.

We zien dat de uitvoeringstijd voor kleinere intervalgroottes slechts zeer licht toe-

neemt, en de overhead van het tussentijds synchroniseren dus nog meevalt. Dit komt

doordat dit deels gecompenseerd wordt door het feit dat er na elk interval minder

46

Page 55: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 4.8: De uitvoeringstijd bij het werken met lokale kopieen bij een conflict

synchronisatiewerk is dan bij een grote synchronisatie achteraf, omdat er per interval

minder verschillende adressen gebruikt worden en dus minder lokale kopieen te syn-

chroniseren zijn.

Parallellisatie met conflicten

Figuur 4.8 toont voor verschillende intervalgroottes het resultaat wanneer een specula-

tieve uitvoering met twee draden geconfronteerd wordt met een conflict. Wie zien de

uitvoeringstijd voor drie verschillende conflicten: een in het begin van de uitvoering, een

ongeveer in het midden van de uitvoering, en een in het laatste interval. We zien dat

de uitvoeringstijd afneemt voor kleinere intervalgroottes, maar bij hele kleine groottes,

wanneer er te veel gesynchroniseerd wordt, terug licht toeneemt.

Hieruit blijkt dus dat het werken met relatief kleine intervallen voordelig is: de

bijkomende overhead is klein, conflicten worden sneller gedetecteerd en er moet minder

code opnieuw uitgevoerd worden.

47

Page 56: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

4.3.3 Conclusie

Uit de resultaten blijkt dat het werken met lokale kopieen van de data de efficientste

manier is om speculatief code uit te voeren. Het laat toe niet elke operatie te moeten

registreren, en een grotere granulariteit te gebruiken.

Het raamwerk werkt met dynamisch gealloceerde kopieen, waardoor het een extra

vertraging introduceert bij het parallelliseren van programma’s die werken met statisch

gealloceerde data. Daarom is de snelheidswinst groter bij programma’s die werken met

dynamisch gealloceerde data.

Een kleine intervalgrootte blijkt voordelig te zijn. Tussentijdse synchronisaties le-

veren geen al te grote overhead op, maar zorgen wel voor een snellere conflictdetectie.

48

Page 57: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Hoofdstuk 5

Parallellisatie van de mcf

benchmark

5.1 MCF benchmark

De 429.mcf benchmark is afgeleid van een programma voor het plannen van busroutes.

Het berekent een minimale kost scenario op basis van een gewogen graaf.[9]

De belangrijkste functie van deze benchmark is de primal_net_simplex functie,

die verantwoordelijk is voor 54.7% van de uitvoeringstijd. Deze wordt schematisch

weergegeven in figuur 5.1. Daar wordt in een lus het gekozen schema geoptimaliseerd

tot er een optimum bereikt is (deze iteraties noemen we het a-blok), en elke 200 iteraties

wordt bovendien het potentieel van de knopen van de graaf herberekend (deze code

noemen we het b-blok). Het herberekende potenieel uit het b-blok wordt in de knopen

van de graaf opgeslagen, maar blijkt telkens uit louter silent stores te bestaan. Silent

stores zijn schrijfoperaties die data wegschrijven die gelijk is aan de data die reeds op

die plaats in het geheugen zat, en dus eigenlijk niets veranderen.[10] Daardoor kunnen

we het b-blok parallel met het daaropvolgende a-blok uitvoeren. Het a-blok verandert

echter wel zaken aan de data, waardoor die veranderingen gescheiden moeten worden

van de originele data zodat die de uitvoering van het b-blok, dat op de data van het

vorige a-blok werkt, niet beınvloedt. Dit kunnen we gebruiken om de overhead van

49

Page 58: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

ons speculatieve raamwerk te testen bij het parallel uitvoeren van verschillende stukken

code, het werken met een lokale kopie van de data, en het synchroniseren daarvan met

de originele data.

5.2 Parallellisatie

Het a-blok en het b-blok worden beiden in een nieuwe functie geplaatst. De variabelen

die geınitialiseerd worden voor de lus en afhankelijkheden hebben tussen verschillende

iteraties, worden in een afzonderlijke structuur buiten de functie geplaatst, zodat die

bereikbaar zijn vanuit de nieuw aangemaakte functies.

5.2.1 Het a-blok

Het a-blok wordt in een functie spec_a geplaatst, waar het wordt aangepast om alle

lees- en schrijfoperaties naar knopen van de graaf om te leiden naar het speculatieve

raamwerk. We maken daarvoor gebruik van de hulpklasse node_t_ptr, die alle operaties

die op variabelen van het node_t type worden toegepast implementeert met de nodige

functieoproepen naar het raamwerk. Van alle functies die worden opgeroepen vanuit het

a-blok worden speculatieve versies aangemaakt waarbij de node_t variabelen veranderd

worden in node_t_ptr variabelen.

Bij het aanmaken van een node_t_ptr, of het veranderen van de node waar hij

naar wijst, wordt door de hulpklasse aan het raamwerk een nieuwe pointer naar de

bijhorende lokale kopie opgevraagd met de functie getReadAndWritePtr, waarna die

pointer bewaard wordt en gebruikt wordt in de rest van de speculatieve uitvoering.

5.2.2 Het b-blok

Het b-blok in de functie spec_refresh_potential kan vrijwel onaangepast blijven,

enkel op de plaatsen waar het potentieel van een knoop weggeschreven wordt, moet

gecontroleerd worden of het om silent stores gaat.

50

Page 59: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 5.1: De originele primal net simplex functie

51

Page 60: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

5.2.3 De primal net simplex functie

Op de plaats waar de oorspronkelijke luscode stond wordt er een array aangemaakt met

pointers naar de twee speculatieve functies. Het a-blok wordt eerst 1 keer afzonderlijk

uitgevoerd, daarna wordt de execute_code functie van het raamwerk aangeroepen met

de array van pointers. Het raamwerk zal er voor zorgen dat het b-blok telkens parallel

uitgevoerd wordt met het volgende a-blok, tot de functie eindigt, wat betekent dat de

lus voltooid is. Daarna eindigt de primal_net_simplex functie onveranderd.

5.2.4 Resultaat

De benchmark is nu speculatief geparallelliseerd met zo weinig mogelijk ingrepen of

specifieke optimalisaties, wat de bedoeling is bij een proces dat automatiseerbaar moet

zijn. Het resultaat laat echter te wensen over, de uitvoeringstijd neemt maar liefst

toe met een factor 7 bij het uitvoeren van de train input. Welke functie hoeveel tijd

in beslag neemt voor het uitvoeren van de train input wordt weergegeven in tabel

5.1. We zien hier duidelijk een enorme overhead van de getReadAndWritePtr functie

van het raamwerk, die 72% van de uitvoeringstijd in beslag neemt, en we zien dat de

speculatieve uitvoering van het b-blok in de spec_refresh_potential functie niet veel

tijd in beslag neemt.

In de volgende sectie beschrijven we aanpassingen om de uitvoeringstijd te verbete-

ren.

5.3 Optimalisaties in de speculatieve uitvoering

Uit voorgaand resultaat is duidelijk dat enkel de noodzakelijke functie oproepen in-

voeren voor het gebruik van speculatief raamwerk niet volstaat om snelheidswinst te

boeken met het parallelliseren van deze code. Er zijn verbeteringen nodig. We proberen

de prestatie te verbeteren door de speculatieve uitvoering wat aan te passen.

Deze versie had met zeer veel onnodige overhead te kampen. Zoals we in de resul-

taten kunnen zien neemt de speculatieve b-functie weinig tijd in beslag. Dit betekent

52

Page 61: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Tabel 5.1: Tijdsverdeling tussen de belangrijkste methodes

dat na voltooien van elk b-blok, het a-blok nog lange tijd aan het werk is, en dat op een

speculatieve manier doet terwijl het de enige actieve draad is. Dit betreft eigenlijk het

overgrote deel van de uitvoering van het a-blok. Het volgende b-blok starten kunnen

we nog niet, omdat die afhankelijk is van de wijzigingen die het nog lopende a-blok

aanbrengt. We kunnen er wel het a-blok op sequentiele code laten overschakelen wan-

neer het b-blok voltooit, om zo vele nutteloze oproepen van de getReadAndWritePtr

te vermijden. Daarom hebben we een synchronisatievlag aangemaakt, die na het uit-

voeren van het b-blok ingesteld wordt, en bij elke iteratie van het a-blok gecontroleerd

wordt. Wanneer die voldaan is, wordt de data van het a-blok gesynchroniseerd door het

raamwerk, dus worden de waarden van de lokale kopiees naar de oorspronkelijke loca-

ties gekopieerd, en wordt er verder sequentiele code uitgevoerd. Wanneer het volgende

b-blok kan beginnen wordt dan weer overgeschakeld op speculatieve code. De control

flow van de speculatieve code ziet er dan uit als in figuur 5.2.

Om de overhead van de getReadAndWritePtr functie nog wat verder te beperken

vereenvoudigen we de code. Aangezien er altijd pointers naar knopen opgevraagd wor-

den aan het raamwerk, die altijd dezelfde grootte hebben, hebben we niet te maken met

overlappende adresregio’s, en kunnen we dus het zoeken naar een lokale kopie baseren

op enkel het beginadres. Verder gebruiken we nu ook een boom-structuur voor het

opzoeken van geheugenlocaties, in plaats van een set.

53

Page 62: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Figuur 5.2: Control flow van de speculatieve primal net simplex functie

54

Page 63: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

5.4 Resultaat

Ook nu loopt de benchmark voor de train input nog zo’n 4% trager dan het origineel.

Het resultaat is nog steeds slecht, maar wel een grote verbetering ten opzichte van het

vorige resultaat.

Hieruit moeten we echter concluderen dat er codetransformaties en optimalisaties

noodzakelijk zijn voor het creeren van code die speculatief parallel kan presteren. Het

simpelweg gebruiken van een speculatief raamwerk volstaat niet. In het voorbeeld

van de mcf benchmark zit de geparallelliseerde code vol pointeroperaties, waarbij vaak

geıtereeerd wordt over zeer veel elementen, met als gevolg zeer veel functieoproepen

naar het raamwerk, voor zeer veel verschillende adressen die allemaal lokaal gekopieerd

moeten worden. Zonder aanpassingen aan de originele mcf code kan dat niet op een

winstgevende manier speculatief geparallelliseerd worden.

De uitvoeringstijd van de benchmark zonder het b-blok bedraagt voor de train

input 89% van de originele uitvoeringstijd. Dit is dus de maximale snelheidswinst die

we zouden kunnen verkijgen bij een parallellisatie van het a-blok en het b-blok zonder

overhead. Bij de speculatieve uitvoering hebben we dus een extra overhead van 15%.

De resultaten in hoofdstuk 4 hebben aangetoond dat het raamwerk wel goed kan

presteren. Er is verder onderzoek vereist naar hoe lussen getransformeerd moeten wor-

den om die snelheidswinst te kunnen behalen.

55

Page 64: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Hoofdstuk 6

Besluit

In deze masterproef hebben we het ontwerp van een softwarematig speculatief raamwerk

besproken en de mogelijkheden getest. Eerst werd er ingegaan op het waarom van

een softwarematig raamwerk voor speculatieve parallellisatie, en gekeken naar ander

wetenschappelijk werk rond deze kwestie.

Vervolgens werden hoofdzakelijk twee technieken besporken om zo’n raamwerk te

laten functioneren, namelijk het werken op gedeelde data en het werken op lokale ko-

pieen van de data. Daarna werden die technieken geımplementeerd en getest, waaruit

bleek dat het werken op lokale kopieen veruit de beste resultaten opleverd.

Het raamwerk werd ook uitgetest op de mcf-benchmark uit de SPEC CPU 2006

bibliotheek, waar het echter weinig resultaat op behaalde. Dit toonde aan dat het

raamwerk voor het parallelliseren van complexe code met veel pointeroperaties gecom-

bineerd moet worden met code transformaties en optimalisaties om de code geschikter

te maken voor speculatieve parallellisatie.

56

Page 65: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

Bibliografie

[1] H. Zhong, M. Mehrara, S. Lieberman, and S. Mahlke. Uncovering hidden loop

level parallelism in sequential applications. In 14th International Symposium on

High Performance Computer Architecture, pages 290–301. IEEE Computer Society,

2008.

[2] M. Bridges, N. Vachharajani, Y. Zhang, T. Jablin, and D. August. Revisiting the

sequential programming model for multi-core. In Proceedings of the 40th Annual

IEEE/ACM International Symposium on Microarchitecture, pages 69–84. IEEE

Computer Society, 2007.

[3] S. Balakrishnan and G. Sohi. Program demultiplexing: Data-flow based specula-

tive parallelization of methods in sequential programs. In Proceedings of the 33rd

annual international symposium on Computer Architecture, pages 302 – 313. IEEE

Computer Society, 2006.

[4] P. Papadimitriou and T. Mowry. Exploring thread-level speculation in software:

The effects of memory access tracking granularity. Technical report, 2001.

[5] M. Prabhu and K. Olukotun. Exposing speculative thread parallelism in spec2000.

In Proceedings of the tenth ACM SIGPLAN symposium on Principles and practice

of parallel programming, pages 142 – 152. ACM, 2005.

[6] Peter Rundberg and Per Stenstrom. An all-software thread-level data dependence

speculation system for multiprocessors. Journal of Instruction-Level Parallelism,

3:2002, 2001.

57

Page 66: Een raamwerk voor softwarematige speculatieve …lib.ugent.be/fulltxt/RUG01/001/418/379/RUG01-001418379...Een raamwerk voor softwarematige speculatieve parallellisatie door Tim Bruneel

[7] C. Oancea and A. Mycroft. Software thread-level speculation: an optimistic library

implementation. In Proceedings of the 1st international workshop on Multicore

software engineering, pages 23–32, 2008.

[8] David Butenhof. Programming with POSIX Threads. Addison-Wesley, 1997.

[9] Standard Performance Evaluation Corporation. Spec cpu 2006.

http://www.spec.org/cpu2006/.

[10] K. Lepak, G. Bell, and M. Lipasti. Silent stores and store value locality. IEEE

Transactions on Computers, 50:1174 – 1190, 2001.

58