<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>Large-Scale Loops Parallelization for GPU Accelerators</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Anatoliy Doroshenko</string-name>
          <email>doroshenkoanatoliy2@gmail.com</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Oleksii Beketov</string-name>
          <email>beketov@isofts.kiev.ua</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>Institute of Software Systems of National Academy of Sciences of Ukraine</institution>
          ,
          <addr-line>Glushkov prosp. 40, 03187 Kyiv</addr-line>
          ,
          <country country="UA">Ukraine</country>
        </aff>
      </contrib-group>
      <abstract>
        <p>The technique that allows to extend GPU capabilities to deal with data volumes that outfit internal GPU's memory capacity is developed. The technique involves loop tiling and data serialization and could be applied to utilize clusters consisting of several GPUs. Applicability criterion is specified. Transforming scheme designed and semiautomatic proof-of-concept software tool are implemented. Conducted an experiment to demonstrate the feasibility of the proposed approach.</p>
      </abstract>
      <kwd-group>
        <kwd />
        <kwd>Parallelization methods</kwd>
        <kwd>loop optimization</kwd>
        <kwd>GPGPU</kwd>
        <kwd>CUDA</kwd>
      </kwd-group>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>Introduction</title>
      <p>Loop parallelization is a long-standing problem of computational programming. Loops
give a fair parallelization opportunity for numerous scientific modeling problems that
involve numerical methods. Along with spreading of GPGPU technology [1] that
allows employment of graphics accelerators for solving computational tasks new
challenges arises. As far as GPU is not a standalone device and is managed by a host
operating unit, it should be considered within the context of heterogeneous computational
platforms. Composing the programs for such the platforms demands knowledge in
architecture and specific programming tools. Generally, the concurrent software
development passes through the stage of successive implementation that becomes a starting
point for further platform-dependent and hardware environment specific
implementations.</p>
      <p>
        The existing automatic code parallelizing tools [2, 3, 4] don't account the limited
amount of GPU's on-board memory space while real-life problems demand in huge
amounts of data to be processed. To embrace those cases of massive computational
tasks that involve large amounts of data we propose a technique that provides an ability
to rip the loop and to split the data and calculation operations.
(
        <xref ref-type="bibr" rid="ref1">1</xref>
        )
(
        <xref ref-type="bibr" rid="ref2">2</xref>
        )
(
        <xref ref-type="bibr" rid="ref3">3</xref>
        )
  denotes quotient,  is a Kronecker delta and Sn – the desired number of sections
for each of the loops to be subdivided, 1  Sn  In . This transformation is commonly
known as loop tiling and is performed in optimizing compilers to modify memory
access patterns for improving cache access [5]. After substitution and reordering the new
loop takes the form
for 0  sN  SN :
...
      </p>
      <p>for 0  s0  S0 :
for sN  L(I N , SN )  iN  minsN 1 L(I N , SN ), SN :
...</p>
      <p>for s0  L(I0, S0)  i0  mins0 1 L(I0, S0), S0 :</p>
      <p></p>
      <p>
        F i , D.
where Ik   , 0  k  N , for 0  i  I : S(i) is a notion for a sequence of
S(0), S(
        <xref ref-type="bibr" rid="ref1">1</xref>
        ),..., S(I ), F : I0 ... I N  D  D is a mapping over the dataset D. We'll

call ik a k-th counter of the cycle (
        <xref ref-type="bibr" rid="ref1">1</xref>
        ), i  i0,i1,..., iN  I0 ... I N a vector of counters

and a particular call of F i,  for some specified value of i an iteration. First, let's
make the following substitution for each of the for statements:
where
for 0  in  In

for 0  sn  Sn :
      </p>
      <p>for sn  L(In, Sn )  in  min sn  1 L(In, Sn ), Sn  ,
L(a, b)   a  1 0, a mod b ,</p>
      <p> b 
The inner loop is similar to the initial loop but of a diminished scale. Leaving the inner
N+1 loops, let's group the first N+1:</p>
      <p>N
for 0  e   Si :</p>
      <p>i0

i  g(e);

Here g() is a mapping that restores the vector of counters i and is constructed the
following way:
g0(e)  e modS0,</p>
      <p>
gk (e)   e 



gN (e)  e

</p>
      <p>N
0  e   Sk .</p>
      <p>k 0</p>
      <p>N 1 
 S j  ,
j0 </p>
      <p>N

jk 1</p>
      <p>j1  k 1 
g j (e)  Sl   S j  , 0  k  N ,</p>
      <p>
        l0  j0 
Loop (
        <xref ref-type="bibr" rid="ref3">3</xref>
        ) maintains the sequence of vector of counters equal to the sequence produced
by the initial loop (
        <xref ref-type="bibr" rid="ref1">1</xref>
        ).
      </p>
      <p>
        Let's denote the inner N+1 loops of the cycle (
        <xref ref-type="bibr" rid="ref2">2</xref>
        ) along with g(e) as a kernel(e) .
We intend to delegate the kernel execution to GPU and to run it concurrently thus
diminishing the depth of the inner loop nest. As the GPU's memory space is isolated from
the host's device one, we introduce serialize operation that is to prepare the input data
required to perform calculations for the step e and deserialize operation to store the
output data processed by GPU. The further implementation of these procedures is out
of our scope and depends on the particular problem. Finally, we got:
      </p>
      <p>N
for 0  e   Si :</p>
      <p>
        i0
serialize(e, inputData, dataPull);
transfer2device(inputData);
kernel(e, inputData, outputData);
transfer2host(outputData);
deserialize(e, outputData, dataPull);
(
        <xref ref-type="bibr" rid="ref4">4</xref>
        )
Iterations of the loop (
        <xref ref-type="bibr" rid="ref4">4</xref>
        ) could be distributed over concurrently running threads through
involving several additional data exchange buffers. This approach could be applied to
any distributed memory computational system, e.g. GPU cluster or heterogeneous
cluster of any other computation empowered devices. To preserve equivalence in a sense
of output results equality for the same given input data Bernstein's [6] conditions must
be met. This roughly means that iterations should not overwrite the other's iterations
input data and should store their output data apart. The set of Sk , 0  k  N are
transformation's tunable parameters that are chosen in a way to satisfy Bernstein's conditions
and to optimize processing time that is to find a trade-off on time spent on data
preparation, transfer and kernel execution. These timings depend on the input and output data
load size which is restricted by the total available amount of GPU's memory and the
hardware configuration parameters such are input and output memory transfer rate and
GPU compute capabilities.
      </p>
    </sec>
    <sec id="sec-2">
      <title>Program Execution Flow</title>
      <p>Let's consider the node that consists of one multicore CPU and one GPU. Modern GPUs
support direct memory access technology thus allowing to proceed data transfer and
kernel execution asynchronously. To optimize data exchange process dual buffering is
involved. Four buffers at both host and device sides are involved – two for the input
and two for the output data exchange. On the host side, calculations proceed in two
threads that execute kernel, serialize and deserialize procedures simultaneously. One of
them serialize input data and fills the input data buffer, then transmits the buffer to the
GPU and launches the kernel, and the second receives output data buffer from GPU and
deserialize it. Besides the calculations, GPU carries bidirectional data
transfers through the asynchronous data transfer mechanism. Calculations are
performed in three stages – initial, cyclic and finalizing.</p>
      <p>At the starting point, data buffers are empty, and GPU awaits the data transfer. It
doesn't matter what of the threads will carry the initial step as all of the operations are
run successively and asynchronous transfer mode is not involved. At the initial step,
CPU serializes input data buffers of the first two iterations and transfers the buffer
containing the first iteration data to the accelerator.</p>
      <p>After the initial step, the cyclic stage starts. The execution flow is shown at the
diagram at Fig.1. On the diagram, the iteration's number of which the data is stored in the
buffer is given after the buffer's name. One step of the cyclic lap divides into odd and
even parts. Both odd and even parts of the first step skip deserialization as the host
output buffers are empty yet. At the odd part of the first step, the accelerator-to-host
transfer is omitted too. Meanwhile, an accelerator performs calculations over the
current buffer, host threads fetch data buffer from the previous step, deserialize
penultimate step buffer, send input data buffer for the next step and serialize buffer for the
after the next step. In one step two kernel launches are executed. After each of the parts,
odd or even, is finished the processes synchronize. Two final steps depend on the actual
kernel launches number. If the number of kernel launches is odd, the final step of the
cyclic part excludes an even part and does not involve serialization and
host-to-accelerator transfer, and the even part of the penultimate step skips serialization. Otherwise,
if the number of launches is even the last cyclic loop step is full, but the even part of
the final step omits serialization.</p>
      <p>The finalizing step deserializes output data buffer transferred at the last cyclic loop
step and then fetches and deserializes the final output data buffer consequently finishing
the calculations.
4</p>
    </sec>
    <sec id="sec-3">
      <title>Application of the Proposed Approach for Constructing a</title>
    </sec>
    <sec id="sec-4">
      <title>CUDA Program</title>
      <p>In this section, we illustrate the application of the proposed approach to matrix
multiplication and N-body problems. The time measurements were collected on the hardware
system composed of Intel Core i5-3570 CPU (4 cores 3.8Hz) with 16Gb of host
memory and NVIDIA Tesla M2050 GPU (3Gb global memory, 384 bits memory
bandwidth, connected through PCIe2.0 x8) running Ubuntu 16.04 host OS.</p>
      <p>
        A semi-automatic source-to-source code transformation tool based on the TermWare
rewriting system [7] aiming to assist in constructing a new concurrent program was
implemented. It takes the initial loop marked with pragmas, applies the transformations
(
        <xref ref-type="bibr" rid="ref3">3</xref>
        ) and provides with a template of the code of a new loop to be substituted. The
remaining actions include serialization and deserialization routines implementation; the
kernel could be implemented as well as generated by another tool and adapted in place.
      </p>
      <p>The algorithm of the initial sequential matrix multiplication program involved
threedimensional loop nest. It was transformed using the proposed technique and
C-toCUDA compilers PPCG and Par4All. Both of the programs generated by PPCG and
P4A showed comparable results. After applying the slicing technique the initial
matrices were split into submatrices. The internal loop subdivision parameter S0 was set to
1, the roles of parameters S2 and S1 is adjusting submatrices width. The schema with
double data exchange buffering and two CPU threads was used. Even not involving
GPU, adjusting the slicing number allowed to reach about 12 times acceleration over
the initial loop due to CPU caching. For the GPU implementation, the parameterized
PPCG generated kernel was used; the source codes of the constructed matrix
multiplication program are available at GitHub [8]. The chart at Fig. 3 shows the constructed
program's timings and the timings of the program obtained with PPCG relatively to the
matrix dataset size. The relative acceleration of about 430 times in comparison to the
sequential program executed on the CPU for the datasets of square matrices of the size
of 5000x5000 single precision floating point numbers was reached. It could be seen
from the chart that the PPCG generated program has achieved maximum data set size
less than 300 Mb that is 10% of GPU's global memory available. This is due to the fact
that PPCG is limited to static memory usage, thus blocking to link programs with too
large static arrays. It is worth to mention that involving two CPU threads is excessive
as the part of serialization and deserialization is negligible compared to GPU kernel
computation time, that could be seen from the GPU execution profile given at Fig.2.
Thus involving just one thread instead won't decrease performance substantially,
however, two concurrent threads are required to avoid gaps in kernel launches and to gain
maximum performance from the GPU.
Another application investigated was a predictor routine from N-body problem with
predictor-corrector time-iterative [9] algorithm. The model of the system consists of a
set of particles that interact pairwise PPCG applying caused a slowdown effect and led
in about 500 times decrease in performance in comparison to the sequential CPU
implementation. As for the constructed program with a self-implemented kernel not
involving shared memory usage, the relative CPU to GPU acceleration at the selected
data size range reached 13 times. The plot at Fig.4 shows the dependency of successive
CPU and transformed GPU programs execution time on the data size that is scaled by
the alteration the number of particles N. The timings are measured for the one time-step
of the prediction routine. The memory limit was not reached as it would take
approximately 30 years to process one time-step of the algorithm for a fully loaded GPU that
was used in the experiment, however, the applicability of the approach is confirmed.</p>
      <p>Thus the difference in the constructed multiplication and N-body programs consists
in the kernel, serializer and deserializer implementation while the control flow structure
remains identical.</p>
      <p>Fig. 3. The dependency of the execution time on the size of the input data for the concurrent
PPCG-generated and constructed matrix multiplication programs.</p>
    </sec>
    <sec id="sec-5">
      <title>Conclusion</title>
      <p>This paper proposes an approach for semi-automated parallelization of nested loops for
graphics processors. The approach is illustrated by the development of CUDA
programs for solving matrix multiplication and N-body problems. As a result, a common
unified scheme was used to parallelize both multiplication and N-body problems. An
assistant semi-automatic code transformation tool was implemented. Further work
relates to developing unified methods and tools in loop parallelization.</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          1.
          <string-name>
            <surname>Harris</surname>
            ,
            <given-names>M. J.</given-names>
          </string-name>
          :
          <string-name>
            <surname>Real-Time Cloud</surname>
          </string-name>
          Simulation and Rendering.
          <source>University of North Carolina Technical Report #TR03-040</source>
          (
          <year>2003</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          2.
          <string-name>
            <given-names>HPC</given-names>
            <surname>Project</surname>
          </string-name>
          , “
          <article-title>Par4all automatic parallelization”</article-title>
          , http://www.par4all.org
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          3.
          <string-name>
            <surname>Irigoin</surname>
            ,
            <given-names>F.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Jouvelot</surname>
            ,
            <given-names>P.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Triolet</surname>
          </string-name>
          , R.:
          <article-title>Semantical interprocedural parallelization: An overview of the pips project</article-title>
          .
          <source>In ACM Int. Conf. on Supercomputing (ICS'2)</source>
          , Cologne, Germany (
          <year>June 1991</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          4.
          <string-name>
            <surname>Verdoolaege</surname>
            ,
            <given-names>S.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Juega</surname>
            ,
            <given-names>J. C.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Cohen</surname>
            ,
            <given-names>A.</given-names>
          </string-name>
          , G´omez,
          <string-name>
            <given-names>J. I.</given-names>
            ,
            <surname>Tenllado</surname>
          </string-name>
          ,
          <string-name>
            <given-names>C.</given-names>
            ,
            <surname>Catthoor</surname>
          </string-name>
          ,
          <string-name>
            <surname>F.</surname>
          </string-name>
          :
          <article-title>Polyhedral parallel code generation for CUDA</article-title>
          .
          <source>ACM Trans. Architec. Code Optim. 9</source>
          ,
          <issue>4</issue>
          ,
          <string-name>
            <surname>Article 54</surname>
          </string-name>
          (
          <year>January 2013</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          5.
          <string-name>
            <surname>Wolfe</surname>
            ,
            <given-names>M.</given-names>
          </string-name>
          :
          <article-title>More Iteration Space Tiling</article-title>
          .
          <source>In: Supercomputing '89:Proceedings of the 1989 ACM/IEEE Conference on Supercomputing</source>
          , pp.
          <fpage>655</fpage>
          -
          <lpage>664</lpage>
          . Reno,
          <string-name>
            <surname>NV</surname>
          </string-name>
          , USA (
          <year>1989</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          6.
          <string-name>
            <surname>Bernstein</surname>
            ,
            <given-names>A. J.:</given-names>
          </string-name>
          <article-title>Analysis of Programs for Parallel Processing</article-title>
          .
          <source>IEEE transactions on electronic computers</source>
          , vol. EC-
          <volume>15</volume>
          , No.
          <volume>5</volume>
          (
          <issue>October</issue>
          ,
          <year>1966</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          7.
          <string-name>
            <surname>Doroshenko</surname>
            ,
            <given-names>A.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Shevchenko</surname>
          </string-name>
          , R.:
          <article-title>TermWare: A Rewriting Framework for Rule-Based Programming Dynamic Applications</article-title>
          .
          <source>Fundamenta Informaticae</source>
          <volume>72</volume>
          (
          <issue>1-3</issue>
          ) (
          <year>2005</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          8.
          <string-name>
            <given-names>GitHub</given-names>
            <surname>Repository</surname>
          </string-name>
          , https://github.com/o-beketov/matmul
        </mixed-citation>
      </ref>
      <ref id="ref9">
        <mixed-citation>
          9.
          <string-name>
            <surname>Aarseth</surname>
            ,
            <given-names>S. J.</given-names>
          </string-name>
          :
          <article-title>Gravitational N-body simulations</article-title>
          . Cambridge University Press (
          <year>2003</year>
          ).
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>