<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ksharida</id>
	<title>Expertiza_Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ksharida"/>
	<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=Special:Contributions/Ksharida"/>
	<updated>2026-07-13T23:04:01Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.0</generator>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74613</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74613"/>
		<updated>2013-04-03T04:47:07Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Relax Write-to-Read program order */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Also, many programs provide sequentially consistent results even if the program order from a write to a read is not maintained.[[#References|&amp;lt;sup&amp;gt;[25]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program[[#References|&amp;lt;sup&amp;gt;[29]&amp;lt;/sup&amp;gt;]] that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code[[#References|&amp;lt;sup&amp;gt;[29]&amp;lt;/sup&amp;gt;]] works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the most restrictive because it prevents a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.[[#References|&amp;lt;sup&amp;gt;[26]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases as opposed to the previously mentioned 3 protocols:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;br /&gt;
#http://xenon.stanford.edu/~hangal/manovit_thesis.pdf&lt;br /&gt;
#http://www.cs.utah.edu/~rajeev/cs7820/pres/7820-12.pdf&lt;br /&gt;
#http://web.cecs.pdx.edu/~alaa/ece588/notes/mem-consistency.pdf&lt;br /&gt;
#http://www.cs.nyu.edu/~lerner/spring10/MCP-S10-Read06-ConsistencyTutorial.pdf&lt;br /&gt;
#http://rsim.cs.illinois.edu/~sadve/JavaWorkshop00/talk.pdf&lt;br /&gt;
#https://www.google.com/url?sa=t&amp;amp;rct=j&amp;amp;q=&amp;amp;esrc=s&amp;amp;source=web&amp;amp;cd=8&amp;amp;cad=rja&amp;amp;ved=0CGsQFjAH&amp;amp;url=http%3A%2F%2Ffaculty.kfupm.edu.sa%2FCOE%2Fmudawar%2Fcs282%2Flectures%2F09-Consistency.pps&amp;amp;ei=zZpbUaOkFozI9gTqzoDgCg&amp;amp;usg=AFQjCNF_vDcWurneM_hObF2iWZ-rxJKBHw&amp;amp;sig2=f9Qc1zCcwiosxiKKzyNO8Q&amp;amp;bvm=bv.44697112,d.eWU&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74612</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74612"/>
		<updated>2013-04-03T04:46:20Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* SPARC V8 Partial Store Ordering */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Also, many programs provide sequentially consistent results even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program[[#References|&amp;lt;sup&amp;gt;[29]&amp;lt;/sup&amp;gt;]] that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code[[#References|&amp;lt;sup&amp;gt;[29]&amp;lt;/sup&amp;gt;]] works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the most restrictive because it prevents a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.[[#References|&amp;lt;sup&amp;gt;[26]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases as opposed to the previously mentioned 3 protocols:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;br /&gt;
#http://xenon.stanford.edu/~hangal/manovit_thesis.pdf&lt;br /&gt;
#http://www.cs.utah.edu/~rajeev/cs7820/pres/7820-12.pdf&lt;br /&gt;
#http://web.cecs.pdx.edu/~alaa/ece588/notes/mem-consistency.pdf&lt;br /&gt;
#http://www.cs.nyu.edu/~lerner/spring10/MCP-S10-Read06-ConsistencyTutorial.pdf&lt;br /&gt;
#http://rsim.cs.illinois.edu/~sadve/JavaWorkshop00/talk.pdf&lt;br /&gt;
#https://www.google.com/url?sa=t&amp;amp;rct=j&amp;amp;q=&amp;amp;esrc=s&amp;amp;source=web&amp;amp;cd=8&amp;amp;cad=rja&amp;amp;ved=0CGsQFjAH&amp;amp;url=http%3A%2F%2Ffaculty.kfupm.edu.sa%2FCOE%2Fmudawar%2Fcs282%2Flectures%2F09-Consistency.pps&amp;amp;ei=zZpbUaOkFozI9gTqzoDgCg&amp;amp;usg=AFQjCNF_vDcWurneM_hObF2iWZ-rxJKBHw&amp;amp;sig2=f9Qc1zCcwiosxiKKzyNO8Q&amp;amp;bvm=bv.44697112,d.eWU&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74611</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74611"/>
		<updated>2013-04-03T04:45:30Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Relax Write-to-Write program order */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Also, many programs provide sequentially consistent results even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program[[#References|&amp;lt;sup&amp;gt;[29]&amp;lt;/sup&amp;gt;]] that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code[[#References|&amp;lt;sup&amp;gt;[29]&amp;lt;/sup&amp;gt;]] works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the most restrictive because it prevents a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases as opposed to the previously mentioned 3 protocols:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
Further reading :[[#References|&amp;lt;sup&amp;gt;[27]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;br /&gt;
#http://xenon.stanford.edu/~hangal/manovit_thesis.pdf&lt;br /&gt;
#http://www.cs.utah.edu/~rajeev/cs7820/pres/7820-12.pdf&lt;br /&gt;
#http://web.cecs.pdx.edu/~alaa/ece588/notes/mem-consistency.pdf&lt;br /&gt;
#http://www.cs.nyu.edu/~lerner/spring10/MCP-S10-Read06-ConsistencyTutorial.pdf&lt;br /&gt;
#http://rsim.cs.illinois.edu/~sadve/JavaWorkshop00/talk.pdf&lt;br /&gt;
#https://www.google.com/url?sa=t&amp;amp;rct=j&amp;amp;q=&amp;amp;esrc=s&amp;amp;source=web&amp;amp;cd=8&amp;amp;cad=rja&amp;amp;ved=0CGsQFjAH&amp;amp;url=http%3A%2F%2Ffaculty.kfupm.edu.sa%2FCOE%2Fmudawar%2Fcs282%2Flectures%2F09-Consistency.pps&amp;amp;ei=zZpbUaOkFozI9gTqzoDgCg&amp;amp;usg=AFQjCNF_vDcWurneM_hObF2iWZ-rxJKBHw&amp;amp;sig2=f9Qc1zCcwiosxiKKzyNO8Q&amp;amp;bvm=bv.44697112,d.eWU&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74610</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74610"/>
		<updated>2013-04-03T04:43:04Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Relax Write-to-Read program order */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Also, many programs provide sequentially consistent results even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program[[#References|&amp;lt;sup&amp;gt;[29]&amp;lt;/sup&amp;gt;]] that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code[[#References|&amp;lt;sup&amp;gt;[29]&amp;lt;/sup&amp;gt;]] works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the most restrictive because it prevents a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases as opposed to the previously mentioned 3 protocols:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;br /&gt;
#http://xenon.stanford.edu/~hangal/manovit_thesis.pdf&lt;br /&gt;
#http://www.cs.utah.edu/~rajeev/cs7820/pres/7820-12.pdf&lt;br /&gt;
#http://web.cecs.pdx.edu/~alaa/ece588/notes/mem-consistency.pdf&lt;br /&gt;
#http://www.cs.nyu.edu/~lerner/spring10/MCP-S10-Read06-ConsistencyTutorial.pdf&lt;br /&gt;
#http://rsim.cs.illinois.edu/~sadve/JavaWorkshop00/talk.pdf&lt;br /&gt;
#https://www.google.com/url?sa=t&amp;amp;rct=j&amp;amp;q=&amp;amp;esrc=s&amp;amp;source=web&amp;amp;cd=8&amp;amp;cad=rja&amp;amp;ved=0CGsQFjAH&amp;amp;url=http%3A%2F%2Ffaculty.kfupm.edu.sa%2FCOE%2Fmudawar%2Fcs282%2Flectures%2F09-Consistency.pps&amp;amp;ei=zZpbUaOkFozI9gTqzoDgCg&amp;amp;usg=AFQjCNF_vDcWurneM_hObF2iWZ-rxJKBHw&amp;amp;sig2=f9Qc1zCcwiosxiKKzyNO8Q&amp;amp;bvm=bv.44697112,d.eWU&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74609</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74609"/>
		<updated>2013-04-03T04:39:41Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Also, many programs provide sequentially consistent results even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the most restrictive because it prevents a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases as opposed to the previously mentioned 3 protocols:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;br /&gt;
#http://xenon.stanford.edu/~hangal/manovit_thesis.pdf&lt;br /&gt;
#http://www.cs.utah.edu/~rajeev/cs7820/pres/7820-12.pdf&lt;br /&gt;
#http://web.cecs.pdx.edu/~alaa/ece588/notes/mem-consistency.pdf&lt;br /&gt;
#http://www.cs.nyu.edu/~lerner/spring10/MCP-S10-Read06-ConsistencyTutorial.pdf&lt;br /&gt;
#http://rsim.cs.illinois.edu/~sadve/JavaWorkshop00/talk.pdf&lt;br /&gt;
#https://www.google.com/url?sa=t&amp;amp;rct=j&amp;amp;q=&amp;amp;esrc=s&amp;amp;source=web&amp;amp;cd=8&amp;amp;cad=rja&amp;amp;ved=0CGsQFjAH&amp;amp;url=http%3A%2F%2Ffaculty.kfupm.edu.sa%2FCOE%2Fmudawar%2Fcs282%2Flectures%2F09-Consistency.pps&amp;amp;ei=zZpbUaOkFozI9gTqzoDgCg&amp;amp;usg=AFQjCNF_vDcWurneM_hObF2iWZ-rxJKBHw&amp;amp;sig2=f9Qc1zCcwiosxiKKzyNO8Q&amp;amp;bvm=bv.44697112,d.eWU&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74608</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74608"/>
		<updated>2013-04-03T04:35:14Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Also, many programs provide sequentially consistent results even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the most restrictive because it prevents a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases as opposed to the previously mentioned 3 protocols:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;br /&gt;
#http://xenon.stanford.edu/~hangal/manovit_thesis.pdf&lt;br /&gt;
#http://www.cs.utah.edu/~rajeev/cs7820/pres/7820-12.pdf&lt;br /&gt;
#http://web.cecs.pdx.edu/~alaa/ece588/notes/mem-consistency.pdf&lt;br /&gt;
#http://www.cs.nyu.edu/~lerner/spring10/MCP-S10-Read06-ConsistencyTutorial.pdf&lt;br /&gt;
#http://rsim.cs.illinois.edu/~sadve/JavaWorkshop00/talk.pdf&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74607</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74607"/>
		<updated>2013-04-03T04:32:32Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Relax Write-to-Read program order */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Also, many programs provide sequentially consistent results even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the most restrictive because it prevents a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases as opposed to the previously mentioned 3 protocols:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74606</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74606"/>
		<updated>2013-04-03T04:30:48Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* IBM-370 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Furthermore, many applications function correctly (i.e., provide sequentially consistent results) even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the most restrictive because it prevents a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases as opposed to the previously mentioned 3 protocols:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74605</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74605"/>
		<updated>2013-04-03T04:29:52Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* SPARC V8 Partial Store Ordering */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Furthermore, many applications function correctly (i.e., provide sequentially consistent results) even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the strictest because it prohibits a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases as opposed to the previously mentioned 3 protocols:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74604</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74604"/>
		<updated>2013-04-03T04:27:28Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* SPARC V8 Partial Store Ordering */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Furthermore, many applications function correctly (i.e., provide sequentially consistent results) even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the strictest because it prohibits a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases:&amp;lt;br&amp;gt;&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74603</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74603"/>
		<updated>2013-04-03T04:26:56Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Relax Write-to-Write program order */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Furthermore, many applications function correctly (i.e., provide sequentially consistent results) even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the strictest because it prohibits a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
Even this model is not flexible enough for compiler optimizations.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
Extra hardware optimization enabled by PSO in addition to the previous set of models is that writes to different locations from the same processor can be pipelined or overlapped and are allowed to reach memory or other cached copies out of program order. PSO and TSO have the same atomicity requirements by allowing a processor to read the value of its own write early, and preventing a processor from reading the value of another processor’s write before the write is visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
This model can result in non sequentially consistent results in both of the below cases:&lt;br /&gt;
a)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Flag1=1                                  Flag2=1&lt;br /&gt;
    if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
       Enter Critical Section                   Enter Critical Section&lt;br /&gt;
b)&lt;br /&gt;
    P1                                       P2&lt;br /&gt;
    Data=2000                                while(Flag==0);&lt;br /&gt;
    Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
As a safety net between  a write and a following read, and to ensure atomicity of writes, same mechanism as TSO is used.&lt;br /&gt;
For maintaining order between two writes, PSO provides an instruction called STBAR (Barrier).&lt;br /&gt;
The safety net provided by PSO for imposing the program order from a write to a read, and for enforcing write&lt;br /&gt;
atomicity, is the same as TSO. PSO provides an explicit STBAR instruction for imposing program order between&lt;br /&gt;
two writes.&lt;br /&gt;
&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     P1                         P2&lt;br /&gt;
     a1:A=1                     a2:while(Flag==0)&lt;br /&gt;
     b1:B=1                     b2:u=A&lt;br /&gt;
     c1:Flag=1                  c2:v=B&lt;br /&gt;
&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74602</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74602"/>
		<updated>2013-04-03T04:05:18Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Relax Write-to-Read program order */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Furthermore, many applications function correctly (i.e., provide sequentially consistent results) even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
This method is not sufficiently flexible for compiler optimizations but successfully mask the latency of the write operation. Compilers tend to require reordering both wrt reads and writes.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the strictest because it prohibits a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
====''Differences in IBM370, TSO and PC''====&lt;br /&gt;
Consider the below code:&lt;br /&gt;
     a)                                       b)&lt;br /&gt;
     A = Flag1 = Flag2 = 0                    A = B = 0&lt;br /&gt;
     P1                   P2                  P1              P2                P3&lt;br /&gt;
     Flag1 = 1            Flag2 = 1           A = 1&lt;br /&gt;
     A = 1                A = 2                               if (A == 1)    &lt;br /&gt;
     register1 = A        register3 = A                       B = 1&lt;br /&gt;
     register2 = Flag2    register4 = Flag1                                     if (B == 1)&lt;br /&gt;
    &lt;br /&gt;
     Result: register1 = 1, register3 = 2,    Result: B = 1, register1 = 0&lt;br /&gt;
     register2 = register4 = 0&lt;br /&gt;
&lt;br /&gt;
TSO and PC allow the results in part a to occur because they let the reads of the flags to happen before the writes. It is not possible as per IBM370 as the read of A is not allowed on each processor till the write on that processor is done.&lt;br /&gt;
Similarly, given part b results are allowed as per PC but not by IBM370 or TSO.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:code2.jpg]]&amp;lt;br /&amp;gt;&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74601</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74601"/>
		<updated>2013-04-03T03:53:18Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Relax Write-to-Read program order */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Furthermore, many applications function correctly (i.e., provide sequentially consistent results) even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the strictest because it prohibits a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
Unlike IBM 370, the TSO model does not provide explicit safety net. But read-modify-write operations can be used to provide the illusion that program order is maintained between a write and a following read. Program order appears to be maintained if either the write or the read is already part of a read-modify-write or is replaced by a read-modify-write.&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
Even in PC model, there is no explicit safety net. Also the TSO approach to provide illusion of a safety net is enough in case of reads. But it cannot be used in case of writes.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:code2.jpg]]&amp;lt;br /&amp;gt;&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74600</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74600"/>
		<updated>2013-04-03T03:41:47Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Total Store Ordering (TSO) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Furthermore, many applications function correctly (i.e., provide sequentially consistent results) even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the strictest because it prohibits a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
a)&lt;br /&gt;
   P1                   P2                   &lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:u=A               b2:v=b&lt;br /&gt;
   c1:w=B               c2:x=A&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
   P3                   P4&lt;br /&gt;
   a1:A=1               a2:B=1               &lt;br /&gt;
   b1:C=1               b2:C=2&lt;br /&gt;
   c1:u=C               c2:v=C&lt;br /&gt;
   d1:w=B               d2:x=A&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:code2.jpg]]&amp;lt;br /&amp;gt;&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74599</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74599"/>
		<updated>2013-04-03T03:36:51Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Relax Write-to-Read program order */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the key program order optimization enabled by these models is to allow a read to be reordered with respect to previous writes from the same processor. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Furthermore, many applications function correctly (i.e., provide sequentially consistent results) even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a program that fails:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Flag1=1                                  Flag2=1&lt;br /&gt;
     if(Flag2==0)                             if(Flag1==0)&lt;br /&gt;
        Enter Critical Section                   Enter Critical Section&lt;br /&gt;
&lt;br /&gt;
This is because the reads are allowed to bypass writes. This can cause P1 to enter critical section without setting the flag. This can cause P2 also to enter critical section.&lt;br /&gt;
&lt;br /&gt;
However below code works well with this model:&lt;br /&gt;
     P1                                       P2&lt;br /&gt;
     Data=2000                                while(Flag==0);&lt;br /&gt;
     Flag=1                                   Read Data&lt;br /&gt;
&lt;br /&gt;
Since writes are not allowed to be reordered with respect to each other, this code works flawlessly.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The three models differ in when they allow a read to return the value of a write. Also, whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM 370 model is the strictest because it prohibits a read from returning the value of a write before the write is made visible to all&lt;br /&gt;
processors. Therefore, even if a processor issues a read to the same address as a previous pending write from&lt;br /&gt;
itself, the read must be delayed until the write is made visible to all processors.&lt;br /&gt;
&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
To enforce the program order constraint from a write to a following read, the IBM 370 model provides special serialization instructions that may be placed between the two operations. Some serialization instructions are special memory instructions that are used for synchronization (e.g., compare&amp;amp;swap), while others are non-memory instructions such as a branch. Referring back to the first example program in this section, placing a serialization instruction after the write on each processor&lt;br /&gt;
provides sequentially consistent results for the program even when it is executed on the IBM 370 model.&lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model partially relaxes the above system's requirement by allowing a read to return the value of its own processor’s write even before the write is serialized with respect to other writes to the same location. However, as with sequential consistency, a read is not allowed to return the value of another processor’s write until it is made visible to all other processors.&lt;br /&gt;
&lt;br /&gt;
The TSO model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
[[Image:Code_1.jpg]]&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Finally, the PC model relaxes both constraints, such that a read can return the value of any write before the write is serialized or made visible to other processors.&lt;br /&gt;
&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:code2.jpg]]&amp;lt;br /&amp;gt;&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74596</id>
		<title>CSC/ECE 506 Spring 2013/10c ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/10c_ks&amp;diff=74596"/>
		<updated>2013-04-03T02:09:55Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: Created page with &amp;quot;='''Memory Consistency models'''= == '''Introduction''' == The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;='''Memory Consistency models'''=&lt;br /&gt;
== '''Introduction''' ==&lt;br /&gt;
The memory consistency model of a shared-memory multiprocessor provides a formal specification of how the memory system will appear to the programmer, eliminating the gap between the behavior expected by the programmer and the actual behavior supported by a system. Effectively, the consistency model places restrictions on the values that can be returned by a read in a shared-memory program execution.&lt;br /&gt;
&lt;br /&gt;
This material gives a brief explanation about the intuition behind using relaxed memory consistency models for scalable design of multiprocessors. It also explains about the  consistency models in real multiprocessor systems like Digital Alpha, Sparc V9 , IBM Power PC and processors from Sun Microsystems.&lt;br /&gt;
&lt;br /&gt;
= '''Sequential Consistency Model (SC)''' =&lt;br /&gt;
=='''Introduction to Sequential Consistency Model'''==&lt;br /&gt;
[[Image:SCM.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
To write correct and efficient shared memory programs, programmers need a precise notion of shared memory semantics. To ensure correct execution, a programmer expects that the data value read should be the same as the latest value written to it in the system.&lt;br /&gt;
However in many commercial shared memory systems,the processor may observe an older value, causing unexpected behavior. Intuitively, a read should return the value of the &amp;quot;last&amp;quot; write to the same memory location. In uniprocessors, &amp;quot;last&amp;quot; is precisely defined by the sequential order specified by the program called '''program order'''. This is not the case in multiprocessors. A write and read of a variable, are not related by program order because they reside on two different processors.&lt;br /&gt;
&lt;br /&gt;
The uniprocessors model, however can be extented to apply to multiprocessors in a natural way. The resulting model is called '''Sequential consistency'''. In brief, sequential consistency requires that all memory operations &lt;br /&gt;
*appear to execute one at a time, &lt;br /&gt;
*all memory operations of a single processor appear to execute in the order described by that processor's program.&lt;br /&gt;
[[Image:SeqC.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
The figure above shows the basic representation for sequential consistency. This conceptual system for SC consists of n processors sharing a single logical memory. Though the figure does not show caches, an SC implementation may still cache data as long as the memory system appears as a single copy memory(i.e the writes should appear atomic). As SC requires program order to be maintained among all operation types, the pictorial representation of the program order shows all combinations of reads and writes, the line between them telling that the operations are required to complete in program order.&lt;br /&gt;
This model ensures that the reads of a variable, will return the new values written to it by a processor. Sequential consistency provides a simple, intuitive programming model. Because of its strict consistency requirements, sequential consistency, many of the architecture and compiler optimizations used in uniprocessors are not safely applicable to sequentially consistent multiprocessors.[ For more details on sequential consistency model and its advantages/disadvantages refer to '''[http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture]''' textbook by Yan Solihin , page 284 through 292]. For this reason, many '''Relaxed consistency models''' have been proposed, most of which&lt;br /&gt;
are supported  by commercial architectures.&lt;br /&gt;
&lt;br /&gt;
=='''Performance of Sequential Consistency on multiprocessors'''==&lt;br /&gt;
Sequential Consistency (SC) is the most intuitive programming interface for shared memory multiprocessors. A system implementing SC appears to execute memory operations one at a time and in program order. A program written for an SC system requires and&lt;br /&gt;
relies on a specified memory behavior to execute correctly. Implementing memory accesses according to the SC model constraints, however, would adversely impact performance because memory accesses in shared-memory multiprocessors often incur prohibitively long&lt;br /&gt;
latencies (tens of times longer than in uniprocessor systems). To enforce sequential consistency, illegal reordering caused by hardware optimizations like '''[http://en.wikipedia.org/wiki/Write_buffer Write buffers]''', '''[http://www.pcguide.com/ref/mbsys/cache/charTransactional-c.html Non-blocking caches]''' etc and compiler optimizations like '''[http://en.wikipedia.org/wiki/Loop-invariant_code_motion code motion]''', '''[http://en.wikipedia.org/wiki/Register_allocation register allocation]''','''[http://en.wikipedia.org/wiki/Common_subexpression_elimination eliminating common subexpressions]''', '''[http://www.cs.cmu.edu/afs/cs/academic/class/15828-s98/lectures/0318/index.htm loop transformations]''' etc resulting in reordering are not allowed. These are the optimizations which are implemented for better performance and are valid in uniprocessors. But in case of multiprocessors, these do not allow to satisfy the sequential consistency requirements and hence are not allowed. This affects the performance.&lt;br /&gt;
&lt;br /&gt;
A number of techniques have been proposed to enable the use of certain optimizations by the hardware and compiler without violating sequential consistency, those having the potential to substantially boost performance. Some of them are mentioned below:&lt;br /&gt;
&lt;br /&gt;
'''Hardware optimization techniques:'''&lt;br /&gt;
&lt;br /&gt;
* '''Prefetching''' : A hardware optimization technique in which the processor automatically prefetches ownership for any write operations that are delayed due to the program order requirement (e.g., by issuing prefetch-exclusive requests for any writes delayed&lt;br /&gt;
in the write buffer), thus partially overlapping the service of the delayed writes with the operations preceding them&lt;br /&gt;
in program order. This technique is only applicable to cache-based systems that use an invalidation-based protocol. This technique is suitable for statically scheduled processors. &lt;br /&gt;
&lt;br /&gt;
* '''Speculative Reads''' : A hardware optimization technique in which read operations that are delayed due to the program order requirement are serviced speculatively ahead of time. Sequential consistency is guaranteed by simply rolling back and reissuing the read and subsequent operations in the infrequent case that the read line gets invalidated or updated before the read could have been issued in a more straightforward implementation. This is suitable for dynamically scheduled processors since much&lt;br /&gt;
of the roll back machinery is already present to deal with branch mispredictions. &lt;br /&gt;
&lt;br /&gt;
More information about these two techniques can be found in this paper presented by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy of Stanford University at International Conference on Parallel Processing, '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models]'''&lt;br /&gt;
&lt;br /&gt;
These two techniques of '''Prefetching''' and '''Speculative Reads''' are expected to be supported by several next generation microprocessors like MIPS R10000 and Intel P6, thus enabling more efficient hardware implementations of sequential consistency.&lt;br /&gt;
&lt;br /&gt;
'''Software Optimization techniques'''&lt;br /&gt;
*'''Shasha and Snir's agorithm ''' : A compiler algorithm proposed by Dennis Shasha and Marc Snir is used to detect when memory operations can be reordered without violating sequential consistency. It uses the technique where Sequential consistency can be enforced by delaying each access to shared memory until the previous access of the same processor has terminated. For performance reasons, it allows several accesses by the same processor to proceed concurrently. It performs an analysis to find a minimal set of delays that enforces sequential consistency. The analysis extends to interprocessor synchronization constraints and to code where blocks of operations have to execute atomically thus providing a new compiler optimization techniques for parallel languages that support shared variables.&lt;br /&gt;
&lt;br /&gt;
In detail implementation of this algorithm can be studied from the paper : '''[http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]'''&lt;br /&gt;
&lt;br /&gt;
*'''Compiler algorithm for SPMD (Single Program multiple data) programs''' : The algorithm proposed by Sasha and Snir has exponential complexity. This new algorithm simplified the cycle detection analysis used in their algorithm to achieve the job in polynomial time.  &lt;br /&gt;
More information about this can be found in this paper by Arvind Krishnamurthy and Katherine Yelick presented at 7th International Workshop on Languages and Compilers for Parallel Computing '''[http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]'''&lt;br /&gt;
&lt;br /&gt;
In general the performance of sequential consistency model on multiprocessors with shared memory is low. But with the use of above techniques, better performance can be achieved. &lt;br /&gt;
&lt;br /&gt;
All of the above schemes mentioned to improve performance of SC allow a processor to overlap or reorder its memory accesses without software support, however, either require complex or restricted hardware (e.g., hardware prefetching and rollback) or the gains are expected to be small.  Further, the optimizations of these schemes can be exploited by hardware (or the runtime&lt;br /&gt;
system software), but cannot be exploited by compilers. Work related to compiler optimizations including that by Shasha and Snir motivate their work for hardware optimizations. Hardware costs are thus high to implement these techniques.&lt;br /&gt;
&lt;br /&gt;
Hence researchers and vendors have alternatively relied on '''relaxed memory consistency models''' that embed the shared-address space programming interface with directives enabling software to inform hardware when memory ordering is necessary.&lt;br /&gt;
&lt;br /&gt;
= '''Relaxed Consistency Models''' =&lt;br /&gt;
&lt;br /&gt;
There are two main reasons to implement Relaxed consistency models&lt;br /&gt;
# It is not always necessary to maintain sequential consistency&lt;br /&gt;
# Hardware overhead and performance degradation are not justified for ease of programming&lt;br /&gt;
&lt;br /&gt;
SC, for example, makes it hard to use write buffers,because write buffers cause operations to be presented to the cache-coherence protocol out of program order.Straightforward processors are also precluded from overlapping multiple reads and writes in the memory. system. This restriction is crippling in systems without caches, where all operations go to memory. In systems with cache coherence—which are the norm today—this restriction impacts activity whenever operations miss or bypass the cache. (Cache bypassing occurs on uncacheable operations to I/O space, some block transfer operations, and writes to some coalescing write buffers.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The basic idea behind relaxed memory models is to enable the use of more optimizations by eliminating some of the constraints that sequential consistency places on the overlap and reordering of memory operations.relaxed models typically allow certain memory operations to execute out of program order or non-atomically.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Relaxed consistency models can be partitioned into subgroups using four&lt;br /&gt;
comparisons: Type of Relaxation, Synchronizing vs. Non-Synchronizing, Issue vs. View-Based, and Relative Model Strength.&lt;br /&gt;
&lt;br /&gt;
'''1. Type of Relaxation:''' - A simple and effective way of categorizing relaxed consistency models is by defining which requirement of sequential consistency is relaxed. relaxed consistency model either relaxes the program order or write atomicity requirement. Depending upon the sequence one or more than one of following order can be relaxed.&lt;br /&gt;
&amp;lt;div style='margin-left:40px;'&amp;gt;&lt;br /&gt;
Read - Read&amp;lt;br&amp;gt;&lt;br /&gt;
Read - Write&amp;lt;br&amp;gt;&lt;br /&gt;
Write -Read&amp;lt;br&amp;gt;&lt;br /&gt;
Write - Write&amp;lt;br&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
'''2. Synchronizing vs. Non-Synchronizing:''' A synchronizing model divides shared memory&lt;br /&gt;
accesses into at least two groups and assigns a different consistency restriction to each group.&lt;br /&gt;
A non-synchronizing model does not differentiate between individual memory accesses and assigns the same consistency model to all accesses collectively. &lt;br /&gt;
&lt;br /&gt;
'''3. Issue vs. View-Based:''' An issue-based relaxation focuses on how the ordering of an&lt;br /&gt;
instruction issue will be seen by the entire system, as a collective unit. On the other hand, a view-based method does not aim to simulate sequential consistency; in&lt;br /&gt;
this category of models, each processor is allowed it’s own view of the ordering of events in&lt;br /&gt;
the system, and these views do not need to match.&lt;br /&gt;
&lt;br /&gt;
'''4. Relative Model Strength:''' Some models are inherently more strong (or strict) than other&lt;br /&gt;
models. If rating the strength of a model by the relaxations of program order or atomicity,&lt;br /&gt;
it may be possible to directly compare the strength of different models, as in a case where&lt;br /&gt;
one model relaxes everything another model relaxes—plus more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In this chapter we will mainly distinguish between different models based on type of relaxation of read-write ordering they allow. Each of these models have some flavors depending on some subtle differences.&lt;br /&gt;
&lt;br /&gt;
=='''Different Relaxed consistency Models'''==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let us now see the consistency models in some of the real multiprocessor systems listed above. We will not introduce those topics that are already covered in Solihin's textbook, like Weak ordering and Processor consistency models. '''RCsc''' and '''RCpc'''. RCsc maintains sequential consistency among synchronization operations, while RCpc maintains processor consistency among synchronization operations.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Read program order'''===&lt;br /&gt;
Generally in all systems writes take more time than reads. Based on this, the first category of relaxed models that we consider are those models that allow a write followed by a read to execute out of program order. The typical way hardware exploits this relaxation is through buffering writes and allowing a read to bypass the writes in the buffer. While maintaining sequential consistency typically requires the processor to wait for a previous write to complete before completing the next read operation, this optimization allows the processor to continue with a read without waiting for write operations to complete. As a result, the write latency can be effectively hidden. Furthermore, many applications function correctly (i.e., provide sequentially consistent results) even if the program order from a write to a read is not maintained.&lt;br /&gt;
&lt;br /&gt;
Therefore, even though systems that exploit this optimization are not sequentially consistent, they appear sequentially consistent to a large class of programs.&lt;br /&gt;
&lt;br /&gt;
[[Image:rewr.png]]&lt;br /&gt;
&lt;br /&gt;
Different flavors&lt;br /&gt;
# processor consistency- PC&lt;br /&gt;
# IBM 370&lt;br /&gt;
# Intel Pentium Pro&lt;br /&gt;
# Sun’s Total Store Order&lt;br /&gt;
&lt;br /&gt;
The differences among them arise from the way they deal with the atomicity of memory operations and whether a processor is allowed to return the value of its own write before the write completes in memory.&lt;br /&gt;
&lt;br /&gt;
====''IBM-370''====&lt;br /&gt;
The IBM-370 model allows a write followed by a read to complete out of program order unless the two operations are to the same location, or if either operation is generated by a serialization instruction, or if there is a serialization instruction in program order between the two operations.&lt;br /&gt;
As seen earlier write buffers are used to implement this reordering. As the writes are handled by write buffer reads can be performed before preceding write. &lt;br /&gt;
&lt;br /&gt;
[[Image:ibm370.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown in the above figure is similar to that used for representing SC. The main difference is the presence of a buffer between each processor and the memory. Since we assume that each processor issues its operations in program order, we use the buffer to model the fact that the operations are not necessarily issued in the same order to memory. The cancelled reply path from the buffer to the processor implies that a read  is not allowed to return the value of a writeto the same location from the buffer. &amp;lt;br /&amp;gt;&lt;br /&gt;
The IBM-370 model has two types of serialization instructions: special instructions that generate memory operations (e.g., compare-and-swap) and special non-memory instructions (e.g., a special branch).&lt;br /&gt;
&lt;br /&gt;
====''Total Store Ordering (TSO)''====&lt;br /&gt;
The total store ordering (TSO) model  allows reordering of read followed by write without any constraint. All other program orders are maintained. The conceptual system is almost identical to that of IBM-370 except the forwarding path from the buffer to a read is no longer blocked. Therefore, if a read matches (i.e., is to the same location as) a write in the write buffer, the value of the last such write in the buffer that is before it in program order is forwarded to the read. Otherwise, the read returns the value in memory, as in the SC and IBM-370 models.&lt;br /&gt;
&lt;br /&gt;
If we consider operations as executing in some sequential order, the buffer-and-memory value requirement requires the read to return the value of either the last write to the same location that appears before the read in this sequence or the last write to the same location that is before the read in program order, whichever occurs later in the sequence.&lt;br /&gt;
&lt;br /&gt;
[[Image:TSO1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
For TSO, a safety net for write atomicity is required only for a write that is followed by a read to the same location in the same processor. The atomicity can be achieved by ensuring program order from the write to the read using read-modify-writes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Difference between IBM370 and TSO:''' &amp;lt;br /&amp;gt; &lt;br /&gt;
Let us consider the program segment below, taken from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''', to demonstrate the difference between TSO and IBM 370 models.&lt;br /&gt;
&lt;br /&gt;
[[Image:Code_1.jpg]]&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First consider the program segment in (a). Under the SC or IBM-370 model, the outcome (u,v,w,x)=(1,1,0,0) is disallowed. However, this outcome is possible under TSO because reads are allowed to bypass all previous writes, even if they are to the same location; therefore the sequence (b1,b2,c1,c2,a1,a2) is a valid total order for TSO. Of course, consistency value requirement still requires b1 and b2 to return the values of a1 and a2, respectively, even though the reads occur earlier in the sequence than the&lt;br /&gt;
writes. This maintains the intuition that a read observes all the writes issued from the same processor as the read. Consider the program segment in (b). In this case, the outcome (u,v,w,x)=(1,2,0,0)is not allowed under SC or IBM-370, but is possible under TSO.&lt;br /&gt;
&lt;br /&gt;
====''Processor Consistency (PC) ''====&lt;br /&gt;
Unlike the previous two models, Processor Consistency, first introduced in [21], is both view-based and non-synchronizing. In other words, each processor is allowed to have its own view of the system, and all memory accesses are treated the same. The order in which writes are observed must be the same as the order in which they were issued. However, if two processors issue writes, those writes do not need to appear to execute in the same order, from the perspective of either of the two processors or a third processor. The conditions of Processor Consistency in another way is&lt;br /&gt;
# Before a read operation is allowed to perform with respect to any other processor, all previous read accesses must have already been performed.&lt;br /&gt;
# Before any write operation is allowed to perform with respect to any other processor, all previous reads and writes must have been performed. The two conditions above imply one important fact: only the read-after-write program order requirement is relaxed.&lt;br /&gt;
&lt;br /&gt;
==='''Relax Write-to-Write program order'''===&lt;br /&gt;
The second category of relaxed models that we consider allow two writes to execute out of program order in addition to allowing the reordering of a write followed by a read. This relaxation enables a number of hardware optimizations, including write merging in a write buffer and overlapping multiple write misses, all of which can lead to a reodering of write operations. Therefore, write operations can be serviced at a much faster rate.&lt;br /&gt;
&lt;br /&gt;
[[Image:reww.png]]&lt;br /&gt;
&lt;br /&gt;
==== ''SPARC V8 Partial Store Ordering'' ====&lt;br /&gt;
The '''Partial Store Ordering'''('''PSO''') model is very similar to the TSO model for SPARC V8. The figure below shows an identical conceptual system. There is only a slight difference in the program order, where writes to locations can be overlapped only if they are not to same locations. This is represented by the dashed line between the W's in the program order figure. Safety net for the program order is provided through a fence instruction, called the store barrier or STBAR, that may be used to enforce the program order between writes.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:PSO.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Let us consider an example from '''[http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]''' to demonstrate the working of PSO.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:code2.jpg]]&amp;lt;br /&amp;gt;&lt;br /&gt;
With relaxed Write to Read models, the only value for (u,v) is (1,1). With PSO, since it allows writes to different locations to complete out of program order, it also allows the outcomes (0,0) or (0,1) or (1,0) for (u,v). In this example, STBAR instruction needs to be placed immediately before ''c1'' on P1 in order to disallow all outcomes except (1,1).&lt;br /&gt;
&lt;br /&gt;
==='''Relax Read-to-Read and Read-to-Write program orders'''===&lt;br /&gt;
This model sheds the restriction on program order between all operations, including read to read and read followed by write to different locations . This flexibility provides the possibility of hiding the latency of read operations by implementing true non-blocking reads in the context of either static (in-order) or dynamic (out-of-order) scheduling processors, supported by techniques such as non-blocking (lockup-free) caches and speculative execution. The compiler also has full flexibility to reorder operations.&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Weak ordering (WO)''', '''Release consistency (RC)''', '''DEC Alpha''', '''Relaxed Memory Order (RMO)''', and '''PowerPC''' are examples of this model, with the last three models for commercial architectures. Except for Alpha,all the other models allow reordering of two reads to the same location. &amp;lt;br /&amp;gt;&lt;br /&gt;
All of the models in this group allow a processor to read its own write early with the exception of  RCpc, a flavor of Release consistency (RC)  and PowerPC. All the three commercial architectures provide explicit fence instructions to ensure program order. Frequent use of fence instructions can incur a significant overhead due to an increase in the number of instructions and the extra delay that may be associated with executing fence instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''DEC Alpha''====&lt;br /&gt;
The program order constraints allow reads and writes to different locations to complete out of program order&lt;br /&gt;
unless there is a fence instruction between them. Memory operations to the same location, including reads, are required to complete in program order. Safety net for program order is provided through the fence instructions, the memory barrier (MB) and the write memory barrier (WMB). The MB instruction can be used to maintain program order between any memory operations, while WMB instruction provides this guarantee only among write operations. The Alpha model does not require a safety net for write atomicity.&lt;br /&gt;
[[Image:alpha.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The conceptual system shown above is the same same as IBM 370 which requires a read to return the value of the last write operation to the same location. But since we can relax the program order from a write to a read, we can safely exploit optimizations such as read forwarding.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''Relaxed Memory Order (RMO)''====&lt;br /&gt;
The SPARC V9 architecture uses RMO which is an extension of the TSO and PSO models used in SPARC V8. &lt;br /&gt;
The Read to Read, Read to write program order is relaxed in this model, like in PSO. But a Read to Write or the order between two writes to the same location  are not relaxed. This is shown in the figure for program order below. &lt;br /&gt;
[[Image:rmo1.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
RMO provides four types of fences[F1 through F4] that allow program order to be selectively maintained between any two types of operations. A single fence instruction, MEMBAR, can specify a combination of the above fence types by setting the appropriate bits in a four-bit opcode. No safety net for write atomicity is required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== ''IBM PowerPC'' ====&lt;br /&gt;
This model constrains  the program order &amp;lt;br /&amp;gt;&lt;br /&gt;
-Between sub-operations. That is each operation may consist of multiple sub-operations and that all sub-operations of the first operation must complete before any sub-operations of the second operation.[represented by the double lines between operations in the figure below]. &amp;lt;br /&amp;gt;&lt;br /&gt;
-Writes to the same location.&amp;lt;br /&amp;gt;&lt;br /&gt;
-Among conflicting operations. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Image:powerpc.jpg]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The IBM PowerPC model exposes multiple-copy semantics of memory to the programmer as shown in the conceptual system above. Safety net fence instruction is called SYNC which is similar to the MB fence instruction of the Alpha systems. However, SYNC between two reads allows to occur out of program order. Also PowerPC allows a write to be seen early by another processor’s read. Hence a read-modify-write operation may be needed  to enforce program order between two reads to the same location as well as to make writes appear atomic.&lt;br /&gt;
&lt;br /&gt;
=='''The Big Picture - Relationship Between Different Models'''==&lt;br /&gt;
[[Image:rbdf.png]]&lt;br /&gt;
&lt;br /&gt;
In this diagram we can see the different memory consistency models and how they are related to each other. The most strict model SC - sequential consistency is at the top which maintains all 4 orders of read-write. The first relaxation of write-read order is allowed in memory models TSO, PC and IBM - 370 which can be seen at second level from top. Third level which comprise of PSO model also allows write - write order relaxation and hence is less strict than any of the TSO, PC or IBM - 370. In the bottom level both read-read and read-write program orders are also relaxed and hence are the least strict. Different flavors of the model in this level are discussed later in chapter and in Yan Solihin[9] text.&lt;br /&gt;
&lt;br /&gt;
=='''Release Consistency Related Models'''==&lt;br /&gt;
Release consistency provides these two kinds. '''Acquire''' accesses are used to tell the memory system that a critical region is about to be entered. '''Release''' accesses say that a critical region has just been exited. These accesses can be implemented either as ordinary operations on special variables or as special operations.&lt;br /&gt;
==='''Release Consistency Models'''===&lt;br /&gt;
===='''Eager Release Consistency Model'''====&lt;br /&gt;
In Eager Release Consistency Model , the invalidations (or write notices) are propagated at release points. '''Munin's write shared protocol''' proposed by '''John K. Bennett, John B. Carter, and Willy Zwaenepoel''' of Rice University implemented this Eager Release Consistency Model. It is a software implementation of Release consistency Model which buffers writes until a release, instead of pipelining them as in the DASH implementation. At the release all writes going to the same destination are merged into a single message. This is illustrated in the following diagram:&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:munin.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This approach is conservative because the system does not know when the next acquire by another processor will occur or whether a given process will even perform an acquire and need to see those write notices. &lt;br /&gt;
&lt;br /&gt;
More information about the implementation of this Eager Release Consistency Model can be studied from these two papers: '''[http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]''' and '''[http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]'''&lt;br /&gt;
&lt;br /&gt;
===='''Lazy Release Consistency Model'''====&lt;br /&gt;
Lazy release consistency (LRC) is consistency model most frequently used in Software Distributed Shared Memory. It is used  for implementing release consistency that lazily pulls modifications across the interconnect only when necessary. The basic concept behind the protocol is to allow processors to continue referencing cache blocks that have been written by other processors. Although write notices are sent as soon as a processor writes a shared block, invalidations occur only at acquire operations. This is sufficient to ensure that true sharing dependencies are observed. Lazy algorithms such as LRC do not make modications globally visible at the time of a release. Instead, LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image: LRC.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about Lazy Release Consistency Model can be obtained from these two papers : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]''' and '''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
===='''Comparison between Lazy and Eager Release Consistency Models'''====&lt;br /&gt;
Eager release consistency represents the state of the art in release consistent protocols for hardware-coherent multiprocessors, while lazy release consistency has been shown to provide better performance for software distributed shared memory (DSM). Several of the optimizations performed by lazy protocols have the potential to improve the performance of hardware-coherent multiprocessors as well, but their complexity has precluded a hardware implementation.&lt;br /&gt;
&lt;br /&gt;
An Eager Release Consistency Model like Munin's write shared protocol may send more messages than a message passing implementation of the same application. The following figure shows an example where processors p1 through p4 repeatedly acquire the lock l, write the shared variable x, and then release l. &lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:Eager2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If an update policy is used in conjunction with Munin's write shared protocol and x is present in all caches, then all of these cached copies are updated at every release. Logically, however it suffices to update each processor's copy only when it acquires l. This results in a single message exchange per acquire as in a message passing implementation.&lt;br /&gt;
&lt;br /&gt;
Unlike eager algorithms such as Munin's write shared protocol, lazy algorithms such as LRC (Lazy Release Consistency) do not make modications globally visible at the time of a release. Instead LRC guarantees only that a processor that acquires a lock will see all modications that precede the lock acquire. As indicated in the above figure , all modications that occur in program order before any of the releases in p1 through p4 precede the lock acquisition in p4. With LRC, modications are propagated at the time of an acquire. Only the modications that precede the acquire are sent to the acquiring processor. The modifications can be piggybacked on the message that grants the lock, further reducing message traffic. The following figure shows the message traffic in LRC for the same shared data accesses as in Figure under lazy consistency model section. l and x are sent in a single message at each acquire.&lt;br /&gt;
&amp;lt;center&amp;gt; [[Image:lazy2.jpg]] &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More information about differences between Eager and Lazy consistency Models can be found here : '''[http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]''' and &lt;br /&gt;
'''[http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]'''&lt;br /&gt;
&lt;br /&gt;
====='''Performance Analysis of Lazy and Eager Consistency Models'''=====&lt;br /&gt;
&lt;br /&gt;
Leonidas I. Kontothanassis, Michael L. Scott, and Ricardo Bianchini from University of Rochester have performed experimentations to evaluate a lazy release-consistent protocol suitable for machines with dedicated protocol processors. Their results indicate that the first protocol outperforms eager release consistency by as much as 20% across a variety of applications. The lazier protocol, on&lt;br /&gt;
the other hand, is unable to recoup its high synchronization overhead. This represents a qualitative shift from the DSM world, where lazier protocols always yield performance improvements. Based on their results, they conclude that machines with flexible hardware&lt;br /&gt;
support for coherence should use protocols based on lazy release consistency, but in a less ''aggressively lazy'' form than is appropriate for DSM.&lt;br /&gt;
&lt;br /&gt;
These are the results they show from their experimentations to compare Lazy and Eager Consistency Models:&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;[[Image:Perf.jpg|250px]]&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More information about this experimentation and results can be obtained from their paper : '''[http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
='''Comparison Between Different Models'''=&lt;br /&gt;
In this section we will consider the comparison made between different consistency models in Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors by Kourosh Gharachorloo, Anoop Gupta, and John Hennessy.&lt;br /&gt;
Architecture used&lt;br /&gt;
chosen an architecture that resembles&lt;br /&gt;
the DASH shared-memory multiprocessor [13],&lt;br /&gt;
Physical&lt;br /&gt;
memory is distributed among the nodes and cache coherence&lt;br /&gt;
is maintained using a distributed directory-based protocol. For&lt;br /&gt;
each memory block, the directory keeps track of remote nodes&lt;br /&gt;
caching the block, and point-to-point messages are sent to invalidate&lt;br /&gt;
remote copies. Acknowledgement messages are used to&lt;br /&gt;
inform the originating processing node when an invalidation has&lt;br /&gt;
been completed.&lt;br /&gt;
[[Image:archused.png]]&lt;br /&gt;
&lt;br /&gt;
Here performance is defined as the processor utilization achieved in execution. reason for using processor utilization as the figure of merit is that&lt;br /&gt;
it provides reasonable results even when the program’s control&lt;br /&gt;
path is not deterministic and depends on relative timing of synchronization&lt;br /&gt;
accesses. The processor utilization for each model&lt;br /&gt;
is normalized to the performance of the BASE model for that&lt;br /&gt;
program. The results show a wide range of performance gains&lt;br /&gt;
due to the less strict models. Moving from BASE to SC, the&lt;br /&gt;
gains are minimal. The largest gains in performance arise when&lt;br /&gt;
moving from SC to PC. Surprisingly, WC does worse than PC&lt;br /&gt;
for PTHOR. RC performs better than all the other models, but&lt;br /&gt;
the gains over PC are small. The maximum gain from relaxing&lt;br /&gt;
the consistency model is about 41% for MP3D, 29% for PTHOR,&lt;br /&gt;
and 11% for LU.&lt;br /&gt;
&lt;br /&gt;
[[Image:perf1.png]]&lt;br /&gt;
&lt;br /&gt;
To better understand the above results, in Figure 4 we present a&lt;br /&gt;
breakdown of the execution time for the applications under each&lt;br /&gt;
of the models. The execution time of the models are normalized&lt;br /&gt;
to the execution time of BASE for each application. The bottom&lt;br /&gt;
section of each column represents the busy time or useful cycles&lt;br /&gt;
executed by the processor. The black section above it represents&lt;br /&gt;
the time that the processor is stalled waiting for reads. This&lt;br /&gt;
time does not include the time that a read/acquire access may&lt;br /&gt;
be stalled waiting for previous writes to perform. This time is&lt;br /&gt;
represented by the section above it. The three sections on top of&lt;br /&gt;
that represent the stalls due to write buffer being full, time spent&lt;br /&gt;
spinning while waiting for acquires to complete, and time spent&lt;br /&gt;
waiting at a barrier.2&lt;br /&gt;
Some general observations that can be made from the breakdown&lt;br /&gt;
are: (i) the latency of read misses forms a large portion of&lt;br /&gt;
the idle time, especially once we move to PC, WC, and RC; (ii)&lt;br /&gt;
the major reason for BASE and SC to be worse than the other&lt;br /&gt;
models is the stalling of the processor before reads (and acquires)&lt;br /&gt;
for pending writes to complete; (iii) the write buffer being full&lt;br /&gt;
does not seem to be a factor in hindering the performance of PC;&lt;br /&gt;
and finally, (iv) the reason for WC performing worse than PC&lt;br /&gt;
and RC is the extra processor stalls at acquires and the first read&lt;br /&gt;
after release accesses (as described in Section 4). The small variation&lt;br /&gt;
in busy times for PTHOR is due to the non-deterministic&lt;br /&gt;
behavior of the application for the same input. We now look at&lt;br /&gt;
the comparative performance of the models in greater detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style='width:1900px;'&amp;gt;[[Image:perf2.png|370px]] [[Image:perf3.png|370px]] [[Image:perf4.png|370px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In another study Ronald Bos et al. investigated the performance benefits of relaxed consistency models on multiprocessors executing a process network application. They used a trace-driven simulator, developed using SystemC, to model the distributed shared memory system of a&lt;br /&gt;
prototype multiprocessor developed at Philips called Philips '''[http://www.es.ele.tue.nl/epicurus/files/report_jvrijnsen.pdf CAKE]''' (a nonuniform, distributed shared memory multiprocessor prototype). The simulator offers two consistency models: Sequential Consistency (SC) and a generalized Relaxed Consistency (RC) model. Input traces were generated by running a process network application in a cycle-accurate simulator of the prototype multiprocessor. The results showed that relaxed consistency has marginal performance benefits over sequential consistency. The advantage of relaxed memory consistency decreases for increasing network latency. More information about this study can be found in this paper '''[http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]'''&lt;br /&gt;
&lt;br /&gt;
='''Some Shortcomings of Relaxed Models'''=&lt;br /&gt;
Even though relaxed models enable desirable optimizations, their major drawback is increased programming complexity. Most programmers have implicit assumptions about the memory behavior of a shared-memory multiprocessor and use these assumptions when reasoning about the correctness of their programs. Correctness&lt;br /&gt;
problems arise when certain orders that are implicitly assumed by the programmer are not maintained by&lt;br /&gt;
the underlying memory model. The advantage of sequential consistency is that no matter what implicit&lt;br /&gt;
assumptions a programmer makes regarding the program order or atomicity of memory operations, SC&lt;br /&gt;
conservatively maintains all such orders. Therefore, the programmer’s implicit assumptions are never&lt;br /&gt;
violated.&lt;br /&gt;
&lt;br /&gt;
In contrast to sequential consistency, relaxed memory models require programmers to abandon their&lt;br /&gt;
implicit and intuitive understanding of how memory behaves. Most of the relaxed models we have described&lt;br /&gt;
require the programmer to reason with low level (and non-intuitive) reordering optimizations to understand&lt;br /&gt;
the behavior of their programs. In addition, many of the models have been defined using complicated&lt;br /&gt;
terminology, and in some cases, the original definitions have ambiguities which leave the semantics open to&lt;br /&gt;
interpretation. These factors further exacerbate the difficulties in programming these models.&lt;br /&gt;
&lt;br /&gt;
Another difficulty with relaxed models is the lack of compatibility among the numerous models and&lt;br /&gt;
systems in existence. Many of the subtle differences among models make little difference in the actual&lt;br /&gt;
performance of a model. However, such differences make the task of porting programs across different&lt;br /&gt;
systems quite cumbersome. Similarly, the variety of models in existence make it difficult for programmers to&lt;br /&gt;
adopt a programming methodology that works across a wide range of systems.&lt;br /&gt;
With all their shortcomings, relaxed models are widely used in many commercial multiprocessor systems,&lt;br /&gt;
including systems designed by major computer manufacturers such as Digital Equipment, IBM, and Sun&lt;br /&gt;
Microsystems. The wide-spread use of these systems suggests that even though sequential consistency is&lt;br /&gt;
simpler to use for programmers, performance often plays an important role in the ultimate choice made by&lt;br /&gt;
system designers and programmers. Nevertheless, we would ideally like to provide the extra performance&lt;br /&gt;
with as little programming complexity as possible.&lt;br /&gt;
&lt;br /&gt;
='''References'''=&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.5.7132&amp;amp;rep=rep1&amp;amp;type=pdf Speculative Sequential Consistency with Little Custom Storage] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.126.3128&amp;amp;rep=rep1&amp;amp;type=pdf Two techniques to enhance the performance of memory consistency models] &amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8185&amp;amp;rep=rep1&amp;amp;type=pdf Optimizing parallel SPMD programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=42277 Efficient and correct execution of parallel programs that share memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-7.pdf Shared Memory Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://portal.acm.org/citation.cfm?id=193889&amp;amp;dl=GUIDE&amp;amp;coll=GUIDE&amp;amp;CFID=84028355&amp;amp;CFTOKEN=32262273 Designing Memory Consistency Models For Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.52.9935&amp;amp;rep=rep1&amp;amp;type=pdf Performance Evaluation of Memory Consistency Models for Shared-Memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://ce.et.tudelft.nl/publicationfiles/915_463_bos.pdf Performance Benefits of Relaxed Memory Consistency for Process Network Applications]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cesr.ncsu.edu/solihin/Main.html Fundamentals of Parallel Computer Architecture by Yan Solihin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.wisc.edu/multifacet/papers/computer98_sccase.pdf Multiprocessors Should Support Simple Memory-Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infolab.stanford.edu/pub/cstr/reports/csl/tr/95/685/CSL-TR-95-685.pdf Technical Report by Kourosh Gharachorloo on Memory Consistency Models for shared-memory Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://cs.gmu.edu/cne/modules/dsm/green/memcohe.html Consistency Models]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.barroso.org/publications/delayed.pdf Delayed Consistency and Its Effects on the Miss Rate of Parallel Programs]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://delivery.acm.org.www.lib.ncsu.edu:2048/10.1145/230000/224398/a61-kontothanassis.html?key1=224398&amp;amp;key2=6529911721&amp;amp;coll=ACM&amp;amp;dl=ACM&amp;amp;CFID=140829&amp;amp;CFTOKEN=60032635 Lazy Release Consistency for Hardware-Coherent Multiprocessors]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55790/files/sosp91.ps.pdf Implementation and Performance of Munin]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.springerlink.com/content/mg141q86k2112788/fulltext.pdf?page=1 Munin: Distributed Shared Memory Using Multi-Protocol Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.rochester.edu/research/cashmere/SC95/lazeag.html Lazy v. Eager Release Consistency]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://infoscience.epfl.ch/record/55789/files/isca92.ps.pdf Lazy Release Consistency for Software Distributed Shared Memory]&amp;lt;br /&amp;gt;&lt;br /&gt;
# [http://www.cs.cornell.edu/Courses/cs717/2001fa/lectures/sarita.ppt Memory Consistency Models, by Sarita Adve]&amp;lt;br /&amp;gt;&lt;br /&gt;
# Kourosh Gharachorloo, Daniel Lenoski, James Laudon, Phillip Gibbons, Anoop Gupta, and John Hennessy. Memory consistency and event ordering in scalable shared-memory multiprocessors. In ISCA ’98: 25 Years of the International Symposia on Computer Architecture (Selected Papers), pages 376–387. ACM, 1998.&lt;br /&gt;
# James R. Goodman. Cache consistency and sequential consistency. Technical Report 61, IEEE Scalable Coherent Interface (SCI) Working Group, March 1989.&lt;br /&gt;
# Dan Lenoski, James Laudon, Kourosh Gharachorloo,Anoop Gupta, and John Hennessy. The directory-based cache coherence protocol for the DASH multiprocessor. In Proceedings of the 17th Annual International Symposium on Computer Architecture, May 1990.&lt;br /&gt;
#Jenny Mankin. Parallel Computing Memory Consistency Models: A Survey in Past and Present Research&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=ECE506_Main_Page&amp;diff=74595</id>
		<title>ECE506 Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=ECE506_Main_Page&amp;diff=74595"/>
		<updated>2013-04-03T02:09:19Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page serves as a portal for all wiki material related to CSC506 and ECE506. Link to any new wiki pages from this page, and add links to any current pages.&lt;br /&gt;
&lt;br /&gt;
=Supplements to Solihin Text=&lt;br /&gt;
&lt;br /&gt;
Post links to the textbook supplements in this section.&lt;br /&gt;
*Chapter 2 [[CSC/ECE 506 Spring 2011/ch2 dm | CSC/ECE 506 Spring 2011/ch2 dm]]&lt;br /&gt;
*Chapter 2 [[Parallel_Programming_Models | Parallel Programming Models]]&lt;br /&gt;
*Chapter 2 (Still being revised) [[CSC/ECE 506 Spring 2011/ch2 cl | CSC/ECE 506 Spring 2011/ch2 cl]]&lt;br /&gt;
*Chapter 2a [[ CSC/ECE 506 Spring 2011/ch2a mc | Current Data-Parallel Architectures ]]&lt;br /&gt;
*Chapter 2a [[ CSC/ECE 506 Spring_2012/2a va ]]&lt;br /&gt;
*Chapter 2b [[CSC/ECE 506 Spring 2012/ch2b cm | CSC/ECE 506 Spring 2012/ch2b cm]]&lt;br /&gt;
*Chapter 2b [[ECE506_CSC/ECE_506_Spring_2012/2b_az | CSC/ECE 506 Spring 2012/2b az - Data-Parallel Processing with the AMD HD 6900 Series Graphics Processing Unit]]&lt;br /&gt;
*Chapter 3 (Final Revision) [[ CSC/ECE 506 Spring 2011/ch3 ab | Parallel Architecture Mechanisms and Programming Models ]]&lt;br /&gt;
*Chapter 4a[[ CSC/ECE 506 Spring 2011/ch4a ob | Parallelization of Nelder Mead Algorithm ]]&lt;br /&gt;
*Chapter 4a (Under Construction) [[ CSC/ECE_506_Spring_2011/ch4a_bm | Parallelization of Algorithms  ]]&lt;br /&gt;
*Chapter 4a [[ CSC/ECE 506 Spring 2011/ch4a zz | CSC/ECE 506 Spring 2011/ch4a zz ]]&lt;br /&gt;
*Chapter 4b [[Chapter 4b CSC/ECE 506 Spring 2011 / ch4b]]&lt;br /&gt;
*Chapter 5a [[ CSC/ECE 506 Spring 2012/ch5a ja | CSC/ECE 506 Spring 2012/ch5a ja ]]&lt;br /&gt;
*Chapter 9a [[CSC/ECE 506 Spring 2012/ch9a cm | CSC/ECE 506 Spring 2012/ch9a cm]]&lt;br /&gt;
*Chapter 6a (Under Construction) [[ CSC/ECE 506 Spring 2011/ch6a jp | CSC/ECE 506 Spring 2011/ch6a jp ]]&lt;br /&gt;
*Chapter 6a (Under Construction) [[ CSC/ECE 506 Spring 2011/ch6a ep | CSC/ECE 506 Spring 2011/ch6a ep ]]&lt;br /&gt;
*Chapter 6b (Ready for First Review) [[CSC/ECE 506 Spring 2011/ch6b ab | CSC/ECE 506 Spring 2011/ch6b ab]]&lt;br /&gt;
*Chapter 7 (Under Construction) [[CSC/ECE 506 Spring 2011/ch7 jp | CSC/ECE 506 Spring 2011/ch7 jp]]&lt;br /&gt;
*Chapter 8 [[CSC/ECE 506 Spring 2011/ch8 mc | CSC/ECE 506 Spring 2011/ch8 mc]]&lt;br /&gt;
*Chapter 10 (Under Construction) [[CSC/ECE 506 Spring 2011/ch10 sb | CSC/ECE 506 Spring 2011/ch10 sb]]&lt;br /&gt;
*Chapter 10 [[CSC/ECE 506 Spring 2012/ch10 sj | CSC/ECE 506 Spring 2012/ch10 sj]]&lt;br /&gt;
*Chapter 10a [[CSC/ECE_506_Spring_2011/ch10a_dc | CSC/ECE_506_Spring_2011/ch10a_dc]]&lt;br /&gt;
*Chapter 11 [[CSC/ECE_506_Spring_2011/ch11_BB_EP | Chapter 11 Supplement]]&lt;br /&gt;
*Chapter 11 [[Scalable_Coherent_Interface | SCI (Scalable Coherent Interface) ]]&lt;br /&gt;
*Chapter 12 [[ CSC/ECE 506 Spring 2011/ch12 ob | Interconnection Network Topologies and Routing Algorithms]]&lt;br /&gt;
*Chapter 12 (Ready for Final Review) [[ CSC/ECE 506 Spring 2011/ch12 aj | Interconnection Network Topologies and Routing Algorithms]]&lt;br /&gt;
*Chapter 12 [[ CSC/ECE 506 Spring 2011/ch12 | Interconnection Network Topologies]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1a ry]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1c dm]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1c cl]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1a mw]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/3a yw]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/7b yw]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/3b sk]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/4b rs]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/6b am]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/8a cj]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/10a dr]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/10a jp]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/9a ms]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/10b sr]]&lt;br /&gt;
*Chapter 11a [[ECE506_CSC/ECE_506_Spring_2012/11a_az | CSC/ECE 506 Spring 2012/11a az - Performance of DSM system]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/12b jh]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2010/8a fu]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2010/8a sk]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/11a ht]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1b dj]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1a sp]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1d ks]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/2b so]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1c ad]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/3b xz]]&lt;br /&gt;
*[[CSC/ECE_506_Spring_2013/4a_aj]]&lt;br /&gt;
*[[CSC/ECE_506_Spring_2013/4a_ss]]&lt;br /&gt;
*[[CSC/ECE_506_Spring_2013/1a_ag]]&lt;br /&gt;
* Chapter 3a [[CSC/ECE_506_Spring_2013/3a_bs]]&lt;br /&gt;
* Chapter 6a [[CSC/ECE_506_Spring_2013/6a_cs]]&lt;br /&gt;
* Chapter 5a [[CSC/ECE_506_Spring_2013/5a_ks]]&lt;br /&gt;
* Chapter 8a [[CSC/ECE_506_Spring_2013/8a_an]]&lt;br /&gt;
* Chapter 7a [[CSC/ECE_506_Spring_2013/7a_bs]]&lt;br /&gt;
* Chapter 8b [[CSC/ECE_506_Spring_2013/8b_ap]]&lt;br /&gt;
* Chpater 8c [[CSC/ECE_506_Spring_2013/8c_da]]&lt;br /&gt;
* Chpater 10a [[CSC/ECE_506_Spring_2013/10a_os]]&lt;br /&gt;
* Chapter 10c [[CSC/ECE_506_Spring_2013/10c_ks]]&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73812</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73812"/>
		<updated>2013-02-27T20:27:28Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[19]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
&lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[20]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[20]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[21]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
'''Hash Table Intro '''&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations. They are used extensively in data processing as in involves a vast amount of data through the hash table using as few indirection's in the storage structure as possible.   &lt;br /&gt;
&lt;br /&gt;
A single hash table level look can easily become a bottleneck, thus several method were developed to overcome this difficulty. Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list. [[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
There are several parallel implementations of hash tables available that use lock based synchronization. Larson et al. in [11] use two lock levels, there is one global table level lock, and there is one separate lightweight lock (a flag) for each bucket. The high level lock is just used for setting the bucket level flags and released right afterwards. This ensures a fine grained mutual exclusion (concurrent operations on bucket level), but needs only one real lock for the implementation. &lt;br /&gt;
&lt;br /&gt;
  A scaleable hash table for shared memory multi-processor (SMP) supports very high rates of concurrent operations (e.g., insert, delete, &lt;br /&gt;
  and lookup), while simultaneously reducing cache misses. The SMP system has a memory subsystem and a processor subsystem interconnected &lt;br /&gt;
  via a bus structure. The hash table is stored in the memory subsystem to facilitate access to data items. The hash table is segmented &lt;br /&gt;
  into multiple buckets, with each bucket containing a reference to a linked list of bucket nodes that hold references to data items with &lt;br /&gt;
  keys that hash to a common value. Individual bucket nodes contain multiple signature-pointer pairs that reference corresponding data items.&lt;br /&gt;
  Each signature-pointer pair has a hash signature computed from a key of the data item and a pointer to the data item. The first bucket &lt;br /&gt;
  node in the linked list for each of the buckets is stored in the hash table.&lt;br /&gt;
&lt;br /&gt;
   To enable multithread access, while serializing operation of the table, the SMP system utilizes two levels of locks: a table lock and&lt;br /&gt;
  multiple bucket locks. The table lock allows access by a single processing thread to the table while blocking access for other processing&lt;br /&gt;
  threads. The table lock is held just long enough for the thread to acquire the bucket lock of a particular bucket node. Once the table lock&lt;br /&gt;
  is released, another thread can access the hash table and any one of the other buckets.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
#http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;br /&gt;
#http://dl.acm.org/citation.cfm?id=320078&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_traversal&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73810</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73810"/>
		<updated>2013-02-27T20:26:53Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[19]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
&lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[20]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[20]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[21]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations. They are used extensively in data processing as in involves a vast amount of data through the hash table using as few indirection's in the storage structure as possible.   &lt;br /&gt;
&lt;br /&gt;
A single hash table level look can easily become a bottleneck, thus several method were developed to overcome this difficulty. Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list. [[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
There are several parallel implementations of hash tables available that use lock based synchronization. Larson et al. in [11] use two lock levels, there is one global table level lock, and there is one separate lightweight lock (a flag) for each bucket. The high level lock is just used for setting the bucket level flags and released right afterwards. This ensures a fine grained mutual exclusion (concurrent operations on bucket level), but needs only one real lock for the implementation. &lt;br /&gt;
&lt;br /&gt;
  A scaleable hash table for shared memory multi-processor (SMP) supports very high rates of concurrent operations (e.g., insert, delete, &lt;br /&gt;
  and lookup), while simultaneously reducing cache misses. The SMP system has a memory subsystem and a processor subsystem interconnected &lt;br /&gt;
  via a bus structure. The hash table is stored in the memory subsystem to facilitate access to data items. The hash table is segmented &lt;br /&gt;
  into multiple buckets, with each bucket containing a reference to a linked list of bucket nodes that hold references to data items with &lt;br /&gt;
  keys that hash to a common value. Individual bucket nodes contain multiple signature-pointer pairs that reference corresponding data items.&lt;br /&gt;
  Each signature-pointer pair has a hash signature computed from a key of the data item and a pointer to the data item. The first bucket &lt;br /&gt;
  node in the linked list for each of the buckets is stored in the hash table.&lt;br /&gt;
&lt;br /&gt;
   To enable multithread access, while serializing operation of the table, the SMP system utilizes two levels of locks: a table lock and&lt;br /&gt;
  multiple bucket locks. The table lock allows access by a single processing thread to the table while blocking access for other processing&lt;br /&gt;
  threads. The table lock is held just long enough for the thread to acquire the bucket lock of a particular bucket node. Once the table lock&lt;br /&gt;
  is released, another thread can access the hash table and any one of the other buckets.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
#http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;br /&gt;
#http://dl.acm.org/citation.cfm?id=320078&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_traversal&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73809</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73809"/>
		<updated>2013-02-27T20:26:35Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[19]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
&lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[20]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[20]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[21]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations. They are used extensively in data processing as in involves a vast amount of data through the hash table using as few indirection's in the storage structure as possible.   &lt;br /&gt;
&lt;br /&gt;
A single hash table level look can easily become a bottleneck, thus several method were developed to overcome this difficulty. Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list. [[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
There are several parallel implementations of hash tables available that use lock based synchronization. Larson et al. in [11] use two lock levels, there is one global table level lock, and there is one separate lightweight lock (a flag) for each bucket. The high level lock is just used for setting the bucket level flags and released right afterwards. This ensures a fine grained mutual exclusion (concurrent operations on bucket level), but needs only one real lock for the implementation. &lt;br /&gt;
&lt;br /&gt;
  A scaleable hash table for shared memory multi-processor (SMP) supports very high rates of concurrent operations (e.g., insert, delete, &lt;br /&gt;
  and lookup), while simultaneously reducing cache misses. The SMP system has a memory subsystem and a processor subsystem interconnected &lt;br /&gt;
  via a bus structure. The hash table is stored in the memory subsystem to facilitate access to data items. The hash table is segmented &lt;br /&gt;
  into multiple buckets, with each bucket containing a reference to a linked list of bucket nodes that hold references to data items with &lt;br /&gt;
  keys that hash to a common value. Individual bucket nodes contain multiple signature-pointer pairs that reference corresponding data items.&lt;br /&gt;
  Each signature-pointer pair has a hash signature computed from a key of the data item and a pointer to the data item. The first bucket &lt;br /&gt;
  node in the linked list for each of the buckets is stored in the hash table.&lt;br /&gt;
&lt;br /&gt;
   To enable multithread access, while serializing operation of the table, the SMP system utilizes two levels of locks: a table lock and&lt;br /&gt;
  multiple bucket locks. The table lock allows access by a single processing thread to the table while blocking access for other processing&lt;br /&gt;
  threads. The table lock is held just long enough for the thread to acquire the bucket lock of a particular bucket node. Once the table lock&lt;br /&gt;
  is released, another thread can access the hash table and any one of the other buckets.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
#http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;br /&gt;
#http://dl.acm.org/citation.cfm?id=320078&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73807</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73807"/>
		<updated>2013-02-27T20:24:39Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Fine-Grain Lock Approach */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[19]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
&lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[20]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[20]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations. They are used extensively in data processing as in involves a vast amount of data through the hash table using as few indirection's in the storage structure as possible.   &lt;br /&gt;
&lt;br /&gt;
A single hash table level look can easily become a bottleneck, thus several method were developed to overcome this difficulty. Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list. [[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
There are several parallel implementations of hash tables available that use lock based synchronization. Larson et al. in [11] use two lock levels :&lt;br /&gt;
&lt;br /&gt;
  A scaleable hash table for shared memory multi-processor (SMP) supports very high rates of concurrent operations (e.g., insert, delete, &lt;br /&gt;
and lookup), while simultaneously reducing cache misses. The SMP system has a memory subsystem and a processor subsystem interconnected &lt;br /&gt;
via a bus structure. The hash table is stored in the memory subsystem to facilitate access to data items. The hash table is segmented &lt;br /&gt;
into multiple buckets, with each bucket containing a reference to a linked list of bucket nodes that hold references to data items with &lt;br /&gt;
keys that hash to a common value. Individual bucket nodes contain multiple signature-pointer pairs that reference corresponding data items.&lt;br /&gt;
Each signature-pointer pair has a hash signature computed from a key of the data item and a pointer to the data item. The first bucket &lt;br /&gt;
node in the linked list for each of the buckets is stored in the hash table.&lt;br /&gt;
&lt;br /&gt;
   To enable multithread access, while serializing operation of the table, the SMP system utilizes two levels of locks: a table lock and&lt;br /&gt;
multiple bucket locks. The table lock allows access by a single processing thread to the table while blocking access for other processing&lt;br /&gt;
threads. The table lock is held just long enough for the thread to acquire the bucket lock of a particular bucket node. Once the table lock&lt;br /&gt;
is released, another thread can access the hash table and any one of the other buckets.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
#http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;br /&gt;
#http://dl.acm.org/citation.cfm?id=320078&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73806</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73806"/>
		<updated>2013-02-27T20:24:25Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Global Lock Approach */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[19]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
&lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[20]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations. They are used extensively in data processing as in involves a vast amount of data through the hash table using as few indirection's in the storage structure as possible.   &lt;br /&gt;
&lt;br /&gt;
A single hash table level look can easily become a bottleneck, thus several method were developed to overcome this difficulty. Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list. [[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
There are several parallel implementations of hash tables available that use lock based synchronization. Larson et al. in [11] use two lock levels :&lt;br /&gt;
&lt;br /&gt;
  A scaleable hash table for shared memory multi-processor (SMP) supports very high rates of concurrent operations (e.g., insert, delete, &lt;br /&gt;
and lookup), while simultaneously reducing cache misses. The SMP system has a memory subsystem and a processor subsystem interconnected &lt;br /&gt;
via a bus structure. The hash table is stored in the memory subsystem to facilitate access to data items. The hash table is segmented &lt;br /&gt;
into multiple buckets, with each bucket containing a reference to a linked list of bucket nodes that hold references to data items with &lt;br /&gt;
keys that hash to a common value. Individual bucket nodes contain multiple signature-pointer pairs that reference corresponding data items.&lt;br /&gt;
Each signature-pointer pair has a hash signature computed from a key of the data item and a pointer to the data item. The first bucket &lt;br /&gt;
node in the linked list for each of the buckets is stored in the hash table.&lt;br /&gt;
&lt;br /&gt;
   To enable multithread access, while serializing operation of the table, the SMP system utilizes two levels of locks: a table lock and&lt;br /&gt;
multiple bucket locks. The table lock allows access by a single processing thread to the table while blocking access for other processing&lt;br /&gt;
threads. The table lock is held just long enough for the thread to acquire the bucket lock of a particular bucket node. Once the table lock&lt;br /&gt;
is released, another thread can access the hash table and any one of the other buckets.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
#http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;br /&gt;
#http://dl.acm.org/citation.cfm?id=320078&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73804</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73804"/>
		<updated>2013-02-27T20:23:30Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[19]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
&lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations. They are used extensively in data processing as in involves a vast amount of data through the hash table using as few indirection's in the storage structure as possible.   &lt;br /&gt;
&lt;br /&gt;
A single hash table level look can easily become a bottleneck, thus several method were developed to overcome this difficulty. Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list. [[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
There are several parallel implementations of hash tables available that use lock based synchronization. Larson et al. in [11] use two lock levels :&lt;br /&gt;
&lt;br /&gt;
  A scaleable hash table for shared memory multi-processor (SMP) supports very high rates of concurrent operations (e.g., insert, delete, and lookup), while simultaneously reducing cache misses. The SMP system has a memory subsystem and a processor subsystem interconnected via a bus structure. The hash table is stored in the memory subsystem to facilitate access to data items. The hash table is segmented into multiple buckets, with each bucket containing a reference to a linked list of bucket nodes that hold references to data items with keys that hash to a common value. Individual bucket nodes contain multiple signature-pointer pairs that reference corresponding data items. Each signature-pointer pair has a hash signature computed from a key of the data item and a pointer to the data item. The first bucket node in the linked list for each of the buckets is stored in the hash table.&lt;br /&gt;
&lt;br /&gt;
   To enable multithread access, while serializing operation of the table, the SMP system utilizes two levels of locks: a table lock and multiple bucket locks. The table lock allows access by a single processing thread to the table while blocking access for other processing threads. The table lock is held just long enough for the thread to acquire the bucket lock of a particular bucket node. Once the table lock is released, another thread can access the hash table and any one of the other buckets.&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
#http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;br /&gt;
#http://dl.acm.org/citation.cfm?id=320078&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73802</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73802"/>
		<updated>2013-02-27T20:19:50Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Concurrency Problem in LDS */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers===&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[19]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
&lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
#http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73801</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73801"/>
		<updated>2013-02-27T20:19:30Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallelization among Readers */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers=== [[#References|&amp;lt;sup&amp;gt;[19]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
&lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
#http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73800</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73800"/>
		<updated>2013-02-27T20:18:39Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers=== &lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
 &lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
#http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73799</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73799"/>
		<updated>2013-02-27T20:18:27Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers=== &lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
 &lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;br /&gt;
http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73798</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73798"/>
		<updated>2013-02-27T20:13:17Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers=== &lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
 &lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices. The goal is to find the shortest path connecting each region.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73797</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73797"/>
		<updated>2013-02-27T20:05:27Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers=== &lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
 &lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73796</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73796"/>
		<updated>2013-02-27T20:04:25Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers=== &lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
 &lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
[[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73795</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73795"/>
		<updated>2013-02-27T20:03:05Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Trees */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers=== &lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
 &lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
In the following sections we describe the problems related to various LDS and some of their possible solutions.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]][[#References|&amp;lt;sup&amp;gt;[18]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73791</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73791"/>
		<updated>2013-02-27T20:00:36Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
   Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers=== &lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
 &lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73790</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73790"/>
		<updated>2013-02-27T20:00:18Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Graphs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Chapter 5a CSC/ECE 506 Spring 2013 / Other linked data structures =&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Linked Data Structures(LDS) consists of different types of data structures such as linked lists, trees, hash tables and graphs. Although each structure is diversed, LDS traversal shares a common characteristic in reading a node and discovering the other nodes it points to. Hence, this often introduces loop carried dependence. Chapter 5 of Solihin, discusses various algorithms on parallelizing LDS using a simple linked list. In this wiki, we attempt to cover other LDS such as trees, hashes and graphs, and how the parallelization algorithms discussed can be applied to these structures. This wiki explores concurrency problems related to each type and possible solutions for parallelizing them.&lt;br /&gt;
&lt;br /&gt;
= Concurrency Problem in LDS =&lt;br /&gt;
&lt;br /&gt;
Non-serializable outcomes can offen occur  while attempting to parallelize LDS in scenarios like :&lt;br /&gt;
&lt;br /&gt;
   Parallel execution of two operations that access a common node, in which at least one operation involves  writing to the node, &lt;br /&gt;
can produce conflicts that lead to non- serializable outcome. Conflicts can occur between LDS operations and memory management &lt;br /&gt;
functions such as memory de-allocation and allocation.  &lt;br /&gt;
&lt;br /&gt;
Solihin discusses three approaches using locks to resolve these issues namely :&lt;br /&gt;
&lt;br /&gt;
===Parallelization among Readers=== &lt;br /&gt;
This is achieved by ensuring mutual exclusion between a read-write and a read-only operation, and not between two read-only operations. This is achieved by defining a read lock and a write lock.&lt;br /&gt;
&lt;br /&gt;
'''Lock compatibility : '''	&lt;br /&gt;
                               Read Lock requested	Write Lock requested&lt;br /&gt;
           Read Locked	          Yes	                       No&lt;br /&gt;
           Write Locked	           No	                       No&lt;br /&gt;
 &lt;br /&gt;
===Global Lock Approach===&lt;br /&gt;
A higher degree of parallelism can be obtained by maintaining a single global lock for each LDS. This however can support only 1 thread modifying a list at any given time.&lt;br /&gt;
&lt;br /&gt;
===Fine-Grain Lock Approach===&lt;br /&gt;
The Fine-Grain lock approach resolves the previous restriction of only sequentially modifying a given list by maintaining locks for each node. Hence, this is a much more tedious approach. The principle here is that, nodes that are modified must be write locked, and nodes that are being read and hence must remain valid are read locked.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Every set of vertices in the same distance from the source is assigned to a processor. This set of vertices is called a regional set of vertices.&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2012/ch5a_ja&amp;diff=73788</id>
		<title>CSC/ECE 506 Spring 2012/ch5a ja</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2012/ch5a_ja&amp;diff=73788"/>
		<updated>2013-02-27T19:59:57Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
This part we implement a parallel algorithm with the same functionality as the serial traversal. &lt;br /&gt;
First, we define a function NEXT[x].  The function NEXT[ x,], defining the successor of a field x, (of node x) in the Euler tour, is given as&lt;br /&gt;
&lt;br /&gt;
[[File: para_tree1.PNG]]&lt;br /&gt;
&lt;br /&gt;
Note that this NEXT function does not provide the successor of the last field of the root-node r in the tour. This is because we break the tour into a linked list such that u, is the starting field and r is the terminal field, where m = no-of-chiidren[r] + 1. The proposed linked list structure can be represented as an array [I . 2n - l] of SNODEREC:&lt;br /&gt;
&lt;br /&gt;
[[File: para2.PNG]]&lt;br /&gt;
This data structure can be constructed from the following algorithm when the input tree is represented&lt;br /&gt;
by a “parent-of” relation with explicit ordering of children. Each field of a tree-node in this algorithm is a record of type SNODEREC.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT&lt;br /&gt;
[[File: para3.PNG]]&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT: &lt;br /&gt;
&lt;br /&gt;
pre-order traversal: select the first copy of&lt;br /&gt;
each node;&lt;br /&gt;
&lt;br /&gt;
post-order traversal: select the last copy of&lt;br /&gt;
each node;&lt;br /&gt;
&lt;br /&gt;
in-order traversal: delete the first copy of each node if it is not&lt;br /&gt;
a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;br /&gt;
#http://www.shodor.org/petascale/materials/UPModules/Binary_Tree_Traversal&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73781</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73781"/>
		<updated>2013-02-27T19:42:22Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
Here, since each processor is assigned an independent sub-tree, elaborate locks are not required.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
   pre-order traversal: select the first copy of each node;&lt;br /&gt;
   post-order traversal: select the last copy of each node;&lt;br /&gt;
   in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
This linked list can be locked while editing as per the LDS chapter in Solihin book. Either a Global lock approach, Fine Grained approach or Read-Write Locks can be used.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73780</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73780"/>
		<updated>2013-02-27T18:44:05Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT for Parallel Tree Traversal:&lt;br /&gt;
   for all Pi, 1&amp;lt;=i&amp;lt;=n, do&lt;br /&gt;
     parallel begin&lt;br /&gt;
        Step 1: Processor Pi builds the jth field of i's parent node if i is the jth child of its parent.&lt;br /&gt;
                The jth field (if it is not the last field) is stored in the ith index of array SUPERNODE.&lt;br /&gt;
        Step 2: Processor Pi builds node i's last fiend whose array index is (n+1)&lt;br /&gt;
     parallel end&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT:&lt;br /&gt;
pre-order traversal: select the first copy of each node;&lt;br /&gt;
post-order traversal: select the last copy of each node;&lt;br /&gt;
in-order traversal: delete the first copy of each node if it is not a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Parallel_tree.png&amp;diff=73779</id>
		<title>File:Parallel tree.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Parallel_tree.png&amp;diff=73779"/>
		<updated>2013-02-27T18:32:46Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: uploaded a new version of &amp;amp;quot;File:Parallel tree.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Parallel_tree.png&amp;diff=73778</id>
		<title>File:Parallel tree.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Parallel_tree.png&amp;diff=73778"/>
		<updated>2013-02-27T18:29:02Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: uploaded a new version of &amp;amp;quot;File:Parallel tree.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Parallel_tree.png&amp;diff=73777</id>
		<title>File:Parallel tree.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Parallel_tree.png&amp;diff=73777"/>
		<updated>2013-02-27T18:26:02Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73776</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73776"/>
		<updated>2013-02-27T18:25:31Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
      but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73775</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73775"/>
		<updated>2013-02-27T18:25:12Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above,&lt;br /&gt;
but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73774</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73774"/>
		<updated>2013-02-27T18:24:29Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
   3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
   1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
   2. Whenever you get to a node that is only present on one processor, ask that processor to execute the Breadth-First C algorithm detailed above, but wait after it finishes one generation.&lt;br /&gt;
   3. Combine all the one-generation results from the different processors in the correct order.&lt;br /&gt;
   4. Allow each processor to execute the next generation of the Breadth-First C algorithm detailed above, and then wait again.&lt;br /&gt;
   5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73773</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73773"/>
		<updated>2013-02-27T18:22:14Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Parallel Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
2. Whenever you get to a node that is only present on one processor, ask that processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
3. The processor will return its result that can be used exactly as if it was a serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
2. Whenever you get to a node that is only present on one processor, ask that &lt;br /&gt;
processor to execute the Breadth-First C algorithm detailed above, but wait&lt;br /&gt;
after it finishes one generation.&lt;br /&gt;
3. Combine all the one-generation results from the different processors in the &lt;br /&gt;
correct order.&lt;br /&gt;
4. Allow each processor to execute the next generation of the Breadth-First C &lt;br /&gt;
algorithm detailed above, and then wait again.&lt;br /&gt;
5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73772</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73772"/>
		<updated>2013-02-27T18:21:23Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
In many ways, a tree is the perfect candidate for parallelism.  In a tree, each &lt;br /&gt;
node/subtree is independent.  As a result, we can split up a large tree into 2, 4, 8, or &lt;br /&gt;
more subtrees and hold subtree on each processor.  Then, the only duplicated data &lt;br /&gt;
that must be kept on all processors is the tiny tip of the tree that is the parent of all &lt;br /&gt;
of the individual subtrees.  Mathematically speaking, for a tree divided among n&lt;br /&gt;
processors (where n is a power of two), the processors only need to hold n – 1 nodes &lt;br /&gt;
in common – no matter how big the tree itself is. &lt;br /&gt;
&lt;br /&gt;
The fact that trees are comprised of independent sub-trees makes parallelizing them &lt;br /&gt;
very easy.  Properly done, the portion of these traversals that is parallelizable grows &lt;br /&gt;
at 2n for an n-generation tree, while the processors only need to synchronize once, at &lt;br /&gt;
the end, so it approaches 100% for large trees (but keep in mind Amdahl’s Law, &lt;br /&gt;
footnote 1).  The basic steps for parallelizing these traversals are as follows:&lt;br /&gt;
&lt;br /&gt;
1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
2. Whenever you get to a node that is only present on one processor, ask that &lt;br /&gt;
    processor to execute the appropriate C algorithm detailed above.&lt;br /&gt;
3. The processor will return its result that can be used exactly as if it was a &lt;br /&gt;
    serial processor.&lt;br /&gt;
&lt;br /&gt;
[[File:parallel_tree.png]]&lt;br /&gt;
&lt;br /&gt;
A Breadth-First traversal is somewhat more complicated to implement as a &lt;br /&gt;
parallel system because at each level, it must access nodes from all of the parallel &lt;br /&gt;
processors.  Theoretically, a Breadth-First traversal can achieve the same 100% &lt;br /&gt;
speedup of Pre-, In-, and Post-Order traversals.  However, the amount of processor to&lt;br /&gt;
processor data transmission adds in a greater potential for delays, thus slowing &lt;br /&gt;
down the algorithm.  Nevertheless, as the size of the tree increases the size of the &lt;br /&gt;
generations grows at the rate of 2n while the number of synchronizations grows at a &lt;br /&gt;
rate of n for an n-generation tree, so the parallelizable portion of these traversals &lt;br /&gt;
also approaches 100%.  The basic steps for parallelizing this traversal are as &lt;br /&gt;
follows:&lt;br /&gt;
&lt;br /&gt;
1. Perform the traversal on the parent part of the tree.&lt;br /&gt;
2. Whenever you get to a node that is only present on one processor, ask that &lt;br /&gt;
processor to execute the Breadth-First C algorithm detailed above, but wait&lt;br /&gt;
after it finishes one generation.&lt;br /&gt;
3. Combine all the one-generation results from the different processors in the &lt;br /&gt;
correct order.&lt;br /&gt;
4. Allow each processor to execute the next generation of the Breadth-First C &lt;br /&gt;
algorithm detailed above, and then wait again.&lt;br /&gt;
5. Repeat Steps 3 and 4 until there are no nodes remaining&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73771</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73771"/>
		<updated>2013-02-27T17:39:54Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
   end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
This part we implement a parallel algorithm with the same functionality as the serial traversal. &lt;br /&gt;
First, we define a function NEXT[x].  The function NEXT[ x,], defining the successor of a field x, (of node x) in the Euler tour, is given as&lt;br /&gt;
&lt;br /&gt;
[[File: para_tree1.PNG]]&lt;br /&gt;
&lt;br /&gt;
Note that this NEXT function does not provide the successor of the last field of the root-node r in the tour. This is because we break the tour into a linked list such that u, is the starting field and r is the terminal field, where m = no-of-chiidren[r] + 1. The proposed linked list structure can be represented as an array [I . 2n - l] of SNODEREC:&lt;br /&gt;
&lt;br /&gt;
[[File: para2.PNG]]&lt;br /&gt;
This data structure can be constructed from the following algorithm when the input tree is represented&lt;br /&gt;
by a “parent-of” relation with explicit ordering of children. Each field of a tree-node in this algorithm is a record of type SNODEREC.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT&lt;br /&gt;
[[File: para3.PNG]]&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT: &lt;br /&gt;
&lt;br /&gt;
pre-order traversal: select the first copy of&lt;br /&gt;
each node;&lt;br /&gt;
&lt;br /&gt;
post-order traversal: select the last copy of&lt;br /&gt;
each node;&lt;br /&gt;
&lt;br /&gt;
in-order traversal: delete the first copy of each node if it is not&lt;br /&gt;
a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73770</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73770"/>
		<updated>2013-02-27T17:38:14Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
This part we implement a parallel algorithm with the same functionality as the serial traversal. &lt;br /&gt;
First, we define a function NEXT[x].  The function NEXT[ x,], defining the successor of a field x, (of node x) in the Euler tour, is given as&lt;br /&gt;
&lt;br /&gt;
[[File: para_tree1.PNG]]&lt;br /&gt;
&lt;br /&gt;
Note that this NEXT function does not provide the successor of the last field of the root-node r in the tour. This is because we break the tour into a linked list such that u, is the starting field and r is the terminal field, where m = no-of-chiidren[r] + 1. The proposed linked list structure can be represented as an array [I . 2n - l] of SNODEREC:&lt;br /&gt;
&lt;br /&gt;
[[File: para2.PNG]]&lt;br /&gt;
This data structure can be constructed from the following algorithm when the input tree is represented&lt;br /&gt;
by a “parent-of” relation with explicit ordering of children. Each field of a tree-node in this algorithm is a record of type SNODEREC.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT&lt;br /&gt;
[[File: para3.PNG]]&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT: &lt;br /&gt;
&lt;br /&gt;
pre-order traversal: select the first copy of&lt;br /&gt;
each node;&lt;br /&gt;
&lt;br /&gt;
post-order traversal: select the last copy of&lt;br /&gt;
each node;&lt;br /&gt;
&lt;br /&gt;
in-order traversal: delete the first copy of each node if it is not&lt;br /&gt;
a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73769</id>
		<title>CSC/ECE 506 Spring 2013/5a ks</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_506_Spring_2013/5a_ks&amp;diff=73769"/>
		<updated>2013-02-27T14:47:12Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: Created page with &amp;quot;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==  An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures suc...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Chapter 5a CSC/ECE 506 Spring 2012 / ch5a ==&lt;br /&gt;
&lt;br /&gt;
An exploration and summary of parallel optimization and concurrency issues as it relates to linked-list based data structures such as hash tables, trees, and graphs.  This topic examines concurrency problems related to each type and possible solutions to allow for parallelization.&lt;br /&gt;
&lt;br /&gt;
= Introduction to Linked-List Parallel Programming =&lt;br /&gt;
&lt;br /&gt;
One component that tends to link together various data structures is their reliance at some level on an internal pointer-based linked list.  For example, hash tables have linked lists to support chained links to a given bucket in order to resolve collisions, trees have linked lists with left and right tree node paths, and graphs have linked lists to determine shortest path algorithms.&lt;br /&gt;
&lt;br /&gt;
But what mechanism allows us to generate parallel algorithms for these structures?  &lt;br /&gt;
&lt;br /&gt;
For an array processing algorithm, a common technique used at the processor level is the copy-scan technique.  This technique involves copying rows of data from one processor to another in a log(n) fashion until all processors have their own copy of that row.  From there, you could perform a reduction technique to generate a sum of all the data, all while working in a parallel fashion.  Take the following grid:[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The basic process for copy-scan would be to:&lt;br /&gt;
  Step 1) Copy the row 1 array to row 2.&lt;br /&gt;
  Step 2) Copy the row 1 array to row 3, row 2 to row 4, etc on the next run.&lt;br /&gt;
  Step 3) Continue in this manner until all rows have been copied in a log(n) fashion.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result (reduction for sum, etc.).&lt;br /&gt;
&lt;br /&gt;
[[File:CopyScan.gif]]&lt;br /&gt;
&lt;br /&gt;
But how does this same process work in the linked list world?&lt;br /&gt;
&lt;br /&gt;
With linked lists, there is a concept called pointer doubling, which works in a very similar manner to copy-scan.[[#References|&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  Step 1) Each processor will make a copy of the pointer it holds to it's neighbor.&lt;br /&gt;
  Step 2) Next, each processor will make a pointer to the processor 2 steps away.&lt;br /&gt;
  Step 3) This continues in logarithmic fashion until each processor has a pointer to the end of the chain.&lt;br /&gt;
  Step 4) Perform the parallel operations to generate the desired result.&lt;br /&gt;
&lt;br /&gt;
[[File:linkedlist.gif]]&lt;br /&gt;
&lt;br /&gt;
However, with linked list programming, similar to array-based programming, it becomes imperative to have some sort of locking mechanism or other parallel technique for critical sections in order to avoid race conditions.  To make sure the results are correct, it is important that operations can be serialized appropriately and that data remains current and synchronized.&lt;br /&gt;
&lt;br /&gt;
In this chapter, we will explore 3 linked-list based data structures and the parallelization opportunities as well as the concurrency issues they present: hash tables, trees, and graphs.&lt;br /&gt;
&lt;br /&gt;
== Trees ==&lt;br /&gt;
&lt;br /&gt;
=== Tree Intro ===&lt;br /&gt;
&lt;br /&gt;
A tree data structure [[#References|&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;]] contains a set of ordered nodes with one parent node followed by zero or more child nodes.  Typically this tree structure is used with searching or sorting algorithms to achieve log(n) efficiencies.  Assuming you have a balanced tree, or a relatively equal set of nodes under each branching structure of the tree, and assuming a proper ordering structure, searches/inserts/deletes should occur far more quickly than having to traverse an entire list.&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
One potential slowdown in a tree data structure could occur during the traversal process.  Even though search/update/insert can occur in a logarithmic fashion, traversal operations such as in-order, pre-order, post-order traversals can still require a full sequence of the list to generate all output.  This gives an opportunity to generate parallel code by having various portions of the traversal occur on different processors.&lt;br /&gt;
&lt;br /&gt;
=== Serial Code Example ===&lt;br /&gt;
&lt;br /&gt;
Below is a code[[#References|&amp;lt;sup&amp;gt;[8]&amp;lt;/sup&amp;gt;]] for serial tree traversal algorithms&lt;br /&gt;
with behavior as the figure below shows:&lt;br /&gt;
&lt;br /&gt;
[[File: tree.PNG]]&lt;br /&gt;
&lt;br /&gt;
procedure Tree_Traversal is&lt;br /&gt;
   type Node;&lt;br /&gt;
   type Node_Access is access Node;&lt;br /&gt;
   type Node is record&lt;br /&gt;
      Left : Node_Access := null;&lt;br /&gt;
      Right : Node_Access := null;&lt;br /&gt;
      Data : Integer;&lt;br /&gt;
   end record;&lt;br /&gt;
   procedure Destroy_Tree(N : in out Node_Access) is&lt;br /&gt;
      procedure free is new Ada.Unchecked_Deallocation(Node, Node_Access);&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Destroy_Tree(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then &lt;br /&gt;
         Destroy_Tree(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Free(N);&lt;br /&gt;
   end Destroy_Tree;&lt;br /&gt;
   function Tree(Value : Integer; Left : Node_Access; Right : Node_Access) return Node_Access is&lt;br /&gt;
      Temp : Node_Access := new Node;&lt;br /&gt;
   begin&lt;br /&gt;
      Temp.Data := Value;&lt;br /&gt;
      Temp.Left := Left;&lt;br /&gt;
      Temp.Right := Right;&lt;br /&gt;
      return Temp;&lt;br /&gt;
   end Tree;&lt;br /&gt;
   procedure Preorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Preorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Preorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Preorder;&lt;br /&gt;
   procedure Inorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Inorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Inorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
   end Inorder;&lt;br /&gt;
   procedure Postorder(N : Node_Access) is&lt;br /&gt;
   begin&lt;br /&gt;
      if N.Left /= null then&lt;br /&gt;
         Postorder(N.Left);&lt;br /&gt;
      end if;&lt;br /&gt;
      if N.Right /= null then&lt;br /&gt;
         Postorder(N.Right);&lt;br /&gt;
      end if;&lt;br /&gt;
      Put(Integer'Image(N.Data));&lt;br /&gt;
   end Postorder;&lt;br /&gt;
   procedure Levelorder(N : Node_Access) is&lt;br /&gt;
      package Queues is new Ada.Containers.Doubly_Linked_Lists(Node_Access);&lt;br /&gt;
      use Queues;&lt;br /&gt;
      Node_Queue : List;&lt;br /&gt;
      Next : Node_Access;&lt;br /&gt;
   begin&lt;br /&gt;
      Node_Queue.Append(N);&lt;br /&gt;
      while not Is_Empty(Node_Queue) loop&lt;br /&gt;
         Next := First_Element(Node_Queue);&lt;br /&gt;
         Delete_First(Node_Queue);&lt;br /&gt;
         Put(Integer'Image(Next.Data));&lt;br /&gt;
         if Next.Left /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Left);&lt;br /&gt;
         end if;&lt;br /&gt;
         if Next.Right /= null then&lt;br /&gt;
            Node_Queue.Append(Next.Right);&lt;br /&gt;
         end if;&lt;br /&gt;
      end loop;&lt;br /&gt;
   end Levelorder;&lt;br /&gt;
   N : Node_Access;&lt;br /&gt;
begin&lt;br /&gt;
   N := Tree(1, &lt;br /&gt;
      Tree(2,&lt;br /&gt;
         Tree(4,&lt;br /&gt;
            Tree(7, null, null),&lt;br /&gt;
            null),&lt;br /&gt;
         Tree(5, null, null)),&lt;br /&gt;
      Tree(3,&lt;br /&gt;
         Tree(6,&lt;br /&gt;
            Tree(8, null, null),&lt;br /&gt;
            Tree(9, null, null)),&lt;br /&gt;
         null));&lt;br /&gt;
 &lt;br /&gt;
   Put(&amp;quot;preorder:    &amp;quot;);&lt;br /&gt;
   Preorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;inorder:     &amp;quot;);&lt;br /&gt;
   Inorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;postorder:   &amp;quot;);&lt;br /&gt;
   Postorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Put(&amp;quot;level order: &amp;quot;);&lt;br /&gt;
   Levelorder(N);&lt;br /&gt;
   New_Line;&lt;br /&gt;
   Destroy_Tree(N);&lt;br /&gt;
end Tree_traversal;&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
This part we implement a parallel algorithm with the same functionality as the serial traversal. &lt;br /&gt;
First, we define a function NEXT[x].  The function NEXT[ x,], defining the successor of a field x, (of node x) in the Euler tour, is given as&lt;br /&gt;
&lt;br /&gt;
[[File: para_tree1.PNG]]&lt;br /&gt;
&lt;br /&gt;
Note that this NEXT function does not provide the successor of the last field of the root-node r in the tour. This is because we break the tour into a linked list such that u, is the starting field and r is the terminal field, where m = no-of-chiidren[r] + 1. The proposed linked list structure can be represented as an array [I . 2n - l] of SNODEREC:&lt;br /&gt;
&lt;br /&gt;
[[File: para2.PNG]]&lt;br /&gt;
This data structure can be constructed from the following algorithm when the input tree is represented&lt;br /&gt;
by a “parent-of” relation with explicit ordering of children. Each field of a tree-node in this algorithm is a record of type SNODEREC.&lt;br /&gt;
&lt;br /&gt;
Algorithm GEN-COMP-NEXT&lt;br /&gt;
[[File: para3.PNG]]&lt;br /&gt;
&lt;br /&gt;
To obtain the required tree-traversals, the following rules are operated on the linked list produced by algorithm GEN-COMP-NEXT: &lt;br /&gt;
&lt;br /&gt;
pre-order traversal: select the first copy of&lt;br /&gt;
each node;&lt;br /&gt;
&lt;br /&gt;
post-order traversal: select the last copy of&lt;br /&gt;
each node;&lt;br /&gt;
&lt;br /&gt;
in-order traversal: delete the first copy of each node if it is not&lt;br /&gt;
a leaf and delete the last copy of each node if it has more than one child.&lt;br /&gt;
&lt;br /&gt;
In this fashion we have broken up the linked list of the tree into successive parts and imposed a divide-and-conquer technique to complete the traversal.&lt;br /&gt;
&lt;br /&gt;
== Hash Tables ==&lt;br /&gt;
=== Hash Table Intro ===&lt;br /&gt;
&lt;br /&gt;
Hash tables[[#References|&amp;lt;sup&amp;gt;[4]&amp;lt;/sup&amp;gt;]] are very efficient data structures often used in searching algorithms for fast lookup operations.  Hash tables contain a series of &amp;quot;buckets&amp;quot; that function like indexes into an array, each of which can be accessed directly using their key value.  The bucket for which a piece of data will be placed is determine by a special hashing function.&lt;br /&gt;
&lt;br /&gt;
The major advantage of a hash table is that lookup times are essentially a constant value, much like an array with a known index.  With a proper hashing function in place, it should be fairly rare that any 2 keys would generate the same value.&lt;br /&gt;
&lt;br /&gt;
In the case that 2 keys do map to the same position, there is a conflict that must be dealt with in some fashion to obtain the correct value.  One way that is relevant to linked list structures is to have a chained hash table in which a linked list is created with all values that have been placed in that particular bucket.  The developer would have to not only take into account the proper bucket for the data being searched for, but also must considered the chained linked list.[[#References|&amp;lt;sup&amp;gt;[7]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:315px-Hash table 3 1 1 0 1 0 0 SP.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Hash tables can be very well suited to parallel applications.  For example, system code responsible for caching between multiple processors could itself be an ideal opportunity for a shared hashmap.  Each processor sharing one common cache would be able to access the relevant information all in one location.&lt;br /&gt;
&lt;br /&gt;
This would, however, involve a good bit of synchronization, as each processor would need to wait in case a lock was being placed on a specific bucket in the cache hashmap.  Unfortunately, traditional locking would be a bad solution to this problem as processors need to run very quickly.  Having to wait for locks would destroy the application processing time.  The need for a non-locking solution is critical to performance.&lt;br /&gt;
&lt;br /&gt;
In Java, the standard class utilized for hashing is the HashMap[[#References|&amp;lt;sup&amp;gt;[17]&amp;lt;/sup&amp;gt;]] class.  This class has a fundamental weakness though in that the entire map requires synchronization prior to each access.  This causes a lot of contention and many bottlenecks on a parallel machine.&lt;br /&gt;
&lt;br /&gt;
Below, I will present a Java-based solution to this problem by using a ConcurrentHashMap class.  This class only requires a portion of the map to be locked and reads can generally occur with no locking whatsoever.&lt;br /&gt;
&lt;br /&gt;
=== HashMap Code with Locking ===&lt;br /&gt;
&lt;br /&gt;
'''  Simple synchronized example to increment a counter.'''[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
  private Map&amp;lt;String,Integer&amp;gt; queryCounts = new HashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private '''synchronized''' void incrementCount(String q) {&lt;br /&gt;
    Integer cnt = queryCounts.get(q);&lt;br /&gt;
    if (cnt == null) {&lt;br /&gt;
      queryCounts.put(q, 1);&lt;br /&gt;
    } else {&lt;br /&gt;
      queryCounts.put(q, cnt + 1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code was written using an ordinary HashMap data structure.  Notice that we use the synchronized keyword here to signify that only one thread can enter this function at any one point in time.  With a really large number of threads, however, waiting to enter the synchronized operation could be a major bottleneck.&lt;br /&gt;
&lt;br /&gt;
'''  Iterator example for synchronized HashMap.'''&lt;br /&gt;
&lt;br /&gt;
  Map m = Collections.synchronizedMap(new HashMap());&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  synchronized(m) { // synchronizing on map&lt;br /&gt;
    Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
    while (i.hasNext())&lt;br /&gt;
      foo(i.next());&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In the above example, we show how an iterator could be used to traverse over a map.  In this case, we would need to utilize the synchronizedMap function available in the Collections interface.  Also, as you may notice, once the iterator code begins we must actually synchronize on the entire map in order to iterate through the results.  But what if several processors wish to iterate through at the same time?&lt;br /&gt;
&lt;br /&gt;
=== Parallel Code Solution ===&lt;br /&gt;
&lt;br /&gt;
The key issue in a hash table in a parallel environment is to make sure any update/insert/delete sequences have been completed properly prior to attempting subsequent operations to make sure the data has been synched appropriately.  However, since access speed is such a critical component of the design of a hash table, it is essential to try and avoid using too many locks for performing synchronization.  Fortunately, a number of lock-free hash designs have been implemented to avoid this bottleneck.&lt;br /&gt;
&lt;br /&gt;
One such example in Java is the ConcurrentHashMap[[#References|&amp;lt;sup&amp;gt;[9]&amp;lt;/sup&amp;gt;]], which acts as a synchronized version of the HashMap.  With this structure, there is full concurrency of retrievals and adjustable expected concurrency for updates.  There is, however, no locking in this data structure and retrievals will typically run in parallel along with updates/deletes.  Retrievals, however, will receive all most recently completed transactions even if it cannot get the values that haven't finished being updated.  This both allows for efficiency and greater concurrency.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Counter Increment Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  private ConcurrentMap&amp;lt;String,Integer&amp;gt; queryCounts =&lt;br /&gt;
    new ConcurrentHashMap&amp;lt;String,Integer&amp;gt;(1000);&lt;br /&gt;
  private void incrementCount(String q) {&lt;br /&gt;
    Integer oldVal, newVal;&lt;br /&gt;
    do {&lt;br /&gt;
      oldVal = queryCounts.get(q);&lt;br /&gt;
      newVal = (oldVal == null) ? 1 : (oldVal + 1);&lt;br /&gt;
    } while (!queryCounts.replace(q, oldVal, newVal));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The above code snippet represents an alternative to the serial option presented in the previous section, while also avoiding much of the locking that takes place using the synchronized functions or synchronized blocks.  With ConcurrentHashMap, however, notice that we must implement some new code in order to handle the fact that a variety of inserts/updates could be running at the same time.  The replace() function here acts much like a compare-and-set operation typically used with concurrent code.  Basically, the value would be changed only if not equal to the previously mapped value.  This is much more efficient that locking the entire function as we often do not expect unequal values.&lt;br /&gt;
&lt;br /&gt;
'''Parallel Traversal Alternative:'''&lt;br /&gt;
&lt;br /&gt;
  Map m = new ConcurrentHashMap();&lt;br /&gt;
  Set s = m.keySet(); // set of keys in hashmap&lt;br /&gt;
  Iterator i = s.iterator(); // Must be in synchronized block&lt;br /&gt;
  while (i.hasNext())&lt;br /&gt;
    foo(i.next());&lt;br /&gt;
&lt;br /&gt;
In the case of a traversal, recall that ConcurrentHashMaps require to locking on read operations.  Thus we can actually remove the synchronized condition here and iterate in a normal fashion.&lt;br /&gt;
&lt;br /&gt;
== Graphs ==&lt;br /&gt;
=== Graph Intro ===&lt;br /&gt;
&lt;br /&gt;
A graph data structure[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] is another type of linked-list structure that focuses on data relationships and the most efficient ways to traverse from one node to another.  For example, in a networking application, one network node may have connections to a variety of other network nodes.  These nodes then also link to a variety of other nodes in the network.  Using this connection of nodes, it would be possible to then find a path from one specific node to another in the chain.  This could be accomplished by having each node contain a linked list of pointers to all other reachable nodes.&lt;br /&gt;
&lt;br /&gt;
[[File:250px-6n-graf.svg.png]]&lt;br /&gt;
&lt;br /&gt;
=== Opportunities for Parallelization ===&lt;br /&gt;
&lt;br /&gt;
Graphs[[#References|&amp;lt;sup&amp;gt;[10]&amp;lt;/sup&amp;gt;]] consist of a finite set of ordered pairs called edges or arcs, of certain entities called nodes or vertices.  From one given vertex, one would typically want to order the different paths from one vertex to another using it's list of edges or, more than likely, would be interested in the fastest means of getting from one of these vertexes to some sort of destination vertex.&lt;br /&gt;
&lt;br /&gt;
Graph nodes typically will keep their list of edges in a linked list.  Also, when attempting to create a shortest path algorithm on the fly, the graph will typically use a combination of a linked list to represent the path as it's being built, along with a queue that is used for each step of that process.  Synchronizing all of these can be a major challenge.&lt;br /&gt;
&lt;br /&gt;
Much like the hash table, graphs cannot afford to be slow and must often generate results in a very efficient manner.  Having to lock on each list of edges or locking on a shortest path list would really be a major obstacle.&lt;br /&gt;
&lt;br /&gt;
Certainly though, the need for parallel processing becomes critical when you consider, for example, that social networking has become such a major proponent of graph algorithms.  Facebook now has roughly a billion users[[#References|&amp;lt;sup&amp;gt;[15]&amp;lt;/sup&amp;gt;]] and each user has series of friend links that must be analyzed and examined.  This list just keeps growing and growing.&lt;br /&gt;
&lt;br /&gt;
One of the most significant opportunities for a parallel algorithm with a graph data structure is with the traversal algorithms.  We can use Breadth-First search[[#References|&amp;lt;sup&amp;gt;[16]&amp;lt;/sup&amp;gt;]] as an example of this, starting from an initial node and expanding outwards until reaching the destination node.&lt;br /&gt;
&lt;br /&gt;
=== Breadth First Search - Serial Version ===&lt;br /&gt;
&lt;br /&gt;
The following shows a sample of a bread first algorithm which traverses from the city of Frankfurt to Augsburg and Stuttgart Germany.  In does so, the graph begins at a root node (Frankfurt) and expands outwardly to all connected nodes on each step.  From there, each of those nodes proceeds to expand the search outwards until all nodes have been covered.&lt;br /&gt;
&lt;br /&gt;
[[File:GermanyBFS.png]][[#References|&amp;lt;sup&amp;gt;[13]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following code snippet implements a BFS search function.  This function utilized coloring schemes and marks to denote that a node has been visited.  It begins with an initial vertex in a queue and expands outward to all it's successors until no further elements remain unmarked.[[#References|&amp;lt;sup&amp;gt;[12]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
  public void search (Graph g)&lt;br /&gt;
  {&lt;br /&gt;
    g.paint(Color.white);   // paint all the graph vertices with white&lt;br /&gt;
    g.mark(false);          // unmark the whole graph&lt;br /&gt;
    refresh(null);          // and redraw it&lt;br /&gt;
    Vertex r = g.root();	// the root is painted grey&lt;br /&gt;
    g.paint(r, Color.gray);       refresh(g.box(r));&lt;br /&gt;
    java.util.Vector queue = new java.util.Vector();	&lt;br /&gt;
    queue.addElement(r);	// and put in a queue&lt;br /&gt;
    while (!queue.isEmpty())&lt;br /&gt;
    {&lt;br /&gt;
      Vertex u = (Vertex) queue.firstElement();&lt;br /&gt;
      queue.removeElement(u); // extract a vertex from the queue&lt;br /&gt;
      g.mark(u, true);          refresh(g.box(u));&lt;br /&gt;
      int dp = g.degreePlus(u);&lt;br /&gt;
      for (int i = 0; i &amp;lt; dp; i++) // look at its successors&lt;br /&gt;
      {&lt;br /&gt;
        Vertex v = g.ithSucc(i, u);&lt;br /&gt;
        if (Color.white == g.color(v))&lt;br /&gt;
        {		    &lt;br /&gt;
          queue.addElement(v);		    &lt;br /&gt;
          g.paint(v, Color.gray);   refresh(g.box(v));&lt;br /&gt;
        }&lt;br /&gt;
     }&lt;br /&gt;
     g.paint(u, Color.black);  refresh(g.box(u));&lt;br /&gt;
     g.mark(u, false);         refresh(g.box(u));	    &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
=== Parallel Solution ===&lt;br /&gt;
&lt;br /&gt;
But could we introduce parallel mechanisms into this Breadth First search?  The most logical and effective way, instead of utilizing locks and synchronized regions, is to use data parallel techniques during the traversal.  This can be accomplished by having each node of a given breadth search step be sent to a separate processor.  So, using the above example, instead of having Frankfurt-&amp;gt;Mannheim followed by Frankfurt-&amp;gt;Wurzburg followed by Frankfurt-&amp;gt;Bassel on the same processor, Frankfurt could split out all 3 searches in a parallel fashion onto 3 different processors.  Then, possible some cleanup code would be left at the end to visit any remaining untouched nodes.  In a network routing applications, being able to split up the search for each IP address code would make searched significantly faster that allowing one processor to be a bottleneck.&lt;br /&gt;
&lt;br /&gt;
Using locking pseudocode, you might have an algorithm similar to this:&lt;br /&gt;
&lt;br /&gt;
  for all vertices u at level d in parallel do&lt;br /&gt;
    for all adjacencies v of u in parallel do&lt;br /&gt;
    dv = D[v];&lt;br /&gt;
    if (dv &amp;lt; 0) // v is visited for the first time&lt;br /&gt;
      vis = fetch_and_add(&amp;amp;Visited[v], 1);  '''LOCK'''&lt;br /&gt;
      if (vis == 0) // v is added to a stack only once&lt;br /&gt;
        D[v] = d+1;&lt;br /&gt;
        pS[count++] = v; // Add v to local thread stack&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
    if (dv == d + 1)&lt;br /&gt;
      fetch_and_add(&amp;amp;sigma[v], sigma[u]);  '''LOCK'''&lt;br /&gt;
      fetch_and_add(&amp;amp;Pcount[v], 1); // Add u to predecessor list of v  '''LOCK'''&lt;br /&gt;
&lt;br /&gt;
A much better parallel algorithm is represented in the following pseudocode.  Notice that each of the vertices is sent to a separate processor and send/receive operations will eventually sync up the path information.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-code.PNG]][[#References|&amp;lt;sup&amp;gt;[14]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
The following graph also shows how now each of the regional sets of vertices being search can be added to the path in a parallel fashion.&lt;br /&gt;
&lt;br /&gt;
[[File:Parallel-graph.PNG]][[#References|&amp;lt;sup&amp;gt;[11]&amp;lt;/sup&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
= Quiz =&lt;br /&gt;
&lt;br /&gt;
1. Describe the copy-scan technique.&lt;br /&gt;
&lt;br /&gt;
2. Describe the pointer doubling technique.&lt;br /&gt;
&lt;br /&gt;
3. Which concurrency issues are of the most concern in a tree data structure?&lt;br /&gt;
&lt;br /&gt;
4. What is the alternative to using a copy-scan technique in pointer-based programming?&lt;br /&gt;
&lt;br /&gt;
5. Which concurrency issues are of the most concern with hash table data structures?&lt;br /&gt;
&lt;br /&gt;
6. Which concurrency issues are of the most concern with graph data structures?&lt;br /&gt;
&lt;br /&gt;
7. Why would you not want locking mechanisms in hash tables?&lt;br /&gt;
&lt;br /&gt;
8. What is the nature of the linked list in a tree structure?&lt;br /&gt;
&lt;br /&gt;
9. Describe a parallel alternative in the tree data structure.&lt;br /&gt;
&lt;br /&gt;
10. Describe a parallel alternative in a graph data structure.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
#http://people.engr.ncsu.edu/efg/506/s01/lectures/notes/lec8.html&lt;br /&gt;
#http://en.wikipedia.org/wiki/Tree_%28data_structure%29&lt;br /&gt;
#http://oreilly.com/catalog/masteralgoc/chapter/ch08.pdf&lt;br /&gt;
#http://www.devjavasoft.org/code/classhashtable.html&lt;br /&gt;
#http://osr600doc.sco.com/en/SDK_c++/_Intro_graph.html&lt;br /&gt;
#http://web.eecs.utk.edu/~berry/cs302s02/src/code/Chap14/Graph.java&lt;br /&gt;
#http://en.wikipedia.org/wiki/File:Hash_table_3_1_1_0_1_0_0_SP.svg&lt;br /&gt;
#http://rosettacode.org/wiki/Talk:Tree_traversal&lt;br /&gt;
#http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml&lt;br /&gt;
#http://en.wikipedia.org/wiki/Graph_%28abstract_data_type%29&lt;br /&gt;
#http://www.cc.gatech.edu/~bader/papers/PPoPP12/PPoPP-2012-part2.pdf&lt;br /&gt;
#http://renaud.waldura.com/portfolio/graph-algorithms/classes/graph/BFSearch.java&lt;br /&gt;
#http://en.wikipedia.org/w/index.php?title=File%3AGermanyBFS.svg&lt;br /&gt;
#http://sc05.supercomputing.org/schedule/pdf/pap346.pdf&lt;br /&gt;
#http://www.facebook.com/press/info.php?statistics&lt;br /&gt;
#http://en.wikipedia.org/wiki/Breadth-first_search&lt;br /&gt;
#http://code.wikia.com/wiki/Hashmap&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=ECE506_Main_Page&amp;diff=73768</id>
		<title>ECE506 Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=ECE506_Main_Page&amp;diff=73768"/>
		<updated>2013-02-27T14:46:36Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Supplements to Solihin Text */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page serves as a portal for all wiki material related to CSC506 and ECE506. Link to any new wiki pages from this page, and add links to any current pages.&lt;br /&gt;
&lt;br /&gt;
=Supplements to Solihin Text=&lt;br /&gt;
&lt;br /&gt;
Post links to the textbook supplements in this section.&lt;br /&gt;
*Chapter 2 [[CSC/ECE 506 Spring 2011/ch2 dm | CSC/ECE 506 Spring 2011/ch2 dm]]&lt;br /&gt;
*Chapter 2 [[Parallel_Programming_Models | Parallel Programming Models]]&lt;br /&gt;
*Chapter 2 (Still being revised) [[CSC/ECE 506 Spring 2011/ch2 cl | CSC/ECE 506 Spring 2011/ch2 cl]]&lt;br /&gt;
*Chapter 2a [[ CSC/ECE 506 Spring 2011/ch2a mc | Current Data-Parallel Architectures ]]&lt;br /&gt;
*Chapter 2a [[ CSC/ECE 506 Spring_2012/2a va ]]&lt;br /&gt;
*Chapter 2b [[CSC/ECE 506 Spring 2012/ch2b cm | CSC/ECE 506 Spring 2012/ch2b cm]]&lt;br /&gt;
*Chapter 2b [[ECE506_CSC/ECE_506_Spring_2012/2b_az | CSC/ECE 506 Spring 2012/2b az - Data-Parallel Processing with the AMD HD 6900 Series Graphics Processing Unit]]&lt;br /&gt;
*Chapter 3 (Final Revision) [[ CSC/ECE 506 Spring 2011/ch3 ab | Parallel Architecture Mechanisms and Programming Models ]]&lt;br /&gt;
*Chapter 4a[[ CSC/ECE 506 Spring 2011/ch4a ob | Parallelization of Nelder Mead Algorithm ]]&lt;br /&gt;
*Chapter 4a (Under Construction) [[ CSC/ECE_506_Spring_2011/ch4a_bm | Parallelization of Algorithms  ]]&lt;br /&gt;
*Chapter 4a [[ CSC/ECE 506 Spring 2011/ch4a zz | CSC/ECE 506 Spring 2011/ch4a zz ]]&lt;br /&gt;
*Chapter 4b [[Chapter 4b CSC/ECE 506 Spring 2011 / ch4b]]&lt;br /&gt;
*Chapter 5a [[ CSC/ECE 506 Spring 2012/ch5a ja | CSC/ECE 506 Spring 2012/ch5a ja ]]&lt;br /&gt;
*Chapter 9a [[CSC/ECE 506 Spring 2012/ch9a cm | CSC/ECE 506 Spring 2012/ch9a cm]]&lt;br /&gt;
*Chapter 6a (Under Construction) [[ CSC/ECE 506 Spring 2011/ch6a jp | CSC/ECE 506 Spring 2011/ch6a jp ]]&lt;br /&gt;
*Chapter 6a (Under Construction) [[ CSC/ECE 506 Spring 2011/ch6a ep | CSC/ECE 506 Spring 2011/ch6a ep ]]&lt;br /&gt;
*Chapter 6b (Ready for First Review) [[CSC/ECE 506 Spring 2011/ch6b ab | CSC/ECE 506 Spring 2011/ch6b ab]]&lt;br /&gt;
*Chapter 7 (Under Construction) [[CSC/ECE 506 Spring 2011/ch7 jp | CSC/ECE 506 Spring 2011/ch7 jp]]&lt;br /&gt;
*Chapter 8 [[CSC/ECE 506 Spring 2011/ch8 mc | CSC/ECE 506 Spring 2011/ch8 mc]]&lt;br /&gt;
*Chapter 10 (Under Construction) [[CSC/ECE 506 Spring 2011/ch10 sb | CSC/ECE 506 Spring 2011/ch10 sb]]&lt;br /&gt;
*Chapter 10 [[CSC/ECE 506 Spring 2012/ch10 sj | CSC/ECE 506 Spring 2012/ch10 sj]]&lt;br /&gt;
*Chapter 10a [[CSC/ECE_506_Spring_2011/ch10a_dc | CSC/ECE_506_Spring_2011/ch10a_dc]]&lt;br /&gt;
*Chapter 11 [[CSC/ECE_506_Spring_2011/ch11_BB_EP | Chapter 11 Supplement]]&lt;br /&gt;
*Chapter 11 [[Scalable_Coherent_Interface | SCI (Scalable Coherent Interface) ]]&lt;br /&gt;
*Chapter 12 [[ CSC/ECE 506 Spring 2011/ch12 ob | Interconnection Network Topologies and Routing Algorithms]]&lt;br /&gt;
*Chapter 12 (Ready for Final Review) [[ CSC/ECE 506 Spring 2011/ch12 aj | Interconnection Network Topologies and Routing Algorithms]]&lt;br /&gt;
*Chapter 12 [[ CSC/ECE 506 Spring 2011/ch12 | Interconnection Network Topologies]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1a ry]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1c dm]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1c cl]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1a mw]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/3a yw]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/7b yw]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/3b sk]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/4b rs]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/6b am]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/8a cj]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/10a dr]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/10a jp]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/9a ms]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/10b sr]]&lt;br /&gt;
*Chapter 11a [[ECE506_CSC/ECE_506_Spring_2012/11a_az | CSC/ECE 506 Spring 2012/11a az - Performance of DSM system]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/12b jh]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2010/8a fu]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2010/8a sk]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/11a ht]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1b dj]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1a sp]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1d ks]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/2b so]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1c ad]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/3b xz]]&lt;br /&gt;
*[[CSC/ECE_506_Spring_2013/4a_aj]]&lt;br /&gt;
*[[CSC/ECE_506_Spring_2013/4a_ss]]&lt;br /&gt;
*[[CSC/ECE_506_Spring_2013/1a_ag]]&lt;br /&gt;
* Chapter 3a [[CSC/ECE_506_Spring_2013/3a_bs]]&lt;br /&gt;
* Chapter 6a [[CSC/ECE_506_Spring_2013/6a_cs]]&lt;br /&gt;
* Chapter 5a [[CSC/ECE_506_Spring_2013/5a_ks]]&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=ECE506_Main_Page&amp;diff=73767</id>
		<title>ECE506 Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=ECE506_Main_Page&amp;diff=73767"/>
		<updated>2013-02-27T14:46:18Z</updated>

		<summary type="html">&lt;p&gt;Ksharida: /* Supplements to Solihin Text */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page serves as a portal for all wiki material related to CSC506 and ECE506. Link to any new wiki pages from this page, and add links to any current pages.&lt;br /&gt;
&lt;br /&gt;
=Supplements to Solihin Text=&lt;br /&gt;
&lt;br /&gt;
Post links to the textbook supplements in this section.&lt;br /&gt;
*Chapter 2 [[CSC/ECE 506 Spring 2011/ch2 dm | CSC/ECE 506 Spring 2011/ch2 dm]]&lt;br /&gt;
*Chapter 2 [[Parallel_Programming_Models | Parallel Programming Models]]&lt;br /&gt;
*Chapter 2 (Still being revised) [[CSC/ECE 506 Spring 2011/ch2 cl | CSC/ECE 506 Spring 2011/ch2 cl]]&lt;br /&gt;
*Chapter 2a [[ CSC/ECE 506 Spring 2011/ch2a mc | Current Data-Parallel Architectures ]]&lt;br /&gt;
*Chapter 2a [[ CSC/ECE 506 Spring_2012/2a va ]]&lt;br /&gt;
*Chapter 2b [[CSC/ECE 506 Spring 2012/ch2b cm | CSC/ECE 506 Spring 2012/ch2b cm]]&lt;br /&gt;
*Chapter 2b [[ECE506_CSC/ECE_506_Spring_2012/2b_az | CSC/ECE 506 Spring 2012/2b az - Data-Parallel Processing with the AMD HD 6900 Series Graphics Processing Unit]]&lt;br /&gt;
*Chapter 3 (Final Revision) [[ CSC/ECE 506 Spring 2011/ch3 ab | Parallel Architecture Mechanisms and Programming Models ]]&lt;br /&gt;
*Chapter 4a[[ CSC/ECE 506 Spring 2011/ch4a ob | Parallelization of Nelder Mead Algorithm ]]&lt;br /&gt;
*Chapter 4a (Under Construction) [[ CSC/ECE_506_Spring_2011/ch4a_bm | Parallelization of Algorithms  ]]&lt;br /&gt;
*Chapter 4a [[ CSC/ECE 506 Spring 2011/ch4a zz | CSC/ECE 506 Spring 2011/ch4a zz ]]&lt;br /&gt;
*Chapter 4b [[Chapter 4b CSC/ECE 506 Spring 2011 / ch4b]]&lt;br /&gt;
*Chapter 5a [[ CSC/ECE 506 Spring 2012/ch5a ja | CSC/ECE 506 Spring 2012/ch5a ja ]]&lt;br /&gt;
*Chapter 9a [[CSC/ECE 506 Spring 2012/ch9a cm | CSC/ECE 506 Spring 2012/ch9a cm]]&lt;br /&gt;
*Chapter 6a (Under Construction) [[ CSC/ECE 506 Spring 2011/ch6a jp | CSC/ECE 506 Spring 2011/ch6a jp ]]&lt;br /&gt;
*Chapter 6a (Under Construction) [[ CSC/ECE 506 Spring 2011/ch6a ep | CSC/ECE 506 Spring 2011/ch6a ep ]]&lt;br /&gt;
*Chapter 6b (Ready for First Review) [[CSC/ECE 506 Spring 2011/ch6b ab | CSC/ECE 506 Spring 2011/ch6b ab]]&lt;br /&gt;
*Chapter 7 (Under Construction) [[CSC/ECE 506 Spring 2011/ch7 jp | CSC/ECE 506 Spring 2011/ch7 jp]]&lt;br /&gt;
*Chapter 8 [[CSC/ECE 506 Spring 2011/ch8 mc | CSC/ECE 506 Spring 2011/ch8 mc]]&lt;br /&gt;
*Chapter 10 (Under Construction) [[CSC/ECE 506 Spring 2011/ch10 sb | CSC/ECE 506 Spring 2011/ch10 sb]]&lt;br /&gt;
*Chapter 10 [[CSC/ECE 506 Spring 2012/ch10 sj | CSC/ECE 506 Spring 2012/ch10 sj]]&lt;br /&gt;
*Chapter 10a [[CSC/ECE_506_Spring_2011/ch10a_dc | CSC/ECE_506_Spring_2011/ch10a_dc]]&lt;br /&gt;
*Chapter 11 [[CSC/ECE_506_Spring_2011/ch11_BB_EP | Chapter 11 Supplement]]&lt;br /&gt;
*Chapter 11 [[Scalable_Coherent_Interface | SCI (Scalable Coherent Interface) ]]&lt;br /&gt;
*Chapter 12 [[ CSC/ECE 506 Spring 2011/ch12 ob | Interconnection Network Topologies and Routing Algorithms]]&lt;br /&gt;
*Chapter 12 (Ready for Final Review) [[ CSC/ECE 506 Spring 2011/ch12 aj | Interconnection Network Topologies and Routing Algorithms]]&lt;br /&gt;
*Chapter 12 [[ CSC/ECE 506 Spring 2011/ch12 | Interconnection Network Topologies]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1a ry]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1c dm]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1c cl]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/1a mw]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/3a yw]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/7b yw]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/3b sk]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/4b rs]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/6b am]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/8a cj]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/10a dr]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/10a jp]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/9a ms]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/10b sr]]&lt;br /&gt;
*Chapter 11a [[ECE506_CSC/ECE_506_Spring_2012/11a_az | CSC/ECE 506 Spring 2012/11a az - Performance of DSM system]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/12b jh]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2010/8a fu]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2010/8a sk]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2012/11a ht]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1b dj]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1a sp]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1d ks]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/2b so]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/1c ad]]&lt;br /&gt;
*[[CSC/ECE 506 Spring 2013/3b xz]]&lt;br /&gt;
*[[CSC/ECE_506_Spring_2013/4a_aj]]&lt;br /&gt;
*[[CSC/ECE_506_Spring_2013/4a_ss]]&lt;br /&gt;
*[[CSC/ECE_506_Spring_2013/1a_ag]]&lt;br /&gt;
* Chapter 3a [[CSC/ECE_506_Spring_2013/3a_bs]]&lt;br /&gt;
* Chapter 6a [[CSC/ECE_506_Spring_2013/6a_cs]]&lt;br /&gt;
* Chapter 5a [[CSC/ECE_506_Spring_2013/6a_ks]]&lt;/div&gt;</summary>
		<author><name>Ksharida</name></author>
	</entry>
</feed>