<!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>An Approach to Parallelizing Fortran Programs using Rewriting Rules Technique</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>Kostiantyn Zhereb</string-name>
          <email>zhereb@gmail.com</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="editor">
          <string-name>Key Terms: High Performance Computing, Model, Methodology.</string-name>
        </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>
      <fpage>112</fpage>
      <lpage>120</lpage>
      <abstract>
        <p>We present an ongoing research in the area of transforming existing sequential Fortran programs into their parallel equivalents. Our approach is to use rewriting rules technique in order to automate the transformation process. Sequential source code is transformed into parallel code for shared-memory systems, such as multicore processors. Parallelizing and optimizing transformations are formally described as rewriting rules which facilitates their reuse. Using high-level algebraic models allows describing program transformations in a more concise manner. Performance measurements demonstrate high efficiency of obtained parallel programs.</p>
      </abstract>
      <kwd-group>
        <kwd>rewriting rules technique</kwd>
        <kwd>algebraic program models</kwd>
        <kwd>multicore processors</kwd>
        <kwd>Fortran</kwd>
        <kwd>OpenMP</kwd>
      </kwd-group>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>-</title>
      <p>
        Despite being one of the first programming languages, Fortran is still widely used, in
particular for solving scientific and engineering computation-intensive problems. Its
popularity is due to its relative simplicity and lack of complex facilities (e.g.
pointers), closeness to mathematical description of problem and efficiency of
generated binary code. Another reason for continued use of Fortran is that in more
than 50 years of its existence a vast repository of programs, libraries and routines for
solving different scientific problems has been created. Algorithms implemented in
such programs are still valuable, however there is a need to adapt this legacy code to
new parallel computational platforms. Furthermore, due to size and complexity of
existing code, manual adaptation is not a practical option: there is a need for
automated tools to facilitate conversion of legacy code to modern parallel platforms
[
        <xref ref-type="bibr" rid="ref5">5</xref>
        ].
      </p>
      <p>In this paper we describe an ongoing research on parallelizing Fortran programs
using rewriting rules technique. Sequential source code is transformed into parallel
code for shared-memory parallel platform (such as multicore processors) using
automated transformations. Parallelizing and optimizing transformations are formally
described as rewriting rules which facilitates their reuse. Such approach is aimed at
two main goals: to improve runtime efficiency of programs and to increase
developer’s productivity. We illustrate our approach on two sample programs: a
simple Gauss elimination algorithm and an applied problem of calculating electron
density from the field of quantum chemistry.</p>
      <p>
        There has been an extensive research in the area of parallelizing existing sequential
code, in particular for multicore architectures. Some approaches require manual code
modification and provide facilities that help a developer express parallelism. Such
approaches include parallel libraries [13], parallel extensions to existing languages
[14] and new parallel languages [16]. Another research direction is interactive
parallelization [11], when a developer manually selects the loops to be parallelized,
and the tool applies transformation automatically (our approach also belongs to this
category). Finally there are numerous approaches to automated parallelization, mostly
implemented as parallelizing compilers [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ]. Such systems use the static analysis of the
source code to detect possible areas of parallelism and generate parallel binary code.
Some papers also use the dynamic analysis to detect parallelism based on concrete
input data [15], or machine learning approaches to select most appropriate
transformations [17], or auto-tuning to discover optimal parameters of
transformations [
        <xref ref-type="bibr" rid="ref6">6</xref>
        ]. The key differences of our approach is the use of the
source-tosource transformations, allowing the developers to examine transformed program
code, and the description of the transformations in terms of the formal models and
rewriting rules, making easier for developers to add new parallelizing transformations
or to modify existing ones.
      </p>
      <p>
        This paper continues our research on automation of process of designing and
development of efficient parallel programs, started in [
        <xref ref-type="bibr" rid="ref3">3</xref>
        ], [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ], [9]. Our previous
papers [
        <xref ref-type="bibr" rid="ref3">3</xref>
        ], [9] applied a similar approach to the development of parallel programs
written in C# language for Microsoft .NET framework, while this paper concentrates
on parallelizing Fortran programs. We have already described our first experiences
with Fortran programs in [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ]. However, as we moved from simple examples to
realworld legacy code, we were forced to revise our approach, as described in this paper
(see section 2). Also this paper places more significance on choosing place of
application of existing program transformation, rather than developing new
transformations.
      </p>
      <p>Below we describe our approach in more detail, provide examples of parallelizing
transformations and illustrate them with parallelization and evaluation of two
programs: small example program (Gauss elimination) and applied quantum
chemistry problem (electron density).
2</p>
    </sec>
    <sec id="sec-2">
      <title>Our Approach: Algebraic Models and Rewriting Rules</title>
      <p>
        As in our previous works [
        <xref ref-type="bibr" rid="ref3">3</xref>
        ], [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ], [9], we use formal facilities, namely rewriting rules
technique and high-level algebraic models of programs, to automate parallelizing
existing sequential code. Legacy source code of sequential program written in Fortran
is transformed into parallel version targeting shared-memory parallel platform
(multicore processors). As a part of transformation process, we create high-level
algebraic models of legacy source code based on Glushkov algebra [
        <xref ref-type="bibr" rid="ref2">2</xref>
        ]. As described
in [
        <xref ref-type="bibr" rid="ref3">3</xref>
        ], the models are created in two steps. First we use target language parser
(Fortran in this paper) to build low-level syntax model, and then rewriting rules of
special form (patterns) to extract language-independent algebraic operators from
language constructs. Using high-level algebraic models allows describing program
transformations in more concise manner. The additional benefit of such models when
applied to legacy code is that they aid in understanding of algorithms by hiding the
(frequently obsolete) implementation details. To this end, using multiple levels of
algebraic models can be useful – e.g. the highest level describes just general structure
of algorithm, while lower levels supply implementation details (the example of such
models is described in section 3).
      </p>
      <p>After high-level program model is created, we use parallelizing transformations to
implement a parallel version of the program on a given platform. Transformations are
represented as rewriting rules and therefore can be applied in automated manner.
(Selection of loops that could be transformed is performed manually.) The declarative
nature of rewriting rules technique simplifies adding new transformations. Also
transformations work with high-level model elements (on any level of abstraction),
which means they are language-independent.</p>
      <p>
        Usage of high-level algebraic models also allows proving correctness of the
developed transformations [
        <xref ref-type="bibr" rid="ref3">3</xref>
        ]. Based on program models, we have developed the
algebra-dynamic models of program execution for multicore architecture using
discrete dynamic (transitional) systems [
        <xref ref-type="bibr" rid="ref2">2</xref>
        ]. For these models, we have (manually)
proved that each of proposed code transformations is correct under certain conditions,
i.e. that initial and transformed programs are equivalent.
      </p>
      <p>
        To automate program transformations we use the rewriting rules system Termware
[
        <xref ref-type="bibr" rid="ref7">7</xref>
        ]. Termware is used to describe transformations of terms, i.e. expressions of form
f t1,, tn  . Transformations are described as Termware rules, i.e. expressions of
form source [condition]-&gt; destination [action].
      </p>
      <p>Here source is a source term (a pattern for match), condition is a condition
of rule application, destination is a transformed term, action is additional
action that is performed when rule fires. Each of 4 components of a rule can contain
variables (denoted as $var), so that rules are more generally applicable. Components
condition and action are optional. They can execute any procedural code, in
particular use the additional data on the program.</p>
      <p>Termware supports a number of evaluation strategies, including TopDown (used in
this paper), BottomUp and a possibility to implement additional strategies. Termware
system itself doesn’t check that transformation process terminates, however the rules
used in this paper are designed in such way that each model element is processed at
most once, therefore the transformation process is guaranteed to terminate.</p>
      <p>
        In addition to rewriting system, our tools include parsers and generators for target
languages that perform transformation between source code and low-level (syntax)
program model, which is represented as Termware term. We have previously
developed such tools for C# language [
        <xref ref-type="bibr" rid="ref3">3</xref>
        ], [9]; in this paper we have developed a
Fortran parser and generator based on GCC Fortran Compiler.
      </p>
    </sec>
    <sec id="sec-3">
      <title>Parallelization for Shared-memory Systems Using OpenMP</title>
      <p>In this section we describe the process of parallelizing sequential Fortran programs
for parallel systems with shared memory, such as multicore processors. We parallelize
source code of Fortran programs by replacing suitable loops with parallel loop
constructs. To create multithreaded Fortran program we use OpenMP framework
[14]. OpenMP PARALLEL DO directives are used to parallelize loops. For simple
loops, just addition of such directive can produce quite efficient parallel code. In this
case there is additional advantage of keeping transformed parallel code similar to
existing sequential code. In more complex cases (when there is data dependency
between iterations) there is a need of more significant transformations, such as using
OpenMP library subroutines for advanced thread management. In such cases, the
transformed source code contains significant changes. However, usage of high-level
algebraic models allows describing these changes in concise and understandable form.</p>
      <p>We will describe the details of our approach using as an example a Fortran
program implementing Gauss elimination algorithm for solving systems of linear
algebraic equations. The Fortran source code was transformed into a low-level syntax
model using developed parser, then into a high-level algebraic model using Termware
patterns. When working with legacy code, we found it useful to apply several levels
of patterns. First we used generic linear algebra patterns, such as vector and matrix
operations. The obtained algebraic model was language-independent, but still quite
detailed. Then we applied patterns specific to the problem in question. In this way we
obtained schematic representation of algorithm useful for its understanding and
deciding where parallelizing transformations should be applied.</p>
      <p>The high-level model of relevant fragment of program has the following form:
DoCnt(K,1,N-1,</p>
      <sec id="sec-3-1">
        <title>FindMaxElement, CheckDetZero, SwapMaxRowColumn,</title>
      </sec>
      <sec id="sec-3-2">
        <title>CalculateRow(K), UpdateElements )</title>
        <p>We will parallelize only two of the operators present in program, namely
FindMaxElement and UpdateElements. Other operators have less computational
complexity, therefore their parallelization is less effective.</p>
        <p>Out of two operators, the simplest is UpdateElements, responsible for calculating
new values for elements of submatrix:</p>
        <p>UpdateElements = DoCnt(I,K+1,N, Assign(S,A(I,K)),</p>
      </sec>
      <sec id="sec-3-3">
        <title>DoCnt(J,K,N+1, Update(A(I,J),S)))</title>
        <p>Here, DoCnt denotes common DO loop with counter. The iterations of the outer
loop are independent, so this fragment is easily parallelized. We use the following
rewriting rule:</p>
      </sec>
      <sec id="sec-3-4">
        <title>DoCnt($var,$start,$end,$body,_MARK_Parallel)-&gt;</title>
      </sec>
      <sec id="sec-3-5">
        <title>ParallelDoCnt($var,$start,$end,$body)</title>
        <p>The loop to be transformed is marked with _MARK_Parallel symbol to enable rule
application. ParallelDoCnt operator is high-level model element responsible for
parallel loop. In particular, for OpenMP platform it is transformed into
OmpParallelDo operator that describes OpenMP directive represented in Fortran as a
pair of special comments: !$OMP PARALLEL DO … !$OMP END PARALLEL DO.</p>
        <p>Notice that for C language the same operator is represented as a single pragma
statement: #pragma omp parallel for. Therefore, using multiple levels of patterns
allows us to provide operators that are common for given platforms, use these generic
operators in most rewriting rules and then specialize them only when transforming
program model back into source code.</p>
        <p>While UpdateElements operator can be parallelized by simple application of
OpenMP directive, the other operator FindMaxElement is more complex. It also has
the form of loop, but iterations of the loop update the same set of variables (value of
the maximum element in submatrix and its indices). This is the case of reduction,
when some local values are calculated on each iteration and then merged into one
global value. OpenMP supports such cases with REDUCTION clause, however only a
set of predefined reduction operators are supported: while finding just maximum
value can be accomplished using OpenMP directives, finding maximum value and
indices where it occurs is not directly supported.</p>
        <p>Therefore we need to provide transformations that parallelize the loop in general
case of reduction. We represent FindMaxElement as following combination:
FindMaxElement=FindMaxElLoc1*…*FindMaxElLocTN*FindMaxEl</p>
      </sec>
      <sec id="sec-3-6">
        <title>Reduct</title>
        <p>On each thread we execute local version of operator (FindMaxElLoc1,…,
FindMaxElementLocTN), and then execute reduction operator
FindMaxElReduct that combines local values into one global value.</p>
        <p>
          Both already described parallelizing transformations are aimed at high-level
structure of algorithm. However, as we observed in [
          <xref ref-type="bibr" rid="ref3">3</xref>
          ], low-level implementation
details, in particular memory access, can have profound impact on overall
performance.
        </p>
        <p>In the Gaussian elimination program we have observed the same effect. We
noticed that for certain sizes of input matrix (N=256*M) there was a sudden increase
of execution time. We attribute this increase to the peculiarities of memory access:
namely, caching adjacent matrix elements. For such matrix size, the adjacent matrix
elements were put into the same cache items, therefore increasing the number of
cache misses and greatly reducing overall performance. To overcome this peculiarity,
we declare the matrix size as N+1 instead of N. The extra elements are not used in
calculations, but they change location of elements and improve efficiency of memory
access. The transformation is implemented with the following rules:</p>
      </sec>
      <sec id="sec-3-7">
        <title>1. [Declaration(N,Integer,$val):$next] -&gt;[Declaration(N,Integer,$val): [Declaration(MN,Integer,$val+MShift($val)): $next]] 2. MShift($val) [$val%32==0]-&gt;1 !-&gt;0</title>
      </sec>
      <sec id="sec-3-8">
        <title>3. Declaration(A,Array(Double,[N,N+1]))</title>
        <p>-&gt; Declaration(A,Array(Double,[MN,MN+1]))</p>
      </sec>
      <sec id="sec-3-9">
        <title>4. Procedure($name,[N:[A:$next]])-&gt;</title>
      </sec>
      <sec id="sec-3-10">
        <title>Procedure($name,[N:[MN:[A:$next]]])</title>
      </sec>
      <sec id="sec-3-11">
        <title>5. [Parameter(N,Integer,In):$next]</title>
        <p>-&gt; [Parameter(N,Integer,In):[Parameter(MN,Integer,In):
$next]]
6. Call($name,[N:[A:$next]])</p>
        <p>-&gt; Call($name,[N:[MN:[A:$next]]])</p>
        <p>The rule 1 adds new parameter, MN, denoting declared matrix size. The rule 2
specifies for which values of matrix size the transformation should be applied. The
rule 3 modifies matrix declaration to use new size MN instead of N. Rules 4-6
propagate new parameter to all procedures, procedure parameters and procedure calls.</p>
        <p>Notice that rules 4-6 are applied multiple times in a single program: for each
procedure definition (rules 4-5) and for each procedure call (rule 6). One of the
advantages of rewriting rules technique is that single rule can describe changes in
multiple places, reducing effort to make the changes and preventing mistakes possible
when applying such changes manually. Notice also that rules 1-6 work on lower level
of abstraction compared with previously described rules. The ability to describe
transformations on different model levels is another advantage of proposed approach
and it allows describing different types of transformations with the same tools.</p>
      </sec>
    </sec>
    <sec id="sec-4">
      <title>4 Performance Evaluation: Test Program and Real-world Example</title>
      <p>To evaluate effects of developed transformations, we have measured the performance
of different versions of initial program of Gauss elimination. We have compared
performance of 4 versions: initial sequential program (SEQ), parallel program with
UpdateElements operator parallelized (PAR1), parallel program with both
UpdateElements and FindMaxElement operators parallelized (PAR2), and program
with both operators parallelized and memory optimization applied (MEM). The
measurements were performed on 4-core parallel system, for matrix sizes from 256 to
2048. Obtained speedup (compared with SEQ program) is shown on fig. 1.</p>
      <p>As can be seen from the diagram, all transformations result in some performance
increase, although their effect differs. For small matrix sizes, both PAR1 and PAR2
show some noticeable speedup, while MEM is not very effective and is very close to
PAR2. However, for larger matrix sizes (N&gt;1024), the situation changes. PAR1 and
PAR2 become less efficient, close to SEQ. However, MEM becomes much more
efficient and demonstrates speedup of more than 10x. Therefore both high-level
transformations of algorithms and taking care of low-level implementation details is
necessary to obtain efficient parallel programs. Measurement results also demonstrate
complex dependency of execution time on real parallel systems, as compared to ideal
theoretical models that suggest simple O(N3) dependency.</p>
      <p>After developing our tools on sample problem (Gauss elimination) we have tried
them on real-world program in area of quantum chemistry. The program calculates
electron and spin density in atoms of polycyclic aromatic hydrocarbons on N*N grid
[12]. The size of the program is 1680 lines of Fortran code. Source code is not well
structured – actual calculations are mixed with I/O operations, debug code and some
hardcoded data. Also it contains mix of features from different versions of language –
from Fortran 77 to Fortran 95. Therefore usage of high-level algebraic models helped
us to understand this legacy code and apply parallelizing transformations in most
efficient way.</p>
      <p>We were able to reuse parallelizing transformations developed for Gauss
elimination program also in electron density program. Only the first, most simple loop
transformation was applied. However, the challenge was to select the most suitable
loop for this transformation, as the program contained 54 loops and trying all of them
was not a feasible option. We have used a profiler tool, Intel VTune Amplifier [10], to
find hotspots in source code. Then we applied rewriting rules technique to detect all
loops enclosing such code fragments. Thus the number of candidate loops was
significantly reduced from 54 to 6. Out of these 6 loop, we applied transformation to
second outermost loop (as the outermost loop contained too few iterations, and
parallelizing inner loops was less efficient because of repeated cost of creating and
synchronizing threads each time inner loop was executed).</p>
      <p>We have compared execution time of initial sequential program (SEQ) and
parallelized program (PAR) for grid dimensions N from 200 to 800 (see fig. 2).</p>
      <p>Applying transformations has resulted in quite significant speedup – from 3.3X to
3.6X (depending on problem size) on 4-core system.</p>
    </sec>
    <sec id="sec-5">
      <title>Conclusion</title>
      <p>In this paper we have described our approach for parallelizing Fortran programs by
applying formalized program transformations to existing sequential Fortran code.
Using rewriting rules technique automates application of transformations and prevents
mistakes that can appear when applying changes to source code manually. High-level
algebraic models simplify understanding of legacy programs and their
transformations, and enable transformation on different levels of abstraction. We have
applied our approach both to simple program implementing Gauss elimination
algorithm and real-world quantum chemistry problem (calculating electron density).
Performance measurements demonstrate significant speedup for both programs.</p>
      <p>Further research directions include development of the same approach for
transforming legacy Fortran applications to target distributed-memory systems and
GPUs. Our future plans also include extension to Grid and cloud-based platforms.
Also we are planning to improve support for large and complex Fortran programs, in
particular automate selection of most suitable place of application for transformations.</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          1.
          <string-name>
            <surname>Allen</surname>
            ,
            <given-names>R.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Kennedy</surname>
            ,
            <given-names>K.</given-names>
          </string-name>
          :
          <article-title>Optimizing Compilers for Modern Architectures: A DependenceBased Approach</article-title>
          . Morgan Kaufmann, San Francisco (
          <year>2001</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          2.
          <string-name>
            <surname>Andon</surname>
            ,
            <given-names>P.I.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Doroshenko</surname>
            ,
            <given-names>A.Yu.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Tseitlin</surname>
            ,
            <given-names>G.O.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Yatsenko</surname>
            ,
            <given-names>O.A.</given-names>
          </string-name>
          :
          <article-title>Algebra-algorithmic models and methods of parallel programming (in Russian)</article-title>
          .
          <source>Academperiodika</source>
          , Kiev (
          <year>2007</year>
          )
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          3.
          <string-name>
            <surname>Andon</surname>
            ,
            <given-names>P.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Doroshenko</surname>
            ,
            <given-names>A.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Zhereb</surname>
            ,
            <given-names>K.</given-names>
          </string-name>
          :
          <article-title>Programming high-performance parallel computations: formal models and graphics processing units</article-title>
          .
          <source>Cybernetics and Systems Analysis</source>
          <volume>47</volume>
          ,
          <issue>4</issue>
          ,
          <fpage>659</fpage>
          -
          <lpage>668</lpage>
          (
          <year>2011</year>
          )
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          4.
          <string-name>
            <surname>Asanovic</surname>
            ,
            <given-names>K.</given-names>
          </string-name>
          et al.:
          <article-title>A view of the parallel computing landscape</article-title>
          .
          <source>Commun. ACM</source>
          <volume>52</volume>
          ,
          <issue>10</issue>
          ,
          <fpage>56</fpage>
          -
          <lpage>67</lpage>
          (
          <year>2009</year>
          )
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          5.
          <string-name>
            <surname>Buttari</surname>
            ,
            <given-names>A.</given-names>
          </string-name>
          , et al.:
          <article-title>The impact of multicore on math software</article-title>
          . In: Kagstrom,
          <string-name>
            <given-names>B.</given-names>
            ,
            <surname>Elmroth</surname>
          </string-name>
          ,
          <string-name>
            <given-names>E.</given-names>
            ,
            <surname>Dongarra</surname>
          </string-name>
          ,
          <string-name>
            <given-names>J.</given-names>
            ,
            <surname>Wasniewski</surname>
          </string-name>
          ,
          <string-name>
            <surname>J. (eds.) PARA</surname>
          </string-name>
          <year>2006</year>
          . LNCS vol.
          <volume>4699</volume>
          , pp.
          <fpage>1</fpage>
          -
          <lpage>10</lpage>
          . Springer, Heidelberg (
          <year>2007</year>
          )
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          6.
          <string-name>
            <surname>Datta</surname>
            ,
            <given-names>K.</given-names>
          </string-name>
          , et al.:
          <article-title>Stencil computation optimization and auto-tuning on state-of-the-art multicore architectures</article-title>
          .
          <source>In: ACM/IEEE Conference on Supercomputing (SC '08)</source>
          , pp.
          <fpage>1</fpage>
          -
          <lpage>12</lpage>
          . IEEE Press, Piscataway (
          <year>2008</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>A Rewriting Framework for Rule-Based Programming Dynamic Applications</article-title>
          .
          <source>Fundamenta Informaticae</source>
          <volume>72</volume>
          ,
          <fpage>1</fpage>
          -
          <lpage>3</lpage>
          ,
          <fpage>95</fpage>
          -
          <lpage>108</lpage>
          (
          <year>2006</year>
          )
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          8.
          <string-name>
            <surname>Doroshenko</surname>
            ,
            <given-names>A.Yu.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Zhereb</surname>
            ,
            <given-names>K.A.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Tyrchak</surname>
            ,
            <given-names>Yu.M.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Khatniuk</surname>
            ,
            <given-names>A.O.</given-names>
          </string-name>
          :
          <article-title>Creating Efficient Parallel Programs in Fortran Using Rewriting Rules Technique (in Russian)</article-title>
          . In: International Conference on High-Performance
          <string-name>
            <surname>Computations (HPC-UA</surname>
          </string-name>
          '
          <year>2011</year>
          ), pp.
          <fpage>76</fpage>
          -
          <lpage>83</lpage>
          . Kyiv,
          <source>October 12-14</source>
          ,
          <year>2011</year>
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>