10 · Lefohn, Kniss, Strzodka, Sengupta, <strong>and</strong> Owenswhere ++it advances the iterator to the next element, *it retrieves the data value, begin obtains an iterator to thefirst element, <strong>and</strong> end returns an iterator one increment past the end of the array.Glift programmers may also traverse sub-regions of structures by specifying the origin <strong>and</strong> size of a range. Forexample, to traverse the elements between virtual addresses (0,0,0,0) <strong>and</strong> (4,4,4,4), the programmer writes:✞ArrayType::range r = array.range( vec4i(0,0,0,0), vec4i(5,5,5,5) );ArrayType::iterator it;<strong>for</strong> (it = r.begin(); it != r.end(); ++it) {*it = -(*it);}✝Given that the goal of Glift is primarily parallel execution, we need to replace the explicit <strong>for</strong> loop with an encapsulatedtraversal function that can be parallelized. For example, the STL’s trans<strong>for</strong>m construct can express theprevious example as:✞ArrayType::range r = array.range( vec4i(0,0,0,0), vec4i(5,5,5,5) );std::trans<strong>for</strong>m( r.begin(), r.end(), r.begin(), std::negate() );✝where the first two arguments specify the input iterators, the third specifies the corresponding output iterator, <strong>and</strong>the fourth argument is the computation to per<strong>for</strong>m on each element. In stream programming terminology, this fourthargument is called the kernel.While this example is executed serially on the CPU, the trans<strong>for</strong>m operation’s order-independent semantic makesit trivially parallelizeable. Glift leverages this insight to express GPU iteration as parallel traversal over a range ofelements.3.4.2 Access Permissions. In addition to defining data traversal, Glift iterators also express data access permissions.Access permissions control if the value of an iterator is read-only, write-only, or read-write. For simplicity,the examples so far have used iterators with read-write access permissions. It is especially important to distinguishbetween access permissions on current GPUs, which prohibit kernels from reading <strong>and</strong> writing to the same memorylocation. The previous example can be re-written to obey such rules by using separate input <strong>and</strong> output arrays:✞// ... Declare arrayIn <strong>and</strong> arrayOut as identically sized arrays ...ArrayType::range r = arrayIn.range( vec4i(0,0,0,0), vec4i(5,5,5,5) );std::trans<strong>for</strong>m( r.begin_i(), r.end_i(), arrayOut.begin_o(), std::negate() );✝Note that begin i <strong>and</strong> end i return read-only (input) iterators <strong>and</strong> begin o returns a write-only output iterator.One-sided communication models such as this avoid synchronization problems <strong>and</strong> are also known to be very efficienton CPU-based parallel computers [Kendall et al. 2005].3.4.3 Access Patterns. Glift iterators support three types of data access patterns: single, neighborhood, <strong>and</strong> r<strong>and</strong>om.An iterator’s access pattern provides a mechanism by which programmers can explicitly declare their application’smemory access patterns. In turn, this in<strong>for</strong>mation can enable compilers or runtime systems to hide memoryaccess latency by pre-loading data from memory be<strong>for</strong>e it is needed. As noted in Purcell et al. [2002], future architecturescould use this in<strong>for</strong>mation to optimize cache usage based on the declared memory access pattern. Forexample, the stream model permits only single-element access in kernels, enabling perfect pre-loading of data be<strong>for</strong>eit is needed. At the other extreme, r<strong>and</strong>om memory accesses af<strong>for</strong>d little opportunities to anticipate which memory willbe needed. Single-access iterators do not permit indexing <strong>and</strong> are analogous to Brook’s stream inputs <strong>and</strong> the STL’snon-r<strong>and</strong>om-access iterators. Neighborhood iterators permit relative indexing in a small, constant-sized region aroundthe current element. These iterators are especially useful in image processing <strong>and</strong> grid-based simulation applications.R<strong>and</strong>om-access iterators permit indexing into any portion of the data structure.ACM Transactions on Graphics, Vol. 25, No. 1, January 2006.☎✆☎✆☎✆
Glift: Generic, Efficient, R<strong>and</strong>om-Access GPU <strong>Data</strong> Structures · 113.4.4 GPU Iterators. We now have enough machinery to express a GPU version of the CPU example shown inSection 3.4.1. To begin, the Cg source <strong>for</strong> negateKernel is:✞☎void main( SingleIter it, out float4 result : COLOR ) {result = -(it.value());}.✝✆SingleIter is a Glift Cg type <strong>for</strong> a single-element (stream) iterator <strong>and</strong> it.value() dereferences the iterator toobtain the data value.The C++ source that initiates the execution of the GPU kernel is:✞☎ArrayType::gpu_in_range inR = array1.gpu_in_range( vec4i(0,0,0,0), vec4i(5,5,5,5) );ArrayType::gpu_out_range outR = array2.gpu_out_range( vec4i(0,0,0,0), vec4i(5,5,5,5) );CGparameter arrayParam = cgGetNamedParameter(prog, "it");inR.bind_<strong>for</strong>_read( arrayParam );outR.bind_<strong>for</strong>_write( GL_COLOR_ATTACHMENT0_EXT );// ... Bind negateKernel fragment program ...exec_gpu_iterators(inR, outR);✝✆The gpu in range <strong>and</strong> gpu out range methods create entities that specify GPU computation over a rangeof elements <strong>and</strong> deliver the values to the kernel via Cg iterators. Note that GPU range iterators are first-class Gliftprimitives that are bound to shaders in the same way as Glift data structures.The exec gpu iterators call is a proof-of-concept GPU execution engine that executes negateKernelacross the specified input <strong>and</strong> output iterators but is not a part of core Glift. The goal of Glift is to provide genericGPU data structures <strong>and</strong> iterators, not provide a runtime GPU execution environment. Glift’s iteration mechanism isdesigned to be a powerful back-end to GPU execution environments such as Brook, Scout, or Sh.3.4.5 Address Iterators. In addition to the data element iterators already described, Glift also supports addressiterators. Rather than iterate over the values stored in a data structure, address iterators traverse N-D virtual or physicaladdresses. Address iterators enable users to specify iteration using only an AddrTrans component.A simple CPU example using an address iterator to add one to all elements of the example 4D array is:✞AddrTransType::range r = addrTrans.range( vec4i(0,0,0,0), vec4i(5,5,5,5) );AddrTransType::iterator ait;<strong>for</strong> (ait = rit.begin(); ait != rit.end(); ++ait) {vec4i va = *ait;vec4f val = array.read( va );array.write( va, val + 1 );}✝Note that iteration is now defined with an address translator rather than a virtual or physical container, <strong>and</strong> the valueof the iterator is an index rather than a data value.The Cg code <strong>for</strong> a GPU address iterator example is:✞float4 main( uni<strong>for</strong>m VMem4D array, AddrIter4D it ) : COLOR {float4 va = it.value();return array.vTex4D(va);}✝ACM Transactions on Graphics, Vol. 25, No. 1, January 2006.☎✆☎✆
- Page 6 and 7: 6 · Lefohn, Kniss, Strzodka, Sengu
- Page 8 and 9: 8 · Lefohn, Kniss, Strzodka, Sengu
- Page 12 and 13: 12 · Lefohn, Kniss, Strzodka, Seng
- Page 14 and 15: 14 · Lefohn, Kniss, Strzodka, Seng
- Page 16 and 17: 16 · Lefohn, Kniss, Strzodka, Seng
- Page 18 and 19: 18 · Lefohn, Kniss, Strzodka, Seng
- Page 20 and 21: 20 · Lefohn, Kniss, Strzodka, Seng
- Page 22 and 23: 22 · Lefohn, Kniss, Strzodka, Seng
- Page 24 and 25: 24 · Lefohn, Kniss, Strzodka, Seng
- Page 26 and 27: 26 · Lefohn, Kniss, Strzodka, Seng
- Page 28 and 29: 28 · Lefohn, Kniss, Strzodka, Seng
- Page 30 and 31: 30 · Lefohn, Kniss, Strzodka, Seng
- Page 32 and 33: 32 · Lefohn, Kniss, Strzodka, Seng
- Page 34 and 35: 34 · Lefohn, Kniss, Strzodka, Seng
- Page 36 and 37: 36 · Lefohn, Kniss, Strzodka, Seng